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

后端 未结 8 1602
醉梦人生
醉梦人生 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)

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

    <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 (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; }));
        }
    
    0 讨论(0)
  • 2021-02-06 05:00

    Off course this is possible. You can create just another level of abstraction. Add you own IMyCommand interface similar or same as ICommand and use that.

    Take a look at my current MVVM solution that solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.

    See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4

    0 讨论(0)
  • 2021-02-06 05:07

    I think you are separating your Project at wrong point. I think you should share your model and business logic classes only.

    VM is an adaptation of model to suit WPF Views. I would keep VM simple and do just that.

    I can't imagine forcing MVVM upon Winforms. OTOH having just model & bussiness logic, you can inject those directly into a Form if needed.

    0 讨论(0)
  • 2021-02-06 05:11

    You should be able to define a single WPF custom routed command in your wpf layer and a single command handler class. All your WPF classes can bind to this one command with appropriate parameters.

    The handler class can then translate the command to your own custom command interface that you define yourself in your ViewModel layer and is independent of WPF.

    The simplest example would be a wrapper to a void delegate with an Execute method.

    All you different GUI layers simply need to translate from their native command types to your custom command types in one location.

    0 讨论(0)
  • 2021-02-06 05:13

    " you can't reuse a WPF MVVM in a WinForms application"

    For this please see url http://waf.codeplex.com/ , i have used MVVM in Win Form, now whenver i would like to upgrade application's presentation from Win Form to WPF, it will be changed with no change in application logic,

    But i have one issue with reusing ViewModel in Asp.net MVC, so i can make same Desktop win application in Web without or less change in Application logic..

    Thanks...

    0 讨论(0)
  • 2021-02-06 05:15

    WinForms doesn't have the rich data binding and commands infrastructure needed to use a MVVM style view model.

    Just like you can't reuse a web application MVC controllers in a client application (at least not without creating mountains of wrappers and adapters that in the end just make it harder to write and debug code without providing any value to the customer) you can't reuse a WPF MVVM in a WinForms application.

    I haven't used GTK# on a real project so I have no idea what it can or can't do but I suspect MVVM isn't the optimal approach for GTK# anyway.

    Try to move as much of the behavior of the application into the model, have a view model that only exposes data from the model and calls into the model based on commands with no logic in the view model.

    Then for WinForms just remove the view model and call the model from the UI directly, or create another intermediate layer that is based on WinForms more limited data binding support.

    Repeat for GTK# or write MVC controllers and views to give the model a web front-end.

    Don't try to force one technology into a usage pattern that is optimized for another, don't write your own commands infrastructure from scratch (I've done it before, not my most productive choice), use the best tools for each technology.

    0 讨论(0)
提交回复
热议问题