wpf - 胖模型、瘦 View 模型和哑 View ,最好的 MVVM 方法?

标签 wpf mvvm architecture

通过 this question 上的慷慨帮助,我把下面的 MVVM 结构放在一起,它在 XAML(当前日期/时间)中实时显示模型的变化,非常好。

A cool advantage of this set up is that when you look at your view in design mode of Visual Studio or Blend, you see the time ticking by, which means that at design time you have access to live data from your model.



在让它工作的过程中,我很惊讶地看到 大部分内容从我的 ViewModel 转移到我的模型 ,包括 INotifyPropertyChange 的实现。另一个变化是我不再绑定(bind)到 ViewModel 上的属性,而是绑定(bind)到方法 .

所以目前这是我最喜欢的 MVVM 风格:
  • View 很愚蠢:
  • 为您的模型中需要的每个对象提供一个 ObjectDataProvider
  • 每个 ObjectDataProvider 都映射到 ViewModel 上的一个方法(不是属性)
  • XAML 元素中没有 x:Name 属性
  • ViewModel 很瘦:
  • 您的 ViewModel 中唯一的东西是您的 View 绑定(bind)到的方法
  • 模型很胖:
  • 该模型在其每个属性上实现 INotifyPropertyChanged。
  • 对于您的 ViewModel 上的每个方法(例如 GetCurrentCustomer),您的模型中都有一个相应的单例方法(例如 GetCurrentCustomer)。
  • 该模型负责任何实时线程功能,如本例

  • 问题:
  • 那些在实际场景中实现过 MVVM 的人,这是否也是您确定的基本结构,如果不是,您的结构有何不同?
  • 您将如何扩展它以包括路由命令和路由事件?

  • 如果您只是将 XAML 和代码复制到新的 WPF 项目中,则以下代码将起作用。

    XAML:
    <Window x:Class="TestBinding99382.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestBinding99382"
        Title="Window1" Height="300" Width="300">
    
        <Window.Resources>
            <ObjectDataProvider 
                 x:Key="DataSourceCustomer" 
                 ObjectType="{x:Type local:ShowCustomerViewModel}" 
                            MethodName="GetCurrentCustomer"/>
        </Window.Resources>
    
        <DockPanel DataContext="{StaticResource DataSourceCustomer}">
            <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
                <TextBlock Text="{Binding Path=FirstName}"/>
                <TextBlock Text=" "/>
                <TextBlock Text="{Binding Path=LastName}"/>
            </StackPanel>
            <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
                <TextBlock Text="{Binding Path=TimeOfMostRecentActivity}"/>
            </StackPanel>
    
        </DockPanel>
    </Window>
    

    隐藏代码:
    using System.Windows;
    using System.ComponentModel;
    using System;
    using System.Threading;
    
    namespace TestBinding99382
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        }
    
        //view model
        public class ShowCustomerViewModel
        {
            public Customer GetCurrentCustomer() {
                return Customer.GetCurrentCustomer();
            }
        }
    
        //model
        public class Customer : INotifyPropertyChanged
        {
            private string _firstName;
            private string _lastName;
            private DateTime _timeOfMostRecentActivity;
            private static Customer _currentCustomer;
            private Timer _timer;
    
            public string FirstName
            {
                get
                {
                    return _firstName;
                }
                set
                {
                    _firstName = value;
                    this.RaisePropertyChanged("FirstName");
                }
            }
    
            public string LastName
            {
                get
                {
                    return _lastName;
                }
                set
                {
                    _lastName = value;
                    this.RaisePropertyChanged("LastName");
                }
            }
    
            public DateTime TimeOfMostRecentActivity
            {
                get
                {
                    return _timeOfMostRecentActivity;
                }
                set
                {
                    _timeOfMostRecentActivity = value;
                    this.RaisePropertyChanged("TimeOfMostRecentActivity");
                }
            }
    
            public Customer()
            {
                _timer = new Timer(UpdateDateTime, null, 0, 1000);
            }
    
            private void UpdateDateTime(object state)
            {
                TimeOfMostRecentActivity = DateTime.Now;
            }
    
            public static Customer GetCurrentCustomer()
            {
                if (_currentCustomer == null)
                {
                    _currentCustomer = new Customer 
                         {  FirstName = "Jim"
                            , LastName = "Smith"
                            , TimeOfMostRecentActivity = DateTime.Now 
                         };
                }
                return _currentCustomer;
            }
    
            //INotifyPropertyChanged implementation
            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged(string property)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(property));
                }
            }
        }
    }
    

    最佳答案

    这是我的意见,因为它的值(value):

    我不太同意您建议的方法(愚蠢的观点除外)。在现实生活中,您通常必须使用现有模型:它可能是您没有时间(或将)更改的遗留代码,甚至是您没有代码的库。在我看来,模型应该完全不知道它的显示方式,并且应该可以在非 WPF 应用程序中轻松使用。所以它不必实现任何特定的接口(interface),如 INotifyPropertyChangedINotifyCollectionChanged使其在 MVVM 中可用。我认为所有与 UI 相关的逻辑都应该驻留在 ViewModel 中。

    关于RoutedEventsRoutedCommands ,它们并不真正适合与 MVVM 模式一起使用。我通常尽量少用RoutedEvents尽可能,没有RoutedCommands一点也不。相反,我的 ViewModel 公开了 RelayCommand我在 XAML 中绑定(bind)到 UI 的属性(有关 RelayCommand 的详细信息,请参阅 Josh Smith 的 this article)。当我真的需要为某些控件处理事件时,我使用附加的行为将事件映射到 ViewModel 命令(查看 Marlon Grech's implementation)

    所以,总而言之:

  • 哑观点
  • 大而智能的ViewModel
  • 您想要或必须使用的任何型号

  • 当然这只是我的方法,它可能不是最好的,但我觉得很舒服;)

    关于wpf - 胖模型、瘦 View 模型和哑 View ,最好的 MVVM 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/852441/

    相关文章:

    c# - 在 Windows Phone 8.1 运行时覆盖 Prism 中的后退按钮

    java - Spring JPA 注释和 ORM.xml 替代方案?

    java - 在非分布式应用程序中使用消息队列是否有意义

    wpf - CellTemplateSelector 不会自动选择模板

    c# - 在 WindowsFormsHost 中插入表单时出现 ArgumentException

    c# - 如何让MVVM(WPF)中的输入控件相互通信?

    c# - 在 Web 应用程序中访问业务逻辑服务是否有最佳实践?

    c# - 删除按键上的 ListView SelectedItem

    c# - 将 CheckBox 从 DataTemplate 绑定(bind)到 ListBox 中的 TemplatedParent

    mvvm - 如何在MVVM中找到集中控制?