胖模特,骨感的ViewModels哑意见,最好的办法MVVM? [英] Fat Models, skinny ViewModels and dumb Views, the best MVVM approach?

查看:120
本文介绍了胖模特,骨感的ViewModels哑意见,最好的办法MVVM?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过对这个问题慷慨的帮助,我总结了以下MVVM结构显示模型的实时变化XAML(当前日期/时间),非常漂亮。


  

这方面的一个凉爽的优势,建立了被
  当你看到你的观点的
  Visual Studio的设计模式
的或
  混合,的你看时间流逝通过的,
  这意味着的在设计时,你
  有机会获得来自住你的数据
  模型


在得到这个工作的过程中,我很惊讶地看到大多数从我的ViewModel批量招进我的模型,包括实施INotifyPropertyChange的。另一个变化是,我不再绑定到的属性的上视图模型,而是的方法

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


  1. 查看是哑巴:


    • 一个ObjectDataProvider的每个对象,你从你的模型需要

    • ObjectDataProvider的每一个映射到在视图模型(而不是属性)
    • 的方法
    • 无X:在XAML元素Name属性


  2. 视图模型是瘦:


    • 在您的视图模型的唯一事情是的方法的到您的视图绑定


  3. 型号是脂肪:


    • 模型上它的各个属性实现INotifyPropertyChanged。

    • 在您的视图模型(例如GetCurrentCustomer)每一个方法都有一个相应的单方法的在你的模型(例如GetCurrentCustomer)。

    • 模型需要照顾的实时线程的功能在这个例子中


问题:


  1. 你们谁已经在实际情况下一直执行MVVM,这是基本的结构,你也纷纷落户后,如果没有,请问你有什么不同?

  2. 您如何延长这包括路由命令和路由事件?

下面code会工作,如果你刚才复制的XAML和code后面进入一个新的WPF项目。

XAML:

 <窗​​口x:类=TestBinding99382.Window1
    的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
    的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
    XMLNS:地方=CLR的命名空间:TestBinding99382
    标题=窗口1HEIGHT =300WIDTH =300>    < Window.Resources>
        <的ObjectDataProvider
             X:键=DataSourceCustomer
             对象类型={X:类型本地:ShowCustomerViewModel}
                        方法名=GetCurrentCustomer/>
    < /Window.Resources>    < D​​ockPanel中的DataContext ={StaticResource的DataSourceCustomer}>
        < StackPanel的DockPanel.Dock =评出的方向=横向>
            < TextBlock的文本={绑定路径=姓}/>
            < TextBlock的文本=/>
            < TextBlock的文本={绑定路径=姓氏}/>
        < / StackPanel的>
        < StackPanel的DockPanel.Dock =评出的方向=横向>
            < TextBlock的文本={绑定路径= TimeOfMostRecentActivity}/>
        < / StackPanel的>    < / DockPanel中>
< /窗GT;

code背后:

 使用System.Windows;
使用System.ComponentModel;
使用系统;
使用的System.Threading;命名空间TestBinding99382
{
    公共部分类窗口1:窗口
    {
        公共窗口1()
        {
            的InitializeComponent();
        }
    }    //视图模型
    公共类ShowCustomerViewModel
    {
        公众客户GetCurrentCustomer(){
            返回Customer.GetCurrentCustomer();
        }
    }    //模型
    公共类客户:INotifyPropertyChanged的
    {
        私人字符串_FirstName;
        私人字符串_lastName;
        私人的DateTime _timeOfMostRecentActivity;
        私有静态客户_currentCustomer;
        私人定时器_timer;        公共字符串名字
        {
            得到
            {
                返回_FirstName;
            }
            组
            {
                _FirstName =价值;
                this.RaisePropertyChanged(名字);
            }
        }        公共字符串姓氏
        {
            得到
            {
                返回_lastName;
            }
            组
            {
                _lastName =价值;
                this.RaisePropertyChanged(姓氏);
            }
        }        公众的DateTime TimeOfMostRecentActivity
        {
            得到
            {
                返回_timeOfMostRecentActivity;
            }
            组
            {
                _timeOfMostRecentActivity =价值;
                this.RaisePropertyChanged(TimeOfMostRecentActivity);
            }
        }        公众客户()
        {
            _timer =新的Timer(UpdateDateTime,NULL,0,1000);
        }        私人无效UpdateDateTime(对象状态)
        {
            TimeOfMostRecentActivity = DateTime.Now;
        }        公共静态客户GetCurrentCustomer()
        {
            如果(_currentCustomer == NULL)
            {
                _currentCustomer =新客户
                     {名字=吉姆
                        ,姓氏=史密斯
                        ,TimeOfMostRecentActivity = DateTime.Now
                     };
            }
            返回_currentCustomer;
        }        // INotifyPropertyChanged的实施
        公共事件PropertyChangedEventHandler的PropertyChanged;
        私人无效RaisePropertyChanged(字符串属性)
        {
            如果(的PropertyChanged!= NULL)
            {
                的PropertyChanged(这一点,新PropertyChangedEventArgs(属性));
            }
        }
    }
}


解决方案

下面是我个人认为,这是非常值得:

我真的不跟你建议(除哑视图)中的方法一致。在现实生活中,你经常会使用现有的模型:它可能是旧的code,你没有时间(或会)改变,甚至库,你没有$ C $℃。在我看来,该模式应该是完全不知道它会显示的方式,而应该是在非WPF应用程序很容易使用。所以它没有实施 INotifyCollectionChanged INotifyPropertyChanged的任何特定的接口,使其在MVVM使用。我认为,所有与UI逻辑应该驻留在视图模型。

关于 RoutedEvents RoutedCommands ,他们是不是真的适合与MVVM模式中使用。我通常尝试使用尽可能少 RoutedEvents 越好,没有 RoutedCommands 可言。相反,我的ViewModels揭露,我绑定到XAML的UI(见的 RelayCommand 属性/dd419663.aspx\">this文章约什 - 史密斯的详细信息关于 RelayCommand )。当我真的需要处理事件的一些控制,我用附加的行为,以视图模型命令的事件映射(看看的马龙格列奇的实现

因此​​,简言之:


  • 阿呆查看

  • 大和智能视图模型

  • 您希望或有任何模型中使用

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

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.

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.

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.

So currently this is my favorite flavor of MVVM:

  1. View is dumb:

    • one ObjectDataProvider for each object you need from your model
    • each ObjectDataProvider maps to a method on the ViewModel (not a property)
    • no x:Name properties in XAML elements
  2. ViewModel is skinny:

    • the only thing in your ViewModel are the methods to which your view binds
  3. Model is fat:

    • the model implements INotifyPropertyChanged on each of its properties.
    • for every method on your ViewModel (e.g. GetCurrentCustomer) there is a corresponding singleton method in your Model (e.g. GetCurrentCustomer).
    • the model takes care of any real time threading functionality as in this example

Questions:

  1. Those of you who have been implementing MVVM in real scenarios, is this the basic structure you have also settled upon, and if not, how does yours vary?
  2. How would you extend this to include routed commands and routed events?

The following code will work if you just copy the XAML and code behind into a new WPF project.

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>

Code Behind:

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 :

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.

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)

So, in summary :

  • Dumb View
  • Big and smart ViewModel
  • Any model you want or have to use

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

这篇关于胖模特,骨感的ViewModels哑意见,最好的办法MVVM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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