现在我们进入Prism的一条重要支柱MVVM模式。MVVM模式简单来说就是把页面UI和后台逻辑分开,这样做的好处是能使你的程序更容易测试,维护和改进。下面的图来自于Prism4的教程显示了MVVM模式的基本工作原理:
好,废话少说,开始Coding吧。
打开之前创建的MyPrism程序:
其中HelloWorldModule下只有一个View页面HelloWorldView.xaml,其只实现一个简单显示HelloWorld控件作用,不包括任何逻辑代码。
为了实现MVVM模式,我们需要添加一个ViewModel文件和Model文件。
在Helloworldmodule下新建两个文件夹,命名为Models和ViewModels,分别添加一个cs文件,如下图:
接下来,修改HellowWorldView页面,实现一个简单的加法界面
代码如下:
<UserControl x:Class="HelloWorldModule.Views.HelloWorldView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ds="clr-namespace:HelloWorldModule.ViewModels"
d:DesignHeight="429" d:DesignWidth="618" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<UserControl.DataContext>
<ds:HelloWorldViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="63"/>
<RowDefinition Height="40"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="5" Text="Hello World" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>
<TextBox Grid.Row="1" Grid.Column="0" Height="23" Name="Number1" Text="{Binding HelloWorldModel.Number1, Mode=TwoWay}" Width="120"/>
<TextBox Grid.Row="1" Grid.Column="2" Height="23" Name="Number2" Text="{Binding HelloWorldModel.Number2, Mode=TwoWay}" Width="120"/>
<sdk:Label Grid.Row="1" Grid.Column="4" Height="28" Name="Result" Content="{Binding HelloWorldModel.Result, Mode=TwoWay}" Width="120"/>
<sdk:Label Grid.Row="1" Grid.Column="1" Height="23" Name="label1" Content="+" Width="30"/>
<sdk:Label Grid.Row="1" Grid.Column="3" Height="22" Name="label2" Content="=" Width="30"/>
<Button Content="Add" Grid.Row="2" Grid.Column="4" Command="{Binding AddCommand}" Height="25" Name="btnAdd" Width="75"/>
</Grid>
</UserControl>
其中的关键代码:
xmlns:ds="clr-namespace:HelloWorldModule.ViewModels"
<UserControl.DataContext>
<ds:HelloWorldViewModel/>
</UserControl.DataContext>
这里吧HellowWorldViewModel类所为HellowWorldView的DataContext,也就是说View中的所有数据绑定都在HellowWorldViewModel类中定义。
<TextBox Grid.Row="1" Grid.Column="0" Height="23" Name="Number1" Text="{Binding HelloWorldModel.Number1, Mode=TwoWay}" Width="120"/>
<TextBox Grid.Row="1" Grid.Column="2" Height="23" Name="Number2" Text="{Binding HelloWorldModel.Number2, Mode=TwoWay}" Width="120"/>
<sdk:Label Grid.Row="1" Grid.Column="4" Height="28" Name="Result" Content="{Binding HelloWorldModel.Result, Mode=TwoWay}" Width="120"/>
这三个控件中,Text绑定了HelloWorldModel.Number1属性,并且是双向绑定,双向绑定既是,如果程序既可以将HelloWorldModel.Number1属性绑定到页面,也可以从页面得到数据。如将其改为单向绑定,程序就无法读取客户输入的数字了。
<Button Content="Add" Grid.Row="2" Grid.Column="4" Command="{Binding AddCommand}" Height="25" Name="btnAdd" Width="75"/>
此处将Button绑定到我们自定义的AddCommand中。
好接下来我们就要在HellowWorldViewModel类中实现Number1,Number2,Result这个三个绑定参数和AddCommand命令。
下面是HellowWorldModel类代码
using System.ComponentModel;
namespace HelloWorldModule.Models
{
public class HelloWorldModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Number1 { get; set; }
public int Number2 { get; set; }
private int result;
public int Result
{
get
{
return this.result;
}
set
{
if (value != this.result)
{
this.result = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Result"));
}
}
}
}
}
}
在HellowWorldModel类中我们定义了基本的参数属性Number1,Number2,Result。Number1,Number2较为简单。Result中包含了一个PropertyChanged的事件,该事件的作用简单来说就是,一旦Result值发生了变化,他将通知所有的绑定Result的控件,刷新数据。为了实现该事件,该类必须继承INotifyPropertyChanged。具体的原理这里就不多说了,网上多的是。
HellowWorldViewModel类代码
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using HelloWorldModule.Models;
namespace HelloWorldModule.ViewModels
{
public class HelloWorldViewModel
{
private readonly HelloWorldModel helloWorldModel;
public HelloWorldViewModel()
{
this.helloWorldModel = new HelloWorldModel();
this.AddCommand = new DelegateCommand<object>(this.OnSubmit);
}
public HelloWorldModel HelloWorldModel
{
get { return this.helloWorldModel; }
}
public ICommand AddCommand { get; private set; }
private void OnSubmit(object obj)
{
helloWorldModel.Result = helloWorldModel.Number1 + helloWorldModel.Number2;
}
}
}
HellowWorldViewModel中创建了一个HellowWorldModel的实例,同时创建AddCommand方法,并实现其具体操作OnSubmit,这些都用来绑定到HellowWorldView中。
OnSubmit中将Number1和Number2的数据相加赋给了Result,由于属性Result实现了PropertyChanged事件,其值一发生变化,便刷新了Result控件值。
好所有的代码都完成了,按F5键运行,得到以下结果。
这篇文章主要是谈了MVVM模式的基本架构和功能实现。关键就在于数据绑定和PropertyChanged事件。
另外读者可能会有疑惑为什么要把HellowWorldModel类和HellowWorldViewModel类分开写,其实完全可以把两者和为一个类。Prism官方给出的ViewModel和Model的区别是:
ViewModel:The view model in the MVVM pattern encapsulates the presentation logic and data for the view. Viewmodel封装了展现的逻辑和数据。
Model:The model in the MVVM pattern encapsulates business logic and data. Model封装了业务的逻辑和数据。
笔者里的理解是分开是为了使代码更清晰,便于阅读。像页面操作如本例中的数据相加操作属于页面的逻辑,因此放在HellowWorldViewModel中,如果有数据库读取或者和其他模块交互,那就属于业务逻辑,就应该放在HellowWorldModel中。
希望大家指正。
源代码:https://files.cnblogs.com/mindflying/MyPrism_BasicMVVM.zip