一、引言
WPF的一些核心内容,包括WPF布局、依赖属性、路由事件、绑定、命令、资源风格和模板。然而,WPF也衍生出一个很好的编程框架,即WVVM,在Web端开发MVC,在WPF客户端开发MVVM,其中VM相当于MVC(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的开发。三者之间的关系如下图所示:
让我们分别介绍这三个部分。
模型(Model)
Model——可以理解为具有字段和属性的类别。
视图(View)
View——可以理解为我们看到的UI。
视图模型(View Model)
View 在View和Model之间,Model起着连接作用,使View和Model层分离。View Model不仅是Model的包装,还包括程序逻辑和Model的扩展。例如,如果Model中有一个不需要显示在UI上的公共属性,我们就不能再view了 在Model中定义它。
WPF程序的运行流程如下图所示:
VM在MVM中的地位可以说是非常重要的。MVVM模式的使用具有以下特点:
- 视图中的cs文件包含很少的代码,其核心逻辑被放置在Vieww 在Model类中,程序逻辑和视图耦合度降低。
- 作为View的Datacontext,ViewModel类。
- 在MVVM下,所有的事件和动作都被视为命令,比如按钮的点击。此时,它不是触发点击事件,而是绑定到点击命令中,然后命令执行相应的逻辑。
本文介绍了MVVM的一些基本知识,以下是如何在WPF程序中应用MVVM模式的例子。在实现WPF程序之前,我们可能会把所有的背景逻辑放在视图背景文件中,这种实现的好处更直观、方便,对于一些小应用程序当然没有问题,但对于复杂的应用程序,可能会导致背景代码非常臃肿,最好变得难以维护。在这个时候,我想到的解决方案是分离责任,将后台的逻辑分离到其他类别。事实上,我理解MVVM就是为了实现这一目标。下面我们将根据MVVM的组成部分来实现这个MVVM程序。
第一步:自然是数据部分,即实现Model层。这里定义了一个包含两个基本属性的Person类。
public class Person { public string Name { get; set; } public int Age { get; set; } }
以下是一种获取测试数据的静态方法。
public class PersonDataHelper { public static ObservableCollection<Person> GetPersons() { ObservableCollection<Person> samplePersons = new ObservableCollection<Person>(); samplePersons.Add(new Person() {Name = "张三", Age = 33}); samplePersons.Add(new Person() { Name ="王五", Age= 22 }); samplePersons.Add(new Person() { Name = "李四", Age = 35 }); samplePersons.Add(new Person() { Name = "LearningHard", Age = 27 }); return samplePersons; } }
第二步:实现ViewModel层,实现数据与界面之间的逻辑。视图模型中包含属性和命令,因为在MVVM中,事件被视为命令,命令只能与具有Command属性的控制器绑定。既然要包含命令,首先要实现一个命令,这里自定义的命令需要实现ICommand接口。在这里,我们定义了一个QueryComand。具体实现代码如下:
public class QueryCommand :ICommand { #region Fields private Action _execute; private Func<bool> _canExecute; #endregion public QueryCommand(Action execute) : this(execute, null) { } public QueryCommand(Action execute, Func<bool> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #region ICommand Member public event EventHandler CanExecuteChanged { add { if (_canExecute != null) { CommandManager.RequerySuggested += value; } } remove { if (_canExecute != null) { CommandManager.RequerySuggested -= value; } } } public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(); } public void Execute(object parameter) { _execute(); } #endregion }
下一步是定义我们的ViewModel类,具体实现代码如下:
1 public class PersonListViewModel : INotifyPropertyChanged 2 { 3 #region Fields 4 private string _searchText; 5 private ObservableCollection<Person> _resultList; 6 #endregion 7 8 #region Properties 9 10 public ObservableCollection<Person> PersonList { get; private set; }11 12 // 查询关键字13 public string Searchtextt144 {15 get { return _searchText; }16 set17 {18 _searchText = value;19 RaisePropertyChanged("SearchText");20 }21 }22 23 // 查询结果24 public ObservableCollection<Person> Resultlist25 {26 get { return _resultList; }27 set28 {29 _resultList = value;30 RaisePropertyChanged("ResultList");31 }32 }33 34 public ICommand QueryCommand 35 { 36 get { return new QueryCommand(Searching, CanSearching); } 37 }38 39 #endregion 40 41 #region construction public PersonListViewModel()43 {44 PersonList = PersonDataHelper.GetPersons();45 _resultList = PersonList;46 }47 48 #endregion49 50 #region Command Handler51 public void Searching()52 {53 ObservableCollection<Person> personList = null;54 if (string.IsNullOrWhiteSpace(SearchText))55 {56 ResultList = PersonList;57 }58 else59 {60 personList = new ObservableCollection<Person>();61 foreach (Person p in PersonList)62 {63 if (p.Name.Contains(SearchText))64 {65 personList.Add(p);66 }67 }68 if (personList != null)69 {70 ResultList = personList;71 }72 }73 }74 75 public bool CanSearching()76 {77 return true;78 }79 80 #endregion 81 82 #region INotifyPropertyChanged Members83 84 public event PropertyChangedEventHandler PropertyChanged;85 86 #endregion87 88 #region Methods89 private void RaisePropertyChanged(string propertyName)90 {91 // take a copy to prevent thread issues92 PropertyChangedEventHandler handler = PropertyChanged;93 if (handler != null)94 {95 handler(this, new PropertyChangedEventArgs(propertyName));96 }97 }98 #endregion 99 }
第三步:实现View层,设计我们的视图,将其Datacontext属性设置为ViewModel类。具体XAML代码如下:
<Window x:Class="MVVMDemo.View.PersonsView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MVVMDemo.ViewModel" Title="PersonsView" Height="350" Width="400"> <!--将Datacontex设置为ViewModel类,当然也可以使用后台代码设置--> <Window.DataContext> <local:PersonListViewModel /> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBox Grid.Row="0" Name="searchtxt" Text="{Binding Path=SearchText, Mode=TwoWay}" HorizontalAlignment="Left" Height="30" Width="280" Margin="10,0,0,0"></TextBox> <Button Grid.Row="0" Name="searchBtn" Content="Search" Command="{Binding Path=QueryCommand}" Width="80" Height="30" HorizontalAlignment="Right" Margin="0,0,10,0"></Button> <DataGrid Grid.Row="1" Name="datGrid" HorizontalAlignment="Center" VerticalAlignment="Top" ItemsSource="{Binding Path=ResultList}" Width="300"></DataGrid> </Grid></Window>
到目前为止,我们的MVVMWPF程序已经完成,这取决于程序是否达到了我们的预期目的。具体操作结果如下图所示:
四、总结在这里,本文的内容被分享,本文也是WPF系列的最后一篇文章。我希望这个系列能让初学者快速开始WPF编程。在接下来的时间里,我计划写一些实用的内容,因为我以前分享一些初级入门系列,然后我计划分享一些实际的项目实现和领域驱动设计,我希望得到每个人的监督和支持。