WPF ICommand MVVM实现
所以在这个特定的MVVM实现中,我需要几个命令。我真的厌倦了逐个实现ICommand类,所以我想出了一个解决方案,但我不知道它有多好,所以任何WPF专家的输入将不胜感激。如果你能提供更好的解决方案,甚至更好!WPF ICommand MVVM实现
我所做的是一个ICommand类和两个委托,它们将一个对象作为参数,一个委托是void(用于OnExecute),另一个是用于OnCanExecute的bool。所以在我的ICommand(由ViewModel类调用)的构造函数中,我发送了两个方法,并在每个ICommand方法上调用委托的方法。
它的工作非常好,但我不确定这是否是一种不好的方法,或者如果有更好的方法。以下是完整的代码,任何输入将不胜感激,甚至是消极的,但请具有建设性。
谢谢!
视图模型:
public class TestViewModel : DependencyObject
{
public ICommand Command1 { get; set; }
public ICommand Command2 { get; set; }
public ICommand Command3 { get; set; }
public TestViewModel()
{
this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1);
this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2);
this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3);
}
public bool CanExecuteCommand1(object parameter)
{
return true;
}
public void ExecuteCommand1(object parameter)
{
MessageBox.Show("Executing command 1");
}
public bool CanExecuteCommand2(object parameter)
{
return true;
}
public void ExecuteCommand2(object parameter)
{
MessageBox.Show("Executing command 2");
}
public bool CanExecuteCommand3(object parameter)
{
return true;
}
public void ExecuteCommand3(object parameter)
{
MessageBox.Show("Executing command 3");
}
}
的ICommand:
public class TestCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
#endregion
}
这几乎是相同的卡尔Shifflet如何表现出RelayCommand
,其中Execute
火灾预定Action<T>
。一个顶尖的解决方案,如果你问我。
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
this._canExecute = canExecute;
this._execute = execute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
这可能被用来作为...
public class MyViewModel
{
private ICommand _doSomething;
public ICommand DoSomethingCommand
{
get
{
if (_doSomething == null)
{
_doSomething = new RelayCommand(
p => this.CanDoSomething,
p => this.DoSomeImportantMethod());
}
return _doSomething;
}
}
}
我刚刚创建了一个小example展示如何实现约定优于配置风格的命令。但是它需要Reflection.Emit()可用。支持代码看起来有点奇怪,但一旦写入,可以多次使用。
预告:
public class SampleViewModel: BaseViewModelStub
{
public string Name { get; set; }
[UiCommand]
public void HelloWorld()
{
MessageBox.Show("Hello World!");
}
[UiCommand]
public void Print()
{
MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel");
}
public bool CanPrint()
{
return !String.IsNullOrEmpty(Name);
}
}
}
UPDATE:现在似乎存在着一些库,例如http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model能够解决的ICommand的样板代码的问题。
我已经写了这个关于ICommand接口的article。
这个想法 - 创建一个需要两个代表的通用命令:一个在调用ICommand.Execute (object param)
时调用,第二个检查是否可以执行命令(ICommand.CanExecute (object param))
的状态。
需要方法切换事件CanExecuteChanged
。它从用户界面元素中调用,用于切换状态CanExecute()
命令。
public class ModelCommand : ICommand
{
#region Constructors
public ModelCommand(Action<object> execute)
: this(execute, null) { }
public ModelCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute != null ? _canExecute(parameter) : true;
}
public void Execute(object parameter)
{
if (_execute != null)
_execute(parameter);
}
public void OnCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#endregion
private readonly Action<object> _execute = null;
private readonly Predicate<object> _canExecute = null;
}
@Carlo我很喜欢你的这个实现,但我想分享我的版本,以及如何在我的视图模型使用
首先实现ICommand的
public class Command : ICommand
{
public delegate void ICommandOnExecute();
public delegate bool ICommandOnCanExecute();
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke() ?? true;
}
public void Execute(object parameter)
{
_execute?.Invoke();
}
#endregion
}
通知我已删除来自的参数ICommandOnExecute和ICommandOnCanExecute并向构造函数添加null
然后在视图模型
public Command CommandToRun_WithCheck
{
get
{
return new Command(() =>
{
// Code to run
},() =>
{
// Code to check to see if we can run
// Return true or false
});
}
}
public Command CommandToRun_NoCheck
{
get
{
return new Command(() =>
{
// Code to run
});
}
}
用我只是觉得这种方式更清洁,因为我不需要给变量赋值,然后实例,这一切在一气呵成完成。
感谢分享!看到解决这个问题的其他方法确实很有趣。自从我阅读RelayCommand后,我决定采用这种模式。我没有在年份做过WPF,但在我公司的这个趋势转向网络之前,我确实使用了RelayCommand几年。 – Carlo 2017-11-08 22:18:21
查看Karl Shifflet的RelayCommand实现:http://www.codeproject.com/KB/WPF/ExploringWPFMVVM.aspx – 2009-09-24 00:26:03