Implementing MVVM in WPF without using System.Windows.Input.ICommand

后端 未结 8 1594
醉梦人生
醉梦人生 2021-02-06 04:43

I\'m trying to implement a WPF application using MVVM (Model-View-ViewModel) pattern and I\'d like to have the View part in a separate assembly (an EXE) from the Model and ViewM

8条回答
  •  滥情空心
    2021-02-06 04:57

    I needed an example of this so I wrote one using various techniques.

    I had a few design goals in mind

    1 - keep it simple

    2 - absolutely no code-behind in the view (Window class)

    3 - demonstrate a dependency of only the System reference in the ViewModel class library.

    4 - keep the business logic in the ViewModel and route directly to the appropriate methods without writing a bunch of "stub" methods.

    Here's the code...

    App.xaml (no StartupUri is the only thing worth noting)

    
    
    

    App.xaml.cs (load up the main view)

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

    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 (there is NO code-behind, literally deleted!)

    
        
            
            
            
        
    
    

    MainViewModel.cs (includes the actual Model as well since this example is so simplified, please excuse the derailing of the MVVM pattern.

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

    Proof

    Just a quick and dirty and I hope it's useful to someone. I saw a few different approaches through various Google's but nothing was quite as simple and easy to implement with the least amount of code possible that I wanted. If there's a way to simplify even further please let me know, thanks.

    Happy Coding :)

    EDIT: To simplify my own code, you might find this useful for making the Adds into one-liners.

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

提交回复
热议问题