胖模型、瘦视图模型和哑视图,最好的 MVVM 方法? [英] Fat Models, skinny ViewModels and dumb Views, the best MVVM approach?

查看:37
本文介绍了胖模型、瘦视图模型和哑视图,最好的 MVVM 方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过这个问题的慷慨帮助,我整理了以下MVVM结构,它实时显示模型的变化XAML(当前日期/时间),非常好.

Through generous help on this question, I put together the following MVVM structure which displays the changes of a model in real time in XAML (current date/time), very nice.

这种设置的一个很酷的优点是当你查看你的视图时Visual Studio 的设计模式或Blend,你看到时间在流逝,这意味着在设计时你可以访问来自您的实时数据模型.

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 的实现.另一个变化是我不再绑定到 ViewModel 上的属性,而是绑定到方法.

In the process of getting this to work, I was surprised to see most of the bulk move from my ViewModel into my Model, including implementation of INotifyPropertyChange. Another change is that I no longer bind to properties on the ViewModel but to methods.

所以目前这是我最喜欢的 MVVM 风格:

So currently this is my favorite flavor of MVVM:

  1. 视图是愚蠢的:

  1. View is dumb:

  • 模型中需要的每个对象都有一个 ObjectDataProvider
  • 每个 ObjectDataProvider 都映射到 ViewModel 上的一个方法(不是属性)
  • XAML 元素中没有 x:Name 属性

ViewModel 很瘦:

ViewModel is skinny:

  • 您的 ViewModel 中唯一的东西是您的视图绑定到的方法

模特很胖:

  • 该模型对其每个属性都实现了 INotifyPropertyChanged.
  • 对于 ViewModel 上的每个方法(例如 GetCurrentCustomer),您的模型(例如 GetCurrentCustomer)中都有一个对应的单例方法.
  • 该模型负责处理本示例中的任何实时线程功能

问题:

  1. 那些在实际场景中实施 MVVM 的人,这是否也是您已经确定的基本结构,如果不是,您的结构有何不同?
  2. 您将如何扩展它以包含路由命令和路由事件?

如果您只是将 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));
            }
        }
    }
}

推荐答案

以下是我的看法,值得一提:

Here's my opinion, for what it's worth :

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

I don't really agree with the approach you suggest (except for the dumb view). In real life, you will often have to use an existing model : it could be legacy code that you don't have the time (or will) to change, or even a library for which you don't have the code. In my opinion, the model should be completely unaware of the way it will be displayed, and should be easily usable in a non-WPF application. So it doesn't have to implement any specific interface like INotifyPropertyChanged of INotifyCollectionChanged to make it usable in MVVM. I think that all the logic related to UI should reside in the ViewModel.

关于 RoutedEventsRoutedCommands,它们并不真正适合与 MVVM 模式一起使用.我通常尝试尽可能少地使用 RoutedEvents,并且根本不使用 RoutedCommands.相反,我的 ViewModel 公开了我在 XAML 中绑定到 UI 的 RelayCommand 属性(请参阅 这篇文章 由 Josh Smith 撰写,了解 RelayCommand 的详细信息).当我真的需要处理某些控件的事件时,我使用附加行为将事件映射到 ViewModel 命令(查看 Marlon Grech 的实现)

Regarding RoutedEvents and RoutedCommands, they are not really suitable for use with the MVVM pattern. I usually try to use as little RoutedEvents as possible, and no RoutedCommands at all. Instead, my ViewModels expose RelayCommand properties that I bind to the UI in XAML (see this article by Josh Smith for details on RelayCommand). When I really need to handle events for some control, I use attached behaviors to map the events to ViewModel commands (have a look at Marlon Grech's implementation)

所以,总结:

  • 愚蠢的看法
  • 大而智能的 ViewModel
  • 您想要或必须使用的任何模型

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

Of course it's just my approach, and it may not be the best, but I feel quite comfortable with it ;)

这篇关于胖模型、瘦视图模型和哑视图,最好的 MVVM 方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆