在不使用System.Windows.Input.ICommand的情况下在WPF中实现MVVM

我正在尝试使用MVVM(Model-View-ViewModel)模式实现WPF应用程序,并且我希望将视图部分放在与Model和ViewModel部件(DLL)分开的程序集(EXE)中。

这里的麻烦是保持Model / ViewModel程序集清除任何WPF依赖。 原因是我想重复使用不同(非WPF)UI技术的可执行文件,例如Mono下的WinForms或GTK#。

默认情况下,这是无法完成的,因为ViewModel公开了一个或多个ICommands。 但是ICommand类型是在属于WPF的System.Windows.Input命名空间中定义的!

那么,是否有一种方法可以在不使用ICommand的情况下满足WPF绑定机制?

谢谢!


您应该能够在您的wpf层和一个命令处理程序类中定义一个WPF自定义路由命令。 您所有的WPF类都可以使用适当的参数绑定到这一个命令。

处理程序类可以将命令转换为您自己定制的命令界面,您可以在ViewModel图层中定义自己并且独立于WPF。

最简单的例子是一个带有Execute方法的void委托的包装。

您所有不同的GUI图层只需要将其本地命令类型转换为一个位置的自定义命令类型即可。


WinForms没有使用MVVM风格视图模型所需的丰富的数据绑定和命令基础结构。

就像你不能在客户端应用程序中重用一个Web应用程序MVC控制器一样(至少在创建包装和适配器的情况下,最终只会让代码难以编写和调试而不向客户提供任何价值),你可以在WinForms应用程序中不重用WPF MVVM。

我没有在一个真正的项目上使用GTK#,所以我不知道它能做什么或不能做什么,但我怀疑MVVM不是GTK#的最佳方法。

尝试将应用程序的行为移入模型中,使视图模型只显示模型中的数据,并根据视图模型中没有逻辑的命令调用模型。

然后,对于WinForms,只需删除视图模型并直接从UI调用模型,或者创建另一个基于WinForms更多有限数据绑定支持的中间层。

重复GTK#或者编写MVC控制器和视图来为模型提供一个Web前端。

不要试图强迫一种技术转化为针对另一种技术进行优化的使用模式,不要从头开始编写自己的命令基础结构(我以前做过,而不是我最有效的选择),为每项技术使用最佳工具。


我需要一个这样的例子,所以我写了一个使用各种技术。

我有一些设计目标

1 - 保持简单

2 - 视图中绝对没有代码隐藏(Window类)

3 - 演示ViewModel类库中仅有System引用的依赖关系。

4 - 将业务逻辑保留在ViewModel中,并直接路由到合适的方法,而无需编写大量“存根”方法。

这是代码...

App.xaml(没有StartupUri是唯一值得注意的事情)

<Application 
    x:Class="WpfApplicationCleanSeparation.App" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>

App.xaml.cs(加载主视图)

using System.Windows;
using WpfApplicationCleanSeparation.ViewModels;

namespace WpfApplicationCleanSeparation
{
    public partial class App
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            var view = new MainView();
            var viewModel = new MainViewModel();

            view.InitializeComponent();
            view.DataContext = viewModel;
            CommandRouter.WireMainView(view, viewModel);
            view.Show();
        }
    }
}

CommandRouter.cs(魔术)

using System.Windows.Input;
using WpfApplicationCleanSeparation.ViewModels;

namespace WpfApplicationCleanSeparation
{
    public static class CommandRouter
    {
        static CommandRouter()
        {
            IncrementCounter = new RoutedCommand();
            DecrementCounter = new RoutedCommand();
        }

        public static RoutedCommand IncrementCounter { get; private set; }
        public static RoutedCommand DecrementCounter { get; private set; }

        public static void WireMainView(MainView view, MainViewModel viewModel)
        {
            if (view == null || viewModel == null) return;

            view.CommandBindings.Add(
                new CommandBinding(
                    IncrementCounter,
                    (λ1, λ2) => viewModel.IncrementCounter(),
                    (λ1, λ2) =>
                        {
                            λ2.CanExecute = true;
                            λ2.Handled = true;
                        }));
            view.CommandBindings.Add(
                new CommandBinding(
                    DecrementCounter,
                    (λ1, λ2) => viewModel.DecrementCounter(),
                    (λ1, λ2) =>
                        {
                            λ2.CanExecute = true;
                            λ2.Handled = true;
                        }));
        }
    }
}

MainView.xaml(没有代码隐藏,从字面上删除!)

<Window 
    x:Class="WpfApplicationCleanSeparation.MainView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:WpfApplicationCleanSeparation="clr-namespace:WpfApplicationCleanSeparation" 
    Title="MainWindow" 
    Height="100" 
    Width="100">
    <StackPanel>
        <TextBlock Text="{Binding Counter}"></TextBlock>
        <Button Content="Decrement" Command="WpfApplicationCleanSeparation:CommandRouter.DecrementCounter"></Button>
        <Button Content="Increment" Command="WpfApplicationCleanSeparation:CommandRouter.IncrementCounter"></Button>
    </StackPanel>
</Window>

MainViewModel.cs(包括实际的模型,因为这个例子是如此简化,请原谅MVVM模式的出轨。

using System.ComponentModel;

namespace WpfApplicationCleanSeparation.ViewModels
{
    public class CounterModel
    {
        public int Data { get; private set; }

        public void IncrementCounter()
        {
            Data++;
        }

        public void DecrementCounter()
        {
            Data--;
        }
    }

    public class MainViewModel : INotifyPropertyChanged
    {
        private CounterModel Model { get; set; }
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        public MainViewModel()
        {
            Model = new CounterModel();
        }

        public int Counter
        {
            get { return Model.Data; }
        }

        public void IncrementCounter()
        {
            Model.IncrementCounter();

            PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
        }

        public void DecrementCounter()
        {
            Model.DecrementCounter();

            PropertyChanged(this, new PropertyChangedEventArgs("Counter"));
        }
    }
}

只是一个快速和肮脏的,我希望它对某人有用。 我通过各种Google看到了几种不同的方法,但没有什么比使用我想要的最少量的代码实现简单和容易。 如果有办法进一步简化请让我知道,谢谢。

快乐编码:)

编辑:为了简化我自己的代码,您可能会发现这对于将添加到单行中很有用。

    private static void Wire(this UIElement element, RoutedCommand command, Action action)
    {
        element.CommandBindings.Add(new CommandBinding(command, (sender, e) => action(), (sender, e) => { e.CanExecute = true; }));
    }
链接地址: http://www.djcxy.com/p/56115.html

上一篇: Implementing MVVM in WPF without using System.Windows.Input.ICommand

下一篇: WPF MVVM: How to close a window