前言

MVVM 就是 Model – View – ViewModel 三组功能(类)分割的设计模式。废话不多说,不知道的自己上网查。

用 MVVM 我认为最大好处是能对 ViewModel 做单元测试。另外,MVVM 分工也比较明显,方便安排程序员分组分工进行项目,基本设计有了之后可以各自敲。

这样的话,写出来,类(class)最起码有三个。比如 Window1 作为 View,Window1ViewModel 作为 ViewModel,实际业务类比如 Sales Order 销售订单作为 Model。

View 不一定要是 System.Control.Window,UserControl 也可以,Page也行,总之,是 UI 用来显示用的。

常用基类

有两个基类,做 MVVM 你有的话会方便一些:

  1. ViewModelBase
  2. RelayCommand / DelegateCommand

我以下代码都不会列这两个出来。

ViewModelBase 代码比较常见,搜索然后抄下来就可以了,然后写 ViewModel 比如 Window1ViewModel 类时候,继承自 ViewModelBase 即可。它的主要作用,是提供 OnPropertyChange(string propertyName) 这方法,告诉视图 View 知道,值发生变化需要更新显示。它的代码,比如如下:

/// <summary>
/// Provides common functionality for ViewModel classes
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
    public virtual string DisplayName { get; set; }

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

#region IDisposable Members

public void Dispose()
    {
        this.OnDispose();
    }

protected virtual void OnDispose()
    {
    }

#endregion
}

个人爱好,删掉 DisplayName 或者自己加其他属性请自便,以它为基类做 ViewModel 时候代码会简单一些。

RelayCommand / DelegateCommand 代码也是比较常见,搜索一下抄下来就是。它是一个实现了 ICommand 接口的类。做命令的绑定,比如 Button 中的 Command 属性,绑定时它的类型要求是 ICommand 的东西。ICommand 比起点击事件优胜的地方是,ICommand 除了委托执行方法以外,还有一个 CanExecute 的委托,可以自动 Enable / Disable 按钮。代码比如:

/// <summary>
/// Base Relay Command implements ICommand for easy delegation
/// </summary>
public class RelayCommand : ICommand
{
    #region Fields

readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

#endregion // Fields

#region Constructors

public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

_execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

#region ICommand Members

[DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

public void Execute(object parameter)
    {
        _execute(parameter);
    }

#endregion // ICommand Members

}

WPF 入门请点这里:十五分钟 WPF 入门

要注意的杂项

有几点要注意:

  1. ICommand 一般不传参数,不是不可以,只是一般来说没必要。一切值都在 ViewModel 内的时候,你不用让 View 再告诉你什么新鲜事
  2. 一般来说一个 ViewModel 对一个 View
  3. WPF 的 PasswordBox 做不了绑定,据说是安全性原因,没办法
  4. 不一定在 View 的背后代码一句都不写才叫做 MVVM,但操作数据的不会在 View 内出现,操作 View 的不会在 ViewModel 出现

示范

来个简单的示范:

View 部分

  1. <Window x:Class="WPF_Binding_Example.MainView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="Window1" Height="300" Width="300">
  5. <Window.Resources>
  6. <Style x:Key="myStyle" TargetType="StackPanel">
  7. <Style.Triggers>
  8. <DataTrigger Binding="{Binding Path=Age}"
  9. Value="0">
  10. <Setter Property="Background"
  11. Value="Yellow"/>
  12. </DataTrigger>
  13. </Style.Triggers>
  14. </Style>
  15. <DataTemplate x:Key="PersonTemplate">
  16. <StackPanel Margin="2"
  17. Orientation="Horizontal"
  18. Style="{StaticResource myStyle}">
  19. <TextBlock Text="{Binding Name}"
  20. Width="50"/>
  21. <TextBlock Text="{Binding Age}"/>
  22. </StackPanel>
  23. </DataTemplate>
  24. </Window.Resources>
  25. <Grid>
  26. <ListView Margin="38,50,33,59"
  27. Name="listView1"
  28. ItemsSource="{Binding PersonList}"
  29. ItemTemplate="{StaticResource PersonTemplate}"
  30. />
  31. <Button Command="{Binding AddRowCommand}"
  32. Height="23"
  33. Width="75"
  34. HorizontalAlignment="Right"
  35. Margin="0,0,11,10"
  36. VerticalAlignment="Bottom"
  37. Content="加一行" />
  38. </Grid>
  39. </Window>

画这界面的同事,只需要知道三件事:

  1. ViewModel 内会有一个叫做 PersonList 的公共类集合(30 行),要用 ListView 显示,按内容做些样式
  2. PersonList 类集合内的单个对象,有两个公共属性,分别是 Name 和 Age (20、22行),但不需要知道实际是什么类
  3. ViewModel 内会有一个实现了 ICommand 接口的实例引用,名字是 AddRowCommand(33行)

然后,ViewModel 部分

  1. using System.Collections.ObjectModel;
  2. using System.Windows.Input;
  3. using WPF_Binding_Example.Domain;
  4. using WPF_Binding_Example.Infrastructure;
  5. namespace WPF_Binding_Example
  6. {
  7. public class MainViewModel : ViewModelBase
  8. {
  9. public MainViewModel()
  10. {
  11. personList = new ObservableCollection<Person>();
  12. //演示用
  13. Add_Dummy_Data();
  14. }
  15. ///<summary>
  16. /// 演示用
  17. ///</summary>
  18. private void Add_Dummy_Data()
  19. {
  20. PersonList.Add(
  21. new Person { Name = "张三", Age = 26 }
  22. );
  23. PersonList.Add(
  24. new Person { Name = "李四", Age = 24 }
  25. );
  26. }
  27. private ObservableCollection<Person> personList;
  28. public ObservableCollection<Person> PersonList
  29. {
  30. get { return personList; }
  31. }
  32. private RelayCommand addRowCommand;
  33. public ICommand AddRowCommand
  34. {
  35. get
  36. {
  37. if (addRowCommand == null)
  38. {
  39. addRowCommand =
  40. new RelayCommand(x => this.AddRow());
  41. }
  42. return addRowCommand;
  43. }
  44. }
  45. private void AddRow()
  46. {
  47. this.PersonList.Add(
  48. new Person { Name = "我是新人", Age = 0 }
  49. );
  50. }
  51. }
  52. }

负责这个类的同事,不需要知道界面的任何东西,这类的代码也非常简单。

ObservableCollection 在 System.Collections.ObjectModel 内,它与其他集合的分别是,集合有变化时,比如加减 Item 等,它会发出通知告诉视图。如果你有自己很有个性的 Collection,要做绑定的话,让它实现  INotifyCollectionChanged 和 INotifyPropertyChanged 即可。

RelayCommand 上面说了,是实现了 ICommand,所以出现了 44-47 行这样的写法。刚才说了 ICommand 还有个 CanExecute 的委托,这里没用到。RelayCommand 的设计是,构造函数参数只有一个方法委托时候,CanExecute 默认返回 True,即永远可执行。

然后是 Model 部分

  1. namespace WPF_Binding_Example.Domain
  2. {
  3. public class Person
  4. {
  5. private string name;
  6. public string Name
  7. {
  8. get { return name; }
  9. set
  10. {
  11. if (value != name)
  12. {
  13. name = value;
  14. }
  15. }
  16. }
  17. private int age;
  18. public int Age
  19. {
  20. get { return age; }
  21. set
  22. {
  23. if (value != age)
  24. {
  25. age = value;
  26. }
  27. }
  28. }
  29. }
  30. }

View 连接 ViewModel

说了半天,除了 ViewModel 和 Model 在上面代码有点关系以外,View 不认识 ViewModel,ViewModel 不认识 View,怎样连在一起?

这样:

  1. using System.Windows;
  2. namespace WPF_Binding_Example
  3. {
  4. ///<summary>
  5. /// Interaction logic for App.xaml
  6. ///</summary>
  7. public partial class App : Application
  8. {
  9. protected override void OnStartup(StartupEventArgs e)
  10. {
  11. base.OnStartup(e);
  12. MainViewModel vm = new MainViewModel();
  13. MainView view = new MainView();
  14. view.DataContext = vm;
  15. view.Show();
  16. }
  17. }
  18. }

请把 WPF 新建项目时,模板自带 app.xaml 的 XAML 代码中 StartupUri 属性删除,然后在 App 类重写 OnStartup 方法,如上。

把 View 和 ViewModel 连在一起的,就一句 view.DataContext = vm;  有人会用 view 构造函数注入 ViewModel,也可以反过来在 ViewModel 构造函数注入 view,然后在 IoC 注册后直接 resolve 出来用,或者写在 XAML 内也行,但都离不开这一句 DataContext = vm。本例子中是在 Application 类重写 OnStartup 方法,把两者创建实例,然后 view.Show 来实现。

接下去会介绍怎样单元测试,和各种情景各种数据结构和控件,怎样用 MVVM 模式做绑定。下一篇,是采购订单做法的一个示例。

我在这群里,欢迎加入交流:
.Net 开发交流 595769918

开发板玩家群 578649319
硬件创客 (10105555)

转载于:https://www.cnblogs.com/leptonation/archive/2012/03/29/2422575.html

C# WPF MVVM 实战 - 1相关推荐

  1. C# WPF MVVM 实战 – 3 – 树结构

    树结构放在 WPF ,有大家熟悉的 TreeView.Menu / MenuItem 等等,自定义的话它是 HierarchicalDataTemplate. 用上 MVVM 模式,视图与数据分离,意 ...

  2. C# WPF MVVM项目实战(进阶②)

    这篇文章还是在之前用Caliburn.Micro搭建好的框架上继续做的开发,今天主要是增加了一个用户窗体ImageProcessView,然后通过Treeview切换选择项之后在界面显示不同效果的图片 ...

  3. C# 值得永久收藏的WPF项目实战(经典)

    01 - 简介 之前也写过好多篇CM框架相关的项目实战文章,比如: C# WPF框架Caliburn.Micro快速搭建 C# WPF框架Caliburn.Micro入门实例1 C# WPF MVVM ...

  4. C# WPF MVVM模式Prism框架下事件发布与订阅

    01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...

  5. C# WPF MVVM模式Prism框架从零搭建(经典)

    01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...

  6. WPF MVVM 弹框之等待框

    WPF MVVM 弹框之等待框 目录 一.效果 二.弹框主体改造 三.等待动画用户控件 四.弹窗 ViewModel 和帮助类的改造 五.使用方法和代码地址 独立观察员 2020年10月13日 之前写 ...

  7. C# WPF MVVM模式Caliburn.Micro框架下事件发布与订阅

    01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Caliburn提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间 ...

  8. WPF MVVM从入门到精通1:MVVM模式简介

    WPF MVVM从入门到精通1:MVVM模式简介 原文:WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录 ...

  9. WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍...

    WPF自学入门(十一)WPF MVVM模式Command命令 在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式.正 ...

  10. (WPF, MVVM) Event 处理

    原文:(WPF, MVVM) Event 处理 WPF的有些UI元素有Command属性可以直接实现绑定,如Button 但是很多Event的触发如何绑定到ViewModel中的Command呢? 答 ...

最新文章

  1. 如何查看自己Oracle的版本
  2. .NET Core开发实战(第25课:路由与终结点:如何规划好你的Web API)--学习笔记(下)...
  3. TFS在项目中DevOps落地进程(下)
  4. textlayout Java_Java TextLayout.getBounds方法代码示例
  5. python实现两个接口的依赖关系
  6. 测试人员的系统性思维
  7. ubuntu 下载速度快但网页打开慢的解决方法
  8. Codeforces Round #484 (Div. 2) D. Shark
  9. MSSQL → 04:表的创建与维护
  10. Data Science With R In Visual Studio
  11. 有限元工程应用方法-ANSYS单元刚度矩阵计算理论详解
  12. android 照片同步 icloud,如何将iCloud照片传输到Android手机
  13. 什么是WINSOCK WSADATA
  14. 《高情商修炼手册》 by 小粥超人(小hi)Hygge @Chou
  15. 辅警小丁同志 灵宝西站派出所第一警务区赵桥
  16. python的pyecharts(群图乱舞)可视化神器
  17. 在java程序中制作动图
  18. 第三方登录之微信登录
  19. 最大熵模型怎么理解?熵是什么??
  20. 1556_AURIX_TC275_复位系统控制单元

热门文章

  1. 算法(c++)——循环比赛日程安排问题
  2. 南邮CTF逆向题第三道Py交易解题思路
  3. Ubuntu fastboot 烧写
  4. Word排版(小计)
  5. windows系统重装(绝对干净)、备份还原、引导修复步骤
  6. sigmoid代码实现
  7. Python获取Excel中超链接并下载至本地
  8. java 系统临时目录_在java中创建临时文件夹
  9. 常用软件开发安装包+破解分享
  10. 星际争霸2 AI开发