ICommand MVVM 实现

所以在我正在做的这个 MVVM 实现中,我需要几个命令。我真的厌倦了一个接一个地实现 ICommand 类,所以我想出了一个解决方案,但是我不知道它有多好,所以这里的任何 WPF 专家的输入都会非常受欢迎。如果你能提供一个更好的解决方案,甚至更好。

我所做的是一个 ICommand 类和两个将对象作为参数的委托,一个委托为 void (用于 OnExecute) ,另一个委托为 bool (用于 OnCanExecute)。因此,在我的 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");
}
}

命令:

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
}
150520 次浏览

这几乎与 Karl Shifflet 演示了如何 RelayCommand相同,其中 Execute发射一个预先确定的 Action<T>。要我说,这是最好的解决方案。

public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;


public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_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;
}
}
}

延伸阅读:
Josh Smith (RelayCommand的介绍者) : 模式-带有 MVVM 设计模式的 WPF 应用程序

我刚刚创建了一个小小的 例子,展示了如何以约定优于配置风格实现命令。但是它需要反射。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);
}
}

}

更新 : 现在似乎有一些类似于 http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model的库可以解决 ICommand 样板代码的问题。

我已经写了关于 ICommand 接口的 文章

创建一个通用命令的想法需要两个委托: 一个在调用 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 我真的很喜欢你的实现,但是我想分享我的版本以及如何在我的 ViewModel 中使用它

首先执行 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
}

注意,我已经从 执行ICommandOnCanExecute中删除了参数,并在构造函数中添加了一个 null

然后在 ViewModel 中使用

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
});
}
}

我只是觉得这样更干净,因为我不需要分配变量,然后实例化,这一切都完成了。