WPF快速入门系列(8)——MVVM快速入门

本是古典 何须时尚 2022-03-28 15:56 888阅读 0赞

一、引言

  在前面介绍了WPF一些核心的内容,其中包括WPF布局、依赖属性、路由事件、绑定、命令、资源样式和模板。然而,在WPF还衍生出了一种很好的编程框架,即WVVM,在Web端开发有MVC,在WPF客户端开发中有MVVM,其中VM就相当于MVC中C(Control)。在Web端,微软开发了Asp.net MVC这样的MVC框架,同样在WPF领域,微软也开发了Prism这样的MVVM框架。Prism项目地址是:http://compositewpf.codeplex.com/SourceControl/latest。大家有兴趣的可以下载源码研究下。

  本文所有源码下载:FristMVVMProject.zip

二、MVVM模式是什么?

  既然讲到MVVM模式,自然第一个问题就是MVVM的含义。MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发。这三者之间的关系如下图所示:

" class="reference-link">292137587471217.png

  下面我们分别来介绍下这三部分。

模型(Model)

  Model——可以理解为带有字段,属性的类。

视图(View)

  View——可以理解为我们所看到的UI。

视图模型(View Model)

View Model在View和Model之间,起到连接的作用,并且使得View和Model层分离。View Model不仅仅是Model的包装,它还包含了程序逻辑,以及Model扩展,例如,如果Model中有一个公开属性不需要在UI上显示,此时我们可以不再View Model中去定义它。

在MVVM模式下,WPF程序的运行流程如下图所示:

292150162002254.png

  在MVVM中,VM的地位可以说是举足轻重。使用MVVM模式具有以下几个特点:

  • 视图的cs文件包括极少的代码,其核心逻辑都被放在View Model类中,从而使得程序逻辑与视图耦合度降低。
  • ViewModel类作为View的DataContext。
  • 在MVVM下,所有的事件和动作都被当成命令,如按钮的点击操作,此时不是触发点击事件,而是绑定到一个点击命令,再由命令去执行对应的逻辑。

三、使用MVVM模式来实现WPF程序

  前面介绍了MVVM一些基础知识,下面通过一个实例来说明下如何在WPF程序中应用MVVM模式。在之前实现WPF程序时,我们可能会把所有的后台逻辑都放在视图的后台文件中,这样的实现方式的好处更直观,方便,对于一些小的应用程序这样做当然没什么问题,但是对于复杂的应用程序这样写的话,可能会导致后台代码显得非常臃肿,到最好变得难以维护。此时想到的解决方案就是职责分离,使后台的逻辑分离到其他类中,MVVM其实我理解就是达到这个目的。下面我们按照MVVM的组成部分来实现下这个MVVM程序。

  第一步:自然是数据部分了,即Model层的实现。在这里定义了一个Person类,其中包含了2个基本的属性。

  1. public class Person
  2. {
  3. public string Name { get; set; }
  4. public int Age { get; set; }
  5. }

  为了进行测试,下面创建一个静态方法来获得测试数据。

  1. public class PersonDataHelper
  2. {
  3. public static ObservableCollection<Person> GetPersons()
  4. {
  5. ObservableCollection<Person> samplePersons = new ObservableCollection<Person>();
  6. samplePersons.Add(new Person() {Name = "张三", Age = 33});
  7. samplePersons.Add(new Person() { Name ="王五", Age= 22 });
  8. samplePersons.Add(new Person() { Name = "李四", Age = 35 });
  9. samplePersons.Add(new Person() { Name = "LearningHard", Age = 27 });
  10. return samplePersons;
  11. }
  12. }

  第二步:实现ViewModel层,实现数据和界面之间的逻辑。在视图模型类中,包含了属性和命令,因为在MVVM中,事件都当成命令来进行处理,其中命令只能与具有Command属性的控件进行绑定。既然要包含命令,首先就需要实现一个命令,这里自定义的命令需要实现ICommand接口。这里我们定义了一个QueryCommand。具体的实现代码如下所示:

  1. public class QueryCommand :ICommand
  2. {
  3. #region Fields
  4. private Action _execute;
  5. private Func<bool> _canExecute;
  6. #endregion
  7. public QueryCommand(Action execute)
  8. : this(execute, null)
  9. {
  10. }
  11. public QueryCommand(Action execute, Func<bool> canExecute)
  12. {
  13. if (execute == null)
  14. throw new ArgumentNullException("execute");
  15. _execute = execute;
  16. _canExecute = canExecute;
  17. }
  18. #region ICommand Member
  19. public event EventHandler CanExecuteChanged
  20. {
  21. add
  22. {
  23. if (_canExecute != null)
  24. {
  25. CommandManager.RequerySuggested += value;
  26. }
  27. }
  28. remove
  29. {
  30. if (_canExecute != null)
  31. {
  32. CommandManager.RequerySuggested -= value;
  33. }
  34. }
  35. }
  36. public bool CanExecute(object parameter)
  37. {
  38. return _canExecute == null ? true : _canExecute();
  39. }
  40. public void Execute(object parameter)
  41. {
  42. _execute();
  43. }
  44. #endregion
  45. }

  接下来就是定义我们的ViewModel类了,具体的实现代码如下所示:

  1. 1 public class PersonListViewModel : INotifyPropertyChanged
  2. 2 {
  3. 3 #region Fields
  4. 4 private string _searchText;
  5. 5 private ObservableCollection<Person> _resultList;
  6. 6 #endregion
  7. 7
  8. 8 #region Properties
  9. 9
  10. 10 public ObservableCollection<Person> PersonList { get; private set; }
  11. 11
  12. 12 // 查询关键字
  13. 13 public string SearchText
  14. 14 {
  15. 15 get { return _searchText; }
  16. 16 set
  17. 17 {
  18. 18 _searchText = value;
  19. 19 RaisePropertyChanged("SearchText");
  20. 20 }
  21. 21 }
  22. 22
  23. 23 // 查询结果
  24. 24 public ObservableCollection<Person> ResultList
  25. 25 {
  26. 26 get { return _resultList; }
  27. 27 set
  28. 28 {
  29. 29 _resultList = value;
  30. 30 RaisePropertyChanged("ResultList");
  31. 31 }
  32. 32 }
  33. 33
  34. 34 public ICommand QueryCommand
  35. 35 {
  36. 36 get { return new QueryCommand(Searching, CanSearching); }
  37. 37 }
  38. 38
  39. 39 #endregion
  40. 40
  41. 41 #region Construction
  42. 42 public PersonListViewModel()
  43. 43 {
  44. 44 PersonList = PersonDataHelper.GetPersons();
  45. 45 _resultList = PersonList;
  46. 46 }
  47. 47
  48. 48 #endregion
  49. 49
  50. 50 #region Command Handler
  51. 51 public void Searching()
  52. 52 {
  53. 53 ObservableCollection<Person> personList = null;
  54. 54 if (string.IsNullOrWhiteSpace(SearchText))
  55. 55 {
  56. 56 ResultList = PersonList;
  57. 57 }
  58. 58 else
  59. 59 {
  60. 60 personList = new ObservableCollection<Person>();
  61. 61 foreach (Person p in PersonList)
  62. 62 {
  63. 63 if (p.Name.Contains(SearchText))
  64. 64 {
  65. 65 personList.Add(p);
  66. 66 }
  67. 67 }
  68. 68 if (personList != null)
  69. 69 {
  70. 70 ResultList = personList;
  71. 71 }
  72. 72 }
  73. 73 }
  74. 74
  75. 75 public bool CanSearching()
  76. 76 {
  77. 77 return true;
  78. 78 }
  79. 79
  80. 80 #endregion
  81. 81
  82. 82 #region INotifyPropertyChanged Members
  83. 83
  84. 84 public event PropertyChangedEventHandler PropertyChanged;
  85. 85
  86. 86 #endregion
  87. 87
  88. 88 #region Methods
  89. 89 private void RaisePropertyChanged(string propertyName)
  90. 90 {
  91. 91 // take a copy to prevent thread issues
  92. 92 PropertyChangedEventHandler handler = PropertyChanged;
  93. 93 if (handler != null)
  94. 94 {
  95. 95 handler(this, new PropertyChangedEventArgs(propertyName));
  96. 96 }
  97. 97 }
  98. 98 #endregion
  99. 99 }

  第三步:实现View层,设计我们的视图,设置它的DataContext属性为ViewModel类。具体的XAML代码如下所示:

  1. <Window x:Class="MVVMDemo.View.PersonsView"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:local="clr-namespace:MVVMDemo.ViewModel"
  5. Title="PersonsView" Height="350" Width="400">
  6. <!--设置DataContex是ViewModel类,当然你也可以使用后台代码设置-->
  7. <Window.DataContext>
  8. <local:PersonListViewModel />
  9. </Window.DataContext>
  10. <Grid>
  11. <Grid.RowDefinitions>
  12. <RowDefinition Height="50"/>
  13. <RowDefinition Height="*"/>
  14. </Grid.RowDefinitions>
  15. <TextBox Grid.Row="0" Name="searchtxt" Text="{Binding Path=SearchText, Mode=TwoWay}" HorizontalAlignment="Left" Height="30" Width="280" Margin="10,0,0,0"></TextBox>
  16. <Button Grid.Row="0" Name="searchBtn" Content="Search" Command="{Binding Path=QueryCommand}" Width="80" Height="30" HorizontalAlignment="Right" Margin="0,0,10,0"></Button>
  17. <DataGrid Grid.Row="1" Name="datGrid"
  18. HorizontalAlignment="Center"
  19. VerticalAlignment="Top" ItemsSource="{Binding Path=ResultList}" Width="300"></DataGrid>
  20. </Grid>
  21. </Window>

  到此,我们的MVVM的WPF程序就已经完成了,下面就是要看看程序是否达到我们预期的目的。具体的运行结果如下图所示:

292229285591261.gif

四、总结

  到这里,本文的内容就分享完了,并且本文也是WPF系列的最后一篇了,希望这个系列可以使得初学者快速上手WPF编程。在接下来的时间里,我打算写一些具有实战性的内容,因为我之前都是分享一些初级的入门系列,接下来打算分享一些实际的项目实现,以及领域驱动设计方面的内容,希望得到大家的督促和支持。

发表评论

表情:
评论列表 (有 0 条评论,888人围观)

还没有评论,来说两句吧...

相关阅读

    相关 k8s快速入门

    介绍   容器化部署   随着Docker技术的流行,对项目的容器化部署方式越来越流行,容器化部署的优点如下:   - 可以保证每个容器拥有自己的文件系统、CPU、内存、

    相关 SpringBoot系列<一>快速入门

    简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使

    相关 Groovy快速入门-8-方法

    这篇来学习方法,这个太重要了,每天写代码都是写在方法里。方法是任何编程语言的核心,就是处理一个特定需求的过程,都可以写到一个或者几个方法中。有些编程语言叫函数,Java这边习惯