在视图模型中使用ICommand是否可以

时间:2013-03-12 19:15:17

标签: c# wpf mvvm

大多数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命名空间?

3 个答案:

答案 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;
}

注意我正在使用FODYweave 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à,解耦命令。

running

另请注意,您的注射是在初始应用程序启动时完成的。您的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工作得很好。

如果您真的担心跨多个类型的项目共享代码,那么最好尝试将公共代码放在典型的“业务层”类型库中,而不是放在视图模型中。