大智能ViewModels,哑视图和任何模型,最好的MVVM方法?

Edw*_*uay 8 c# architecture wpf mvvm

下面的代码是我以前的MVVM方法的重构(Fat Models,瘦的ViewModels和dumb Views,最好的MVVM方法?),我将逻辑和INotifyPropertyChanged实现从模型中移回ViewModel.这更有意义,因为正如所指出的,您经常必须使用您无法更改或不想更改的模型,因此您的MVVM方法应该能够使用任何模型类,因为它碰巧存在.

此示例仍允许您在Visual Studio和Expression Blend中以设计模式查看模型中的实时数据,我认为这很重要,因为您可以拥有设计器连接的模拟数据存储,例如最小和最大的字符串. UI可能会遇到,以便他可以根据这些极端情况调整设计.

问题:

  • 我有点惊讶我甚至不得不在我的ViewModel中"放一个计时器",因为它似乎是INotifyPropertyChanged的一个功能,它似乎是多余的,但这是我可以不断获得XAML UI的唯一方法(一次)每秒)反映我的模型的状态.因此,如果您遇到任何不利因素,例如使用线程或性能,那么听到任何可能采用这种方法的人都会很有趣.

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

XAML:

<Window x:Class="TestMvvm73892.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestMvvm73892"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ObjectDataProvider 
              x:Key="DataSourceCustomer" 
              ObjectType="{x:Type local:CustomerViewModel}" 
             MethodName="GetCustomerViewModel"/>
    </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>
Run Code Online (Sandbox Code Playgroud)

代码背后:

using System;
using System.Windows;
using System.ComponentModel;
using System.Threading;

namespace TestMvvm73892
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }

    //view model
    public class CustomerViewModel : INotifyPropertyChanged
    {
        private string _firstName;
        private string _lastName;
        private DateTime _timeOfMostRecentActivity;
        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 CustomerViewModel()
        {
            _timer = new Timer(CheckForChangesInModel, null, 0, 1000);
        }

        private void CheckForChangesInModel(object state)
        {
            Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();
            MapFieldsFromModeltoViewModel(currentCustomer, this);
        }

        public static CustomerViewModel GetCustomerViewModel()
        {
            CustomerViewModel customerViewModel = new CustomerViewModel();
            Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();

            MapFieldsFromModeltoViewModel(currentCustomer, customerViewModel);

            return customerViewModel;
        }

        public static void MapFieldsFromModeltoViewModel
             (Customer model, CustomerViewModel viewModel) 
        {
            viewModel.FirstName = model.FirstName;
            viewModel.LastName = model.LastName;
            viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity;
        }

        public static Customer GetCurrentCustomer()
        {
            return Customer.GetCurrentCustomer();
        }


        //INotifyPropertyChanged implementation
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

    }

    //model
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime TimeOfMostRecentActivity { get; set; }

        public static Customer GetCurrentCustomer()
        {
            return new Customer 
                       { FirstName = "Jim"
                         , LastName = "Smith"
                         , TimeOfMostRecentActivity = DateTime.Now 
                       };
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

Joe*_*ran 13

我喜欢上面的示例,我认为它实现了MVVM的精神.但是,为了澄清,ViewModel代码和Model代码不应与实际Code Behind位于同一源文件中.事实上,我认为他们不应该在同一个项目中.

根据我的理解,这是MVVM:

M - 模型是从业务层(BL)返回的数据.这应该是轻量级的,包含只读数据.Model类是哑的并且不包含Update,Write或Delete逻辑,并且由BL作为请求,命令,操作等的结果生成.Model类不知道消费应用程序的表示需求,因此可以通过任何方式使用它们.为了真正利用这种可重用性,我们希望Model类独立于UI项目.

VM - ViewModel包含通信层:它向BL发出请求并以适合呈现的方式处理结果.与上面的示例一样,它也接收模型并根据特定的表示需求重新格式化.把它想象成一个"绑定类".在上面的示例中,数据只是从一个对象移动到下一个对象,但ViewModel将负责诸如公开"FullName"类型属性或将前导零添加到ZipCode之类的事情.绑定类是实现INotifyPropertyChanged的绑定类是正确的.而且,为了可重用性,我可能也会将此层提取到自己的项目中.这将允许您尝试各种UI选项而不进行管道更改.

V - View绑定到VM中创建的Binding类对象.View非常愚蠢:它对BL或VM一无所知.数据可以在两个方向上绑定,但VM处理错误,验证等.任何数据同步操作都是通过将请求传回BL,并再次处理结果来处理的.

这取决于应用程序的类型,但是不断检查模型以确定它是否已经改变似乎很重要.假装您正在连接到从DAL构建业务对象(BO)的BL,该DAL连接到DB.在这种情况下,你会不断重新创建BO,我肯定会成为性能杀手.您可以在BL上实现一个侦听通知的结帐系统,或者有一个方法可以将上次已知的更改时间与实际进行比较,或者您可以将BL缓存在BL上.只是一些想法.

另外,我在上面说过,模型应该是轻量级的.有很多重量级的选择,比如CSLA,但我不确定它们是否适合MVVM的想法.

我不是故意让自己成为一名专家,到目前为止,我在设计新软件架构时只研究过这些想法.我想读一些关于这个主题的讨论.

  • CRUD方法属于业务层.VM只是将请求传递给BL.所以流程将是这样的:V响应用户输入并触发ICommand,ICommand在VM上执行一个方法,该方法将请求发送给BL,BL返回一个M对象,VM处理并转换为绑定对象,被传回V. (4认同)