WPF中使用MVVM模式开发有诸如以下优点:
1.结构清晰明朗,上手成本低,一个新人也能快速的定位自己需要改动代码的位置。
2.耦合度低,更改WPF控件不影响数据结构,更改代码成本低。
3.可重用性高,比如一个处理逻辑放到ViewModel模块中,其他界面如果要用掉直接调用即可。
当然并不是所有的项目都去使用MVVM开发,一些简单的我们也可以按照正常的开发。
本次例子参考“刘铁猛”老师的WPF视频教程,大家有兴趣可以去观看。
话不多说,开始写Demo。
这是一个很简单的Demo,我们的第一反应就是,不就是一个界面上有两个Button按钮。
一个名为:“保存”,一个名为:“相加”。然后给两个Button分别添加两个Click事件。
点保存按钮的时候弹出文件保存框:
SaveFileDialog dlg = new SaveFileDialog(); dlg.ShowDialog();
点击相加按钮的时候计算Textbox1(数字一)与TextBox2(数字二)输入的值想加的结果然后赋值给TextBox3(结果为):
textBox3.Text = (double.Parse(textBox1.Text)+ double.Parse(textBox2.Text)).ToString();
其实这样没错,但是现在我把界面改变了,把保存按钮换成了保存菜单,把三个Textbox换成了三个Slider,前面两个Slider想加的结果显示到第三个Slider上面。例如这样:
这个时候我们就需要在后台把之前的Click事件里面的代码进行修改,随着需求的不断变更,开发成本还是比较高的,这个是我们用MVVM模式来实现一下这些效果。
首先我们创建一个SampleMvvmDemo的WPF应用项目,然后在项目中添加两个文件夹
一个为Command用来触发空间的Command
一个为ViewModels用来处理页面上的数据或者控件逻辑。
这个是项目结构,简单的MVVM框架
然后我们在ViewModels中添加两个类
第一个为消息通知类(NotificationObject.cs),用来告诉页面值发生了变化,需要触发逻辑
using System.ComponentModel; namespace SampleMvvmDemo.ViewModels { class NotificationObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } } }
第二为窗体的交互逻辑类(MainWindowViewModel.cs),看名字也能得知这是界面与数据之间逻辑交互的地方。
using Microsoft.Win32; using SampleMvvmDemo.Commands; using System; namespace SampleMvvmDemo.ViewModels { class MainWindowViewModel : NotificationObject { public MainWindowViewModel() { AddCommand = new DelegateCommand(); AddCommand.ExecuteAction = new Action<object>(Add); SaveCommand = new DelegateCommand(); SaveCommand.ExecuteAction = new Action<object>(Save); } private double input1; public double Input1 { get { return input1; } set { input1 = value; RaisePropertyChanged("Input1"); } } private double input2; public double Input2 { get { return input2; } set { input2 = value; RaisePropertyChanged("Input2"); } } private double resulte; public double Result { get { return resulte; } set { resulte = value; RaisePropertyChanged("Result"); } } public DelegateCommand AddCommand { get; set; } public DelegateCommand SaveCommand { get; set; } private void Save(object parameter) { SaveFileDialog dlg = new SaveFileDialog(); dlg.ShowDialog(); } private void Add(object parameter) { Result = input1 + input2; } } }
写到这里的时候,会有些地方报错,这是因为我们Command还没有写方法
我们在Command文件夹中添加一个DelegateCommand.cs类,代码如下:
using System; using System.Windows.Input; namespace SampleMvvmDemo.Commands { class DelegateCommand : ICommand { public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { if (CanExcuteFunc == null) { return true; } return CanExcuteFunc(parameter); } public void Execute(object parameter) { if (ExecuteAction == null) { return; } ExecuteAction(parameter); } public Action<object> ExecuteAction { get; set; } public Func<object, bool> CanExcuteFunc { get; set; } } }
至此,我们已经把MVVM中的VM实现了。
接下来我们把界面设计好也就是MVVM中的V。
界面代码:
<Window x:Class="SampleMvvmDemo.MainWindow" 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" xmlns:local="clr-namespace:SampleMvvmDemo" mc:Ignorable="d" Title="MainWindow" Height="263" Width="300" WindowStartupLocation="CenterScreen"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Button Content="保存" Command="{Binding SaveCommand}"/> <Grid Grid.Row="1"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" > <TextBlock Text="数字一:" VerticalAlignment="Center"/> <TextBox x:Name="textBox1" Width="242" Background="LightBlue" FontSize="24" Margin="5" Text="{Binding Input1}"/> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <TextBlock Text="数字二:" VerticalAlignment="Center"/> <TextBox x:Name="textBox2" Width="242" Background="LightBlue" FontSize="24" Margin="5" Text="{Binding Input2}"/> </StackPanel> <StackPanel Grid.Row="2" Orientation="Horizontal"> <TextBlock Text="结果为:" VerticalAlignment="Center"/> <TextBox x:Name="textBox3" Width="242" Background="LightGreen" FontSize="24" Margin="5" Text="{Binding Result}"/> </StackPanel> <Button x:Name="btnAdd" Content="相加" Grid.Row="3" Width="120" Height="40" Command="{Binding AddCommand}"/> </Grid> </Grid> </Window>
界面后台代码:
using SampleMvvmDemo.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace SampleMvvmDemo { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainWindowViewModel(); } } }
我们可以看到View的后台代码非常的简洁。
即使像刚刚提到的改变控件的需求,我们也无需动后台的代码,只需要改变控件,重新绑定即可。
例如改成所说的Slider 与菜单保存
<Window x:Class="SampleMvvmDemo.MainWindow" 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" xmlns:local="clr-namespace:SampleMvvmDemo" mc:Ignorable="d" Title="MainWindow" Height="263" Width="300" WindowStartupLocation="CenterScreen"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Menu> <MenuItem Header="菜单"> <MenuItem Header="保存" Command="{Binding SaveCommand}"/> </MenuItem> </Menu> <Grid Grid.Row="1"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" > <TextBlock Text="数字一:" VerticalAlignment="Center"/> <Slider x:Name="textBox1" Width="242" Background="LightBlue" FontSize="24" Margin="5" Value="{Binding Input1}"/> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <TextBlock Text="数字二:" VerticalAlignment="Center"/> <Slider x:Name="textBox2" Width="242" Background="LightBlue" FontSize="24" Margin="5" Value ="{Binding Input2}"/> </StackPanel> <StackPanel Grid.Row="2" Orientation="Horizontal"> <TextBlock Text="结果为:" VerticalAlignment="Center"/> <Slider x:Name="textBox3" Width="242" Background="LightGreen" FontSize="24" Margin="5" Value="{Binding Result}"/> </StackPanel> <Button x:Name="btnAdd" Content="相加" Grid.Row="3" Width="120" Height="40" Command="{Binding AddCommand}"/> </Grid> </Grid> </Window>