大多数WPF mvvm应用程序,我们在视图模型中使用ICommand
。但它指的是System.Windows.Input
。所以视图模型现在与System.Windows.Input
命名空间紧密结合。根据我的理解,view-model应该能够在普通的C#winform应用程序或asp.net应用程序中使用。
通常我们使用以下代码行来执行RelayCommand
。
private RelayCommand testCommand;// or private ICommand testCommand;
public ICommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
我觉得我们需要删除所有ICommand
并使用RelayCommand
代替。所以我们可以从视图模型中消除System.Windows
命名空间。所以最终的代码看起来像这样,
private RelayCommand testCommand;
public RelayCommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
有关此方法的任何建议吗?或者有没有办法从视图模型中消除System.Windows
命名空间?
答案 0 :(得分:5)
如果您愿意,可以非常简单地避免将ViewModel与ICommand耦合。可能不是一个坏主意,WPF可能有一天会成为MFC的方式。矫枉过正?也许,但这里是 a 如何:
在您看来:
<StackPanel>
<Button Command="{Binding Path=MyCommand}"> Do it! Kill me Now!</Button>
<TextBlock Text="{Binding Path=Message}"></TextBlock>
</StackPanel>
将ViewModel注入DataContext,从视图模型中承担本机命令的责任:
public class ViewModel : INotifyPropertyChanged
{
public string Message { get; set; }
public object MyCommand { get; set; }
public void OnMyCommand(object parameter)
{
Message += "I Ran something" + Environment.NewLine;
}
public bool CanMyCommand(object parameter)
{
return true;
}
// Injected Native Command handler
public ViewModel(ICommandFactory factory)
{
MyCommand = factory.CreateInstance(OnMyCommand, CanMyCommand);
}
public event PropertyChangedEventHandler PropertyChanged;
}
注意我正在使用FODY到weave in属性更改处理程序。 INotifyPropertyChanged是System.dll btw。
现在,绑定这份合同:
public interface ICommandFactory
{
object CreateInstance(Action<object> action, Func<object, bool> predicate);
}
...给你一个本机Command对象的东西;
public class NativeCommand : ICommand
{
private readonly Action<object> _action;
private readonly Func<object, bool> _predicate;
public NativeCommand(Action<object> action, Func<object, bool> predicate)
{
_action = action;
_predicate = predicate;
}
public bool CanExecute(object parameter)
{
return _predicate(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public event EventHandler CanExecuteChanged;
}
public class NativeCommandFactory : ICommandFactory
{
public object CreateInstance(Action<object> action, Func<object, bool> predicate)
{
return new NativeCommand(action, predicate);
}
}
Bind<ICommandFactory>().To<NativeCommandFactory>();
Voilà,解耦命令。
另请注意,您的注射是在初始应用程序启动时完成的。您的ViewModel与您选择的任何IoC容器分离。
答案 1 :(得分:4)
有关此方法的任何建议吗?
这仍然无法使您与System.Windows.Input
脱离,因为RelayCommand
仍然必须实施ICommand
,即使它是间接实施它。
在ViewModel中实现ICommand
是为了实用而往往需要的东西之一。理想情况下,ICommand
(或类似的接口)将在不是特定于XAML的命名空间中实现。话虽如此,它直接在Portable Class Libraries内得到支持,因此它与特定的框架(WPF,Silverlight,Phone等)无关,与XAML一样。
答案 2 :(得分:2)
嗯,从理论上讲,你是非常正确的。如果ICommand的好处完全与UI平台无关。
但是从实际的角度来看,如果你在WPF应用程序中使用MVVM,那么你很可能完全依赖于WPF的数据绑定和数据模板功能。试图将WinForms UI放在类似的东西之上可能需要大量额外的努力。
我过去曾参与过一些相当大的WPF / MVVM项目。我们认为MVVM是一种将UI的特定细节与代码分离的方式 - 不是因为我们可以切换到WinForms / ASP.NET /等等,但是我们可以改变UI的外观(即编辑) XAML)无需更改ViewModels。在这方面,MVVM工作得很好。
如果您真的担心跨多个类型的项目共享代码,那么最好尝试将公共代码放在典型的“业务层”类型库中,而不是放在视图模型中。