将命令抽象到视图模型中是 XAML/MVVM 项目的宝贵实践。我明白了。而且,我在 WinRT 中看到了 ICommand;但是,我们如何实施呢?我还没有找到真正有效的样本。有人知道吗?
5 回答
我一直最喜欢的是 Microsoft 模式和实践团队提供的 DelegateCommand。它允许您创建一个键入的命令:
MyCommand = new DelegateCommand<MyEntity>(OnExecute);
...
private void OnExecute(MyEntity entity)
{...}
它还提供了一种引发 CanExecuteChanged 事件的方法(禁用/启用命令)
MyCommand.RaiseCanExecuteChanged();
这是代码:
public class DelegateCommand<T> : ICommand
{
private readonly Func<T, bool> _canExecuteMethod;
private readonly Action<T> _executeMethod;
#region Constructors
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion Constructors
#region ICommand Members
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute(object parameter)
{
try
{
return CanExecute((T)parameter);
}
catch { return false; }
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion ICommand Members
#region Public Methods
public bool CanExecute(T parameter)
{
return ((_canExecuteMethod == null) || _canExecuteMethod(parameter));
}
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged(EventArgs.Empty);
}
#endregion Public Methods
#region Protected Methods
protected virtual void OnCanExecuteChanged(EventArgs e)
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion Protected Methods
}
查看RelayCommand类(仅 METRO 代码)。该NotifyPropertyChanged
课程可以在这里找到。该类NotifyPropertyChanged
仅用于允许绑定CanExecute
并使用RaiseCanExecuteChanged
.
原始的中继命令类可以在这里找到
不幸的是,似乎没有为您实现它的本机类。如果您想自己实现该接口并不太复杂,并且流行的MVVM Lite工具包包含其自己的RelayCommand版本。您可以通过右键单击 References 并选择“Manage NuGet Packages”将 MVVM Lite 添加到您的项目中。如果您没有此选项,请在Tools -> Extensions and Updates下启用 Nuget 。
我一直在寻找 XAML-MVVM 命令的最小端到端实现,但还没有找到。
因此,按照#Rico 的回答,我最终得到了以下作为最小 RelayCommand 的工作。我在一个大型项目中使用了类似的最小版本。
public class RelayCommand : System.Windows.Input.ICommand {
readonly Action<object> execute;
public RelayCommand(Action<object> execute) {
this.execute = execute;
}
public bool CanExecute(object parameter) {
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
this.execute(parameter);
}
}
CanExecute
较大的 RelayCommand 类似乎提供了对and的更多控制CanExecuteChanged
,但您不需要它来开始 - 而且您可能根本不需要它。
要在视图模型中使用它:
class ViewModel : INotifyPropertyChanged {
<< ... snip VM properties and notifications ...>>
public RelayCommand DoSomethingCommand {
get {
return new RelayCommand(param => {
this.DoSomething(param as AType);
Debug.WriteLine("Command Executed");
});
}
}
}
(我们不需要命令的 INotifyPropertyChanged,但任何视图模型通常都会实现它。)
最后,XAML...
<Grid>
<!-- Set the data context here, for illustration. -->
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<!-- A sample control bind to a property -->
<TextBlock
Text="{Binding AProp}"/>
<!-- Bind a command -->
<Button Command="{Binding DoSomethingCommand}" CommandParameter="foo">Change!</Button>
</Grid>
我在https://code.msdn.microsoft.com/windowsapps/Working-with-ICommand-690ba1d4找到了这个非常好的示例
<Page.Resources>
<local:MyCommandsCollection x:Key="MyCommands" />
</Page.Resources>
<Button Width="280"
Height="59"
Margin="513,280,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding MyFirstCommand}"
CommandParameter="{Binding Text,
ElementName=myTextBox}"
Content="Execute Command" />
public class MyCommandsCollection
{
public MyCommand MyFirstCommand
{
get { return new MyCommand(); }
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
MessageDialog message = new MessageDialog(
"The command is executing, the value of the TextBox is " + parameter as String);
await message.ShowAsync();
}
}
我用 x:Bind 试过了,效果很好。我所需要的只是在我的 ViewModel 中公开一个属性,该属性返回“MyCommand”类的一个新实例,这一切都很好。
由于我在我的 XAML 中设置 DataContext,我不需要弄乱任何“MyCommandCollection”的东西。耶编译绑定。