如果您正在使用MVVM并使用命令,您通常会在ViewModel上看到由私有RelayCommand或DelegateCommand字段支持的ICommand属性,例如MSDN上原始MVVM文章中的此示例:
RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(param => this.Save(),
param => this.CanSave );
}
return _saveCommand;
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这很麻烦,并且使得设置新命令相当繁琐(我与一些资深的WinForms开发人员合作,他们对所有这些打字都不屑一顾).所以我想简化它并挖掘一下.我在get {}块的第一行设置了一个断点,看到它只是在我的应用程序第一次加载时才被击中 - 我可以随后发出尽可能多的命令,这个断点永远不会被击中 - 所以我想要简化这个以从我的ViewModel中删除一些混乱,并注意到以下代码的工作方式相同:
public ICommand SaveCommand
{
get
{
return new RelayCommand(param => this.Save(), param => this.CanSave );
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我不太了解C#或垃圾收集器,知道这是否会导致问题,例如在某些情况下产生过多的垃圾.这会造成任何问题吗?
我是单元测试MVVM的新手,并在我的项目中使用PRISM.我正在我们当前的项目上实现单元测试,没有运气在线查找资源,这将告诉我如何调用异步方法的totest DelegateCommand.这是我的帖子的后续问题 - 如何使用异步方法对ViewModel进行单元测试.关于如何在MVVM中单元测试异步方法,并回答公共方法可以使用异步TestMethod进行测试.仅当我要测试的方法是公共方法时,此方案才有效.
问题是我想测试我的DelegateCommand,因为这是我想在其他类上公开的唯一公共细节,其他一切都是私有的.我可以公开我的私人方法,但我永远不会这样做,因为它是一个糟糕的设计.我不知道如何解决这个问题 - 是否需要对DelegateCommand进行测试,还是有其他一些工作要做?我很想知道其他人如何去做,并以某种方式引导我走向正确的道路.
这是我的代码
async void GetTasksAsync()
{
this.SimpleTasks.Clear();
Func<IList<ISimpleTask>> taskAction = () =>
{
var result = this.dataService.GetTasks();
if (token.IsCancellationRequested)
return null;
return result;
};
IsBusyTreeView = true;
Task<IList<ISimpleTask>> getTasksTask = Task<IList<ISimpleTask>>.Factory.StartNew(taskAction, token);
var l = await getTasksTask; // waits for getTasksTask
if (l != null)
{
foreach (ISimpleTask t in l)
{
this.SimpleTasks.Add(t); // adds to ViewModel.SimpleTask
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里也是我的VM中的命令,它调用上面的异步方法
this.GetTasksCommand = new DelegateCommand(this.GetTasks);
void GetTasks()
{
GetTasksAsync();
}
Run Code Online (Sandbox Code Playgroud)
现在我的测试方法就像
[TestMethod]
public …Run Code Online (Sandbox Code Playgroud) 在我的视图中,我有一个按钮.
当用户单击此按钮时,我希望ViewModel在数据库中保存TextBlock的上下文.
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock Text="{Binding FirstName}"/>
<TextBox Text="Save this text to the database."/>
<Button Content="Save" Command="{Binding SaveCommand}"/>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)
但是,在我的ViewModel中的DelegateCommand中,"Save()"方法不传递任何参数,那么如何从视图中获取数据呢?
#region DelegateCommand: Save
private DelegateCommand saveCommand;
public ICommand SaveCommand
{
get
{
if (saveCommand == null)
{
saveCommand = new DelegateCommand(Save, CanSave);
}
return saveCommand;
}
}
private void Save()
{
TextBox textBox = ......how do I get the value of the view's textbox from here?....
}
private bool CanSave()
{
return true;
}
#endregion
Run Code Online (Sandbox Code Playgroud) 我试图从"使用C#进行.NET域驱动设计"中找到一个示例,其中包含一个代码示例,您可以在其中看到声明了某些类型的属性DelegateCommand.
现在,我已经尝试了谷歌搜索,但我无法在MSDN上的任何地方找到它的参考(实际上,我发现了这篇文章,但不是DelegateCommand文章本身).
这DelegateCommand部分内容是WPF或者它只是书中创造的内容吗?
如果它是WPF(或任何其他MS产品)的一部分,那么汇编/命名空间是什么?
是否可以触发命令以通知窗口已加载.另外,我没有使用任何MVVM框架(在某种意义上的框架,Caliburn,Onxy,MVVM Toolkit等)
我刚刚使用MVVM模式完成了用WPF和c#编写的桌面应用程序.在这个应用程序中,我使用Delegate Command实现来包装我的ModelView中公开的ICommands属性.问题是这些DelegateCommands阻止我的ModelView和View在关闭视图后被垃圾收集.因此,在我终止整个应用程序之前,它一直保持冷静.我分析了应用程序,我发现它是关于将模型视图保存在内存中的委托命令.我怎么能避免这种情况,这是mvvm模式的本质,还是我的模式的植入?谢谢.
编辑:这是我实现MVVM模式的小但完整的部分
第一:CommandDelegte类
class DelegateCommand:ICommand
{
private Action<object> execute;
private Predicate<object> canExcute;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExcute = canExecute;
}
public bool CanExecute(object parameter)
{
if (this.canExcute != null)
{
return canExcute(parameter);
}
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Run Code Online (Sandbox Code Playgroud)
第二:ModelView类
public class …Run Code Online (Sandbox Code Playgroud) 我刚学习WPF中的MVVM,我对WPF和MVVM都是全新的(我理解它是如何工作的,但从未使用它......)
我在网上找到的每一篇教程/文章都使用了RelayCommand或DelegateCommand.
在我的观察中,这些模式迫使VM违反SRP原则,因为它将把命令逻辑保存在其中.
为什么不使用ICommand接口的自定义实现?像这样:
想象一下,您正在显示一个人并将其保存到数据库中:
我的Xaml就是这样的:
<StackPanel>
<TextBlock Width="248" Height="24" Text="The name is:: " />
<TextBlock Width="248" Height="24" Text="{Binding Name}">
</TextBlock>
<TextBox HorizontalAlignment="Left" Name="textBox1" Width="120" Height="23"
VerticalAlignment="Top" Text="{Binding Name}"
/>
<Button Name="Salvar" VerticalAlignment="Bottom"
Command="{Binding SavePerson}"
CommandParameter="{Binding}">Save</Button>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)
这是我的VM:
public class PersonVM: INotifyPropertyChanged
{
private string nameValue;
public string Name
{
get{
return nameValue;
}
set
{
if (value != this.nameValue)
{
this.nameValue= value;
NotifyPropertyChanged("Name");
}
}
}
public ICommand SavePerson{ get { return new SavePersonCommand(); } }
#region INotifyPropertyChanged Members …Run Code Online (Sandbox Code Playgroud) 当我按下按钮时,我正试图获得两个Texbox的值(我正在模拟一个登录窗口).按钮中指定的命令正确触发,但我不知道如何获取文本框的值来执行"登录".
这是我的ViewModel:
class LoginViewModel : BaseViewModel
{
public LoginViewModel()
{
}
private DelegateCommand loginCommand;
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
loginCommand = new DelegateCommand(new Action(LoginExecuted),
new Func<bool>(LoginCanExecute));
return loginCommand;
}
}
public bool LoginCanExecute()
{
//Basic strings validation...
return true;
}
public void LoginExecuted()
{
//Do the validation with the Database.
System.Windows.MessageBox.Show("OK");
}
}
Run Code Online (Sandbox Code Playgroud)
这是观点:
<Grid DataContext="{StaticResource LoginViewModel}">
<TextBox x:Name="LoginTxtBox" HorizontalAlignment="Left" Height="23" Margin="34,62,0,0" Width="154" />
<PasswordBox x:Name="PasswordTxtBox" HorizontalAlignment="Left" Height="23" Margin="34,104,0,0" Width="154"/>
<Button x:Name="btnAccept"
HorizontalAlignment="Left"
Margin="34,153,0,0"
Width="108" …Run Code Online (Sandbox Code Playgroud) 我有一个treeView项的模板:
<HierarchicalDataTemplate x:Key="RatesTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ID}"/>
<Button CommandParameter="{Binding Path=ID}"
Command="{Binding ElementName=CalcEditView, Path=DataContext.Add}">Add</Button>
</StackPanel>
</HierarchicalDataTemplate>
Run Code Online (Sandbox Code Playgroud)
作为DataContext,我有一个ID为非空字段的linq实体.
问题是:如果我使用CanExecutedMethod的DelegateCommand'Add':
AddRate = new DelegateCommand<int?>(AddExecute,AddCanExecute);
Run Code Online (Sandbox Code Playgroud)
它只调用一次,参数为null(而textBlock显示正确的ID值).在调用ID属性之前调用CanExecute(使用调试器检查).似乎在绑定到实际参数之前,wpf正在调用canExecute并忘记它.绑定完成并加载正确的值后,它不会再次调用CanExecute.
作为一种解决方法,我可以使用只有执行委托的命令:
Add = new DelegateCommand<int?>(AddExecute);
Run Code Online (Sandbox Code Playgroud)
使用正确的ID值调用AddExecute并且工作正常.但我仍然想使用CanExecute功能.有任何想法吗?
Model-View-ViewModel(MVVM)方法似乎是WPF UI开发中的领跑者模式.我读过的几乎每篇文章都暗示这是最好的做法.在许多文章中使用RelayCommand或DelegateCommand也很突出,似乎几乎不可能在不使用它们(或任何其他变体)的情况下实现MVVM.那么为什么它们不是.NET 4的一部分呢?
我知道RelayCommand实现起来非常简单,许多第三方工具包已经拥有它但我只是想知道微软为什么会遗漏一些所谓"最佳实践"实现的基本和基本的东西?
delegatecommand ×10
wpf ×9
mvvm ×8
c# ×5
relaycommand ×3
.net ×2
icommand ×1
prism ×1
unit-testing ×1
vb.net ×1