12

ICommand我们在视图模型中使用的大多数 WPF mvvm 应用程序。但它指的是System.Windows.Input. 所以视图模型现在与System.Windows.Input命名空间紧密结合。根据我的理解,视图模型应该能够在普通的 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并使用RelayCommandSystem.Windows所以我们可以从视图模型中消除命名空间。所以最终的代码看起来像这样,

private RelayCommand testCommand;

public RelayCommand TestCommand
{
    get
    {
        return testCommand ?? 
            (testCommand = new RelayCommand(param => Test()));
    }
}

public void Test()
{

}

对这种方法有什么建议吗?或者有什么方法可以System.Windows从视图模型中消除命名空间?

4

3 回答 3

6

对这种方法有什么建议吗?

这仍然不能将您与System.Windows.Inputas RelayCommandstill must implement分离ICommand,即使它是间接实现它。

ICommand在 ViewModel 中实现是为了实用而需要做的事情之一。理想情况下,ICommand(或类似的接口)将在非 XAML 特定的命名空间中实现。话虽如此,它直接在Portable Class Libraries中得到支持,因此它不像一般的 XAML 那样依赖于特定框架(WPF、Silverlight、Phone 等)。

于 2013-03-12T19:20:29.370 回答
6

如果您愿意,可以很简单地避免将您的 ViewModel 耦合到 ICommand。可能不是一个坏主意,WPF 可能有一天会走上 MFC 的道路。矫枉过正?也许,但这是一个方法:

在您看来:

<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编织属性更改处理程序。INotifyPropertyChanged 是 System.dll 顺便说一句。

现在,绑定这个合约:

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

瞧,解耦的命令。

跑步

另请注意,您的注入是在初始应用程序启动时完成的。您的 ViewModel 与您选择的任何 IoC 容器分离。

于 2013-03-12T20:46:20.663 回答
2

好吧,从理论上讲,你是对的。如果 ICommand 的 nice 完全独立于 UI 平台就好了。

但从实际的角度来看,如果您在 WPF 应用程序中使用 MVVM,那么您很有可能仍然相当依赖 WPF 的数据绑定和数据模板功能。尝试将 WinForms UI 放在类似的东西之上可能需要大量的额外工作。

我过去曾参与过一些相当大的 WPF/MVVM 项目。我们认为 MVVM 是一种将 UI 的特定细节从代码中分离出来的方法——不是为了让我们可以切换到 WinForms/ASP.NET/whatever,而是为了改变我们 UI 的外观和感觉(即编辑XAML),而无需更改 ViewModel。在这方面,MVVM 工作得很好。

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

于 2013-03-12T20:00:11.663 回答