zoukankan      html  css  js  c++  java
  • 走进WPF之MVVM完整案例

    学习WPF如果不学MVVM,仿佛缺少了灵魂。那什么是MVVM呢?为什么要学MVVM呢,本以一个简单的增删改查的小例子,简述MVVM的基本知识及如何通过进行MVVM架构的程序开发,仅供学习分享使用,如有不足之处,还请指正。

    什么是MVVM?

    MVVM是Model-View-ViewModel的简写。它本质上就是MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。分别定义如下:

    • 【模型】指的是后端传递的数据。
    • 【视图】指的是所看到的页面。
    • 【视图模型】mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:
      • 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
      • 二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定。

    MVVM示意图如下所示:

    安装MvvmLight插件

    项目名称右键-->管理NuGet程序包-->搜索MvvmLight-->安装。如下所示:

     弹出接受许可证窗口,点击【接受】如下所示:

     MvvmLight安装成功后,自动引用需要的第三方库,并默认生成示例内容,有些不需要的需要删除,如下所示:

    MVVM示例截图

    主要通过MVVM实现数据的CRUD【增删改查】基础操作,如下所示:

     MVVM开发步骤

    1. 创建Model层

    本例主要是对学生信息的增删改查,所以创建Student模型类,如下所示:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace WpfApp3.Models
     8 {
     9     /// <summary>
    10     /// 学生类
    11     /// </summary>
    12     public class Student
    13     {
    14         /// <summary>
    15         /// 唯一标识
    16         /// </summary>
    17         public int Id { get; set; }
    18 
    19         /// <summary>
    20         /// 学生姓名
    21         /// </summary>
    22         public string Name { get; set; }
    23 
    24         /// <summary>
    25         /// 年龄
    26         /// </summary>
    27         public int Age { get; set; }
    28 
    29         /// <summary>
    30         /// 班级
    31         /// </summary>
    32         public string Classes { get; set; }
    33     }
    34 }

    2. 创建DAL层

    为了简化示例,模拟数据库操作,构建基础数据,如下所示:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using WpfApp3.Models;
     7 
     8 namespace WpfApp3.DAL
     9 {
    10     public class LocalDb
    11     {
    12         private List<Student> students;
    13 
    14         public LocalDb() {
    15             init();
    16         }
    17 
    18         /// <summary>
    19         /// 初始化数据
    20         /// </summary>
    21         private void init() {
    22             students = new List<Student>();
    23             for (int i = 0; i < 30; i++)
    24             {
    25                 students.Add(new Student()
    26                 {
    27                     Id=i,
    28                     Name=string.Format("学生{0}",i),
    29                     Age=new Random(i).Next(0,100),
    30                     Classes=i%2==0?"一班":"二班"
    31                 });
    32             }
    33         }
    34 
    35         /// <summary>
    36         /// 查询数据
    37         /// </summary>
    38         /// <returns></returns>
    39         public List<Student> Query()
    40         {
    41             return students;
    42         }
    43 
    44         /// <summary>
    45         /// 按名字查询
    46         /// </summary>
    47         /// <param name="name"></param>
    48         /// <returns></returns>
    49         public List<Student> QueryByName(string name)
    50         {
    51             return students.Where((t) => t.Name.Contains(name)).ToList();//FindAll((t) => t.Name.Contains(name));
    52         }
    53 
    54         public Student QueryById(int Id)
    55         {
    56             var student = students.FirstOrDefault((t) => t.Id == Id);
    57             if (student != null)
    58             {
    59                 return new Student() { 
    60                     Id=student.Id,
    61                     Name=student.Name,
    62                     Age=student.Age,
    63                     Classes=student.Classes
    64                 };
    65             }
    66             return null;
    67         }
    68 
    69 
    70         /// <summary>
    71         /// 新增学生
    72         /// </summary>
    73         /// <param name="student"></param>
    74         public void AddStudent(Student student)
    75         {
    76             if (student != null)
    77             {
    78                 students.Add(student);
    79             }
    80         }
    81 
    82         /// <summary>
    83         /// 删除学生
    84         /// </summary>
    85         /// <param name="Id"></param>
    86         public void DelStudent(int Id)
    87         {
    88             var student = students.FirstOrDefault((t) => t.Id == Id); //students.Find((t) => t.Id == Id);
    89             if (student != null)
    90             {
    91                 students.Remove(student);
    92             }
    93             
    94         }
    95     }
    96 
    97 
    98 }

    3. 创建View层

    View层与用户进行交互,用户数据的展示,及事件的响应。在本例中,View层主要有数据查询展示,新增及编辑页面。

    在View层,主要是命令的绑定,及数据的绑定。

    1. 在DataGridTextColumn中通过Binding="{Binding Id}"的形式绑定要展示的列属性名。
    2. 在Button按钮上通过Command="{Binding AddCommand}"的形式绑定要响应的命令。
    3. 在TextBox文本框中通过Text="{Binding Search}"的形式绑定查询条件属性。

    数据展示窗口,如下所示:

     1 <Window x:Class="WpfApp3.MainWindow"
     2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     6         xmlns:local="clr-namespace:WpfApp3"
     7         mc:Ignorable="d"
     8         Title="MainWindow" Height="450" Width="800">
     9     <Grid>
    10         <Grid.RowDefinitions>
    11             <RowDefinition Height="80"></RowDefinition>
    12             <RowDefinition Height="*"></RowDefinition>
    13         </Grid.RowDefinitions>
    14         <StackPanel Orientation="Horizontal" Grid.Row="0" Margin="5" VerticalAlignment="Center">
    15             <TextBlock Text="姓名:" Margin="10" Padding="5"></TextBlock>
    16             <TextBox x:Name="sname" Text="{Binding Search}" Width="120" Margin="10" Padding="5"></TextBox>
    17             <Button x:Name="btnQuery" Content="查询" Margin="10" Padding="5" Width="80" Command="{Binding QueryCommand}"></Button>
    18             <Button x:Name="btnReset" Content="重置" Margin="10" Padding="5" Width="80" Command="{Binding ResetCommand}"></Button>
    19             <Button x:Name="btnAdd" Content="创建" Margin="10" Padding="5" Width="80"  Command="{Binding AddCommand}"></Button>
    20         </StackPanel>
    21         <DataGrid x:Name="dgInfo" Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="False" CanUserSortColumns="False" Margin="10" ItemsSource="{Binding GridModelList}">
    22             <DataGrid.Columns>
    23                 <DataGridTextColumn Header="Id" Width="100"  Binding="{Binding Id}"></DataGridTextColumn>
    24                 <DataGridTextColumn Header="姓名" Width="100" Binding="{Binding Name}"></DataGridTextColumn>
    25                 <DataGridTextColumn Header="年龄" Width="100" Binding="{Binding Age}"></DataGridTextColumn>
    26                 <DataGridTextColumn Header="班级" Width="100" Binding="{Binding Classes}"></DataGridTextColumn>
    27                 <DataGridTemplateColumn Header="操作" Width="*">
    28                     <DataGridTemplateColumn.CellTemplate>
    29                         <DataTemplate>
    30                             <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
    31                                 <Button x:Name="edit" Content="编辑" Width="60" Margin="3" Height="25" CommandParameter="{Binding Id}" Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"></Button>
    32                                 <Button x:Name="delete" Content="删除" Width="60" Margin="3" Height="25"  CommandParameter="{Binding Id}" Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"></Button>
    33                             </StackPanel>
    34                         </DataTemplate>
    35                     </DataGridTemplateColumn.CellTemplate>
    36                 </DataGridTemplateColumn>
    37             </DataGrid.Columns>
    38         </DataGrid>
    39     </Grid>
    40 </Window>

    新增及编辑页面,如下所示:

     1 <Window x:Class="WpfApp3.Views.StudentWindow"
     2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     6         xmlns:local="clr-namespace:WpfApp3.Views"
     7         mc:Ignorable="d"
     8         Title="StudentWindow" Height="440" Width="500" AllowsTransparency="False" WindowStartupLocation="CenterScreen" WindowStyle="None">
     9     <Grid>
    10         <Grid.RowDefinitions>
    11             <RowDefinition Height="60"></RowDefinition>
    12             <RowDefinition></RowDefinition>
    13             <RowDefinition Height="60"></RowDefinition>
    14         </Grid.RowDefinitions>
    15         <TextBlock FontSize="30" Margin="10">修改学生信息</TextBlock>
    16         <StackPanel Grid.Row="1" Orientation="Vertical">
    17             <TextBlock FontSize="20" Margin="10" Padding="5">姓名</TextBlock>
    18             <TextBox x:Name="txtName" FontSize="20"  Padding="5" Text="{Binding Model.Name}"></TextBox>
    19             <TextBlock FontSize="20" Margin="10"  Padding="5">年龄</TextBlock>
    20             <TextBox x:Name="txtAge" FontSize="20"  Padding="5" Text="{Binding Model.Age}"></TextBox>
    21             <TextBlock FontSize="20" Margin="10"  Padding="5">班级</TextBlock>
    22             <TextBox x:Name="txtClasses" FontSize="20"  Padding="5" Text="{Binding Model.Classes}"></TextBox>
    23         </StackPanel>
    24         <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
    25             <Button x:Name="btnSave" Content="保存" Margin="10" FontSize="20" Width="100" Click="btnSave_Click" ></Button>
    26             <Button x:Name="btnCancel" Content="取消" Margin="10" FontSize="20" Width="100" Click="btnCancel_Click" ></Button>
    27         </StackPanel>
    28     </Grid>
    29 </Window>

    3. 创建ViewModel层

    ViewModel层是MVVM的核心所在,起到承上启下的作用。ViewModel需要继承GalaSoft.MvvmLight.ViewModelBase基类。

    ViewModel中属性实现数据的绑定,命令实现用户交互的响应。如下所示:

      1 using GalaSoft.MvvmLight;
      2 using GalaSoft.MvvmLight.Command;
      3 using System.Collections.Generic;
      4 using System.Collections.ObjectModel;
      5 using System.Linq;
      6 using System.Windows;
      7 using WpfApp3.DAL;
      8 using WpfApp3.Models;
      9 using WpfApp3.Views;
     10 
     11 namespace WpfApp3.ViewModel
     12 {
     13     /// <summary>
     14     /// 
     15     /// </summary>
     16     public class MainViewModel : ViewModelBase
     17     {
     18         #region 属性及构造函数
     19 
     20         private LocalDb localDb;
     21 
     22         private ObservableCollection<Student> gridModelList;
     23 
     24         public ObservableCollection<Student> GridModelList
     25         {
     26             get { return gridModelList; }
     27             set
     28             {
     29                 gridModelList = value;
     30                 RaisePropertyChanged();
     31             }
     32         }
     33 
     34         /// <summary>
     35         /// 查询条件
     36         /// </summary>
     37         private string search;
     38 
     39         public string Search
     40         {
     41             get { return search; }
     42             set
     43             {
     44                 search = value;
     45                 RaisePropertyChanged();
     46             }
     47         }
     48 
     49 
     50         /// <summary>
     51         /// 
     52         /// </summary>
     53         public MainViewModel()
     54         {
     55             localDb = new LocalDb();
     56             QueryCommand = new RelayCommand(this.Query);
     57             ResetCommand = new RelayCommand(this.Reset);
     58             EditCommand = new RelayCommand<int>(this.Edit);
     59             DeleteCommand = new RelayCommand<int>(this.Delete);
     60             AddCommand = new RelayCommand(this.Add);
     61         }
     62 
     63         #endregion
     64 
     65         #region command
     66 
     67         /// <summary>
     68         /// 查询命令
     69         /// </summary>
     70         public RelayCommand QueryCommand { get; set; }
     71 
     72         /// <summary>
     73         /// 重置命令
     74         /// </summary>
     75         public RelayCommand ResetCommand { get; set; }
     76 
     77         /// <summary>
     78         /// 编辑
     79         /// </summary>
     80         public RelayCommand<int> EditCommand { get; set; }
     81 
     82         /// <summary>
     83         /// 删除
     84         /// </summary>
     85         public RelayCommand<int> DeleteCommand { get; set; }
     86 
     87         /// <summary>
     88         /// 新增
     89         /// </summary>
     90         public RelayCommand AddCommand { get; set; }
     91 
     92         #endregion
     93 
     94         public void Query()
     95         {
     96             List<Student> students;
     97             if (string.IsNullOrEmpty(search))
     98             {
     99                 students = localDb.Query();
    100             }
    101             else
    102             {
    103                 students = localDb.QueryByName(search);
    104             }
    105 
    106             GridModelList = new ObservableCollection<Student>();
    107             if (students != null)
    108             {
    109                 students.ForEach((t) =>
    110                 {
    111                     GridModelList.Add(t);
    112                 });
    113             }
    114         }
    115 
    116         /// <summary>
    117         /// 重置
    118         /// </summary>
    119         public void Reset()
    120         {
    121             this.Search = string.Empty;
    122             this.Query();
    123         }
    124 
    125         /// <summary>
    126         /// 编辑
    127         /// </summary>
    128         /// <param name="Id"></param>
    129         public void Edit(int Id)
    130         {
    131             var model = localDb.QueryById(Id);
    132             if (model != null)
    133             {
    134                 StudentWindow view = new StudentWindow(model);
    135                 var r = view.ShowDialog();
    136                 if (r.Value)
    137                 {
    138                     var newModel = GridModelList.FirstOrDefault(t => t.Id == model.Id);
    139                     if (newModel != null)
    140                     {
    141                         newModel.Name = model.Name;
    142                         newModel.Age = model.Age;
    143                         newModel.Classes = model.Classes;
    144                     }
    145                     this.Query();
    146                 }
    147             }
    148         }
    149 
    150         /// <summary>
    151         /// 删除
    152         /// </summary>
    153         /// <param name="Id"></param>
    154         public void Delete(int Id)
    155         {
    156             var model = localDb.QueryById(Id);
    157             if (model != null)
    158             {
    159                 var r = MessageBox.Show($"确定要删除吗【{model.Name}】?","提示",MessageBoxButton.YesNo);
    160                 if (r == MessageBoxResult.Yes)
    161                 {
    162                     localDb.DelStudent(Id);
    163                     this.Query();
    164                 }
    165             }
    166         }
    167 
    168         /// <summary>
    169         /// 新增
    170         /// </summary>
    171         public void Add()
    172         {
    173             Student model = new Student();
    174             StudentWindow view = new StudentWindow(model);
    175             var r = view.ShowDialog();
    176             if (r.Value)
    177             {
    178                 model.Id = GridModelList.Max(t => t.Id) + 1;
    179                 localDb.AddStudent(model);
    180                 this.Query();
    181             }
    182         }
    183     }
    184 }

    4. 数据上下文

    当各个层分别创建好后,那如何关联起来呢?答案就是DataContext【数据上下文】。

    查询页面上下文,如下所示:

     1 namespace WpfApp3
     2 {
     3     /// <summary>
     4     /// MainWindow.xaml 的交互逻辑
     5     /// </summary>
     6     public partial class MainWindow : Window
     7     {
     8         public MainWindow()
     9         {
    10             InitializeComponent();
    11             MainViewModel viewModel = new MainViewModel();
    12             viewModel.Query();
    13             this.DataContext = viewModel;
    14         }
    15     }
    16 }

    新增页面上下文,如下所示:

     1 namespace WpfApp3.Views
     2 {
     3     /// <summary>
     4     /// StudentWindow.xaml 的交互逻辑
     5     /// </summary>
     6     public partial class StudentWindow : Window
     7     {
     8         public StudentWindow(Student student)
     9         {
    10             InitializeComponent();
    11             this.DataContext = new
    12             {
    13                 Model = student
    14             };
    15         }
    16 
    17         private void btnSave_Click(object sender, RoutedEventArgs e)
    18         {
    19             this.DialogResult = true;
    20         }
    21 
    22         private void btnCancel_Click(object sender, RoutedEventArgs e)
    23         {
    24             this.DialogResult = false;
    25         }
    26     }
    27 }

    总结

    MVVM具有低耦合,可重用,可测试,独立开发的优点,核心要素就两个:

    • 属性发生变化时的通知,即可达到数据的实时更新。
    • 命令是实现用户与程序之间数据和算法的桥梁。

    备注

    本文作为MVVM的简单入门示例,旨在抛砖引玉,一起学习,共同进步。如果对WPF的其他入门知识,不是很了解,可以参考其他博文。

    玉楼春·别后不知君远近

    欧阳修 〔宋代〕

    别后不知君远近,触目凄凉多少闷。渐行渐远渐无书,水阔鱼沉何处问。
    夜深风竹敲秋韵,万叶千声皆是恨。故攲单枕梦中寻,梦又不成灯又烬。注:攲(yǐ)


    作者:小六公子
    出处:http://www.cnblogs.com/hsiang/
    本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
    关注个人公众号,定时同步更新技术及职场文章

  • 相关阅读:
    Saltstack module gem 详解
    Saltstack module freezer 详解
    Saltstack module firewalld 详解
    Saltstack module file 详解
    Saltstack module event 详解
    Saltstack module etcd 详解
    Saltstack module environ 详解
    Saltstack module drbd 详解
    Saltstack module dnsutil 详解
    获取主页_剥离百度
  • 原文地址:https://www.cnblogs.com/hsiang/p/15579839.html
Copyright © 2011-2022 走看看