因为上一篇的案例是没实现的,我感觉很不爽,因为我不知道怎么处理这个,所以我去找博主的视频看了,结果视频做的是实现了一个页面的增删改查操作,跟文档里的是不一样的。不过这个是运行成功的,所以我要记录一下。

设计

功能点

①使用GataGrid控件显示数据

②使用了Command命令

③上下文关联的理解

④较为标准的MVVM,只是有点简单,但是用于理解MVVM还是够的

实现效果

WPF MVVM框架实现增删改查 - 图1

具体实现

1、导入NuGet包

WPF MVVM框架实现增删改查 - 图2

该包已经弃用了,因为视频已经很老了,不过还是可以用的,现在WPF主流框架应该是Prism,不过没用过,留一个坑在这。

这个包导进来之后,默认会在目录下创建ViewModel层的示例代码。

WPF MVVM框架实现增删改查 - 图3

删除ViewModelLocator.cs文件,因为本案例中用不到,还会报错!同时将MainViewModel.cs中不必要的注释、App.xaml资源引用删掉。

2、Model

新建一个Model文件夹,里面添加一个Student类:

WPF MVVM框架实现增删改查 - 图4

本案例中就以Student作为MVVM中的Model。

  1. using GalaSoft.MvvmLight;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace MVVMTestDemo.Models
  8. {
  9. public class Student : ViewModelBase
  10. {
  11. private int _id;
  12. private string _name;
  13. public int Id
  14. {
  15. get { return _id; }
  16. set
  17. {
  18. _id = value;
  19. RaisePropertyChanged();
  20. }
  21. }
  22. public string Name
  23. {
  24. get { return _name; }
  25. set
  26. {
  27. _name = value;
  28. RaisePropertyChanged();
  29. }
  30. }
  31. }
  32. }

3、创建数据源

新建一个Db文件夹,添加一个localDb类:

WPF MVVM框架实现增删改查 - 图5

这个类就充当案例中DataGrid的数据源。

  1. using MVVMTestDemo.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace MVVMTestDemo.Db
  8. {
  9. /// <summary>
  10. /// 该类就是充当数据源
  11. /// </summary>
  12. public class localDb
  13. {
  14. public localDb()
  15. {
  16. Init();
  17. }
  18. private List<Student> Students;
  19. /// <summary>
  20. /// 初始化一个学生列表,里面包含30条数据
  21. /// </summary>
  22. private void Init()
  23. {
  24. Students = new List<Student>();
  25. for (int i = 0; i < 30; i++)
  26. {
  27. Students.Add(new Student()
  28. {
  29. Id = i,
  30. Name = $"Sample{i}"
  31. });
  32. }
  33. }
  34. /// <summary>
  35. /// 公共的方法用来获取学生列表
  36. /// </summary>
  37. /// <returns>学生列表</returns>
  38. public List<Student> GetStudents()
  39. {
  40. return Students;
  41. }
  42. /// <summary>
  43. /// 往学生列表中添加学生
  44. /// </summary>
  45. /// <param name="stu"></param>
  46. public void AddStudent(Student stu)
  47. {
  48. Students.Add(stu);
  49. }
  50. /// <summary>
  51. /// 通过输入的Id来查找学生进行删除操作
  52. /// </summary>
  53. /// <param name="id"></param>
  54. public void DelStudent(int id)
  55. {
  56. var model = Students.FirstOrDefault(t => t.Id == id);
  57. if(model != null)
  58. {
  59. Students.Remove(model);
  60. }
  61. }
  62. /// <summary>
  63. /// 通过学生姓名来"模糊"查找学生
  64. /// </summary>
  65. /// <param name="name">学生姓名</param>
  66. /// <returns>返回叫这个名字的学生列表</returns>
  67. public List<Student> GetStudentsByName(string name)
  68. {
  69. return Students.Where(q => q.Name.Contains(name)).ToList();
  70. }
  71. public Student GetStudentById(int id)
  72. {
  73. var model = Students.FirstOrDefault(t => t.Id == id);
  74. if (model != null)
  75. {
  76. return new Student()
  77. {
  78. Id = model.Id,
  79. Name = model.Name
  80. };
  81. }
  82. return null;
  83. }
  84. }
  85. }
  1. **视频作者在书写这个类的时候,给了我一些启发**:
  1. 首先在构造器里,使用了一个封装的Init()方法,而不是直接将Init()方法中的内容直接写进构造器里面,按我目前的理解,这应该是一种习惯吧,封装的习惯,我也应该培养一下。作用都差不多,在其他类中声明这个类的对象的时候,都会用到构造器,然后对一些属性进行初始化。作者这里的封装Init()方法也可以起到相同的作用。同时,封装的这个private的Init()方法也可以在localDb这个类中继续使用。
  2. 多次使用Lambda表达式,虽然都是很简单的,但是对于一个不怎么用Lambda的人来说,这是个很好的机会来进行理解和锻炼。
  3. 这个类中的最后一个GetStudentById(int id)方法,是作者修改后的版本了,原本是一句很简单的return语句://return Students.FirstOrDefault(t => t.Id == id);//这里最好不要使用查到的"引用",因为会涉及到一个"同步改变"的情况。这个语句时候来作者实现其他功能的时候发现的bug,因为这里获取到的直接是List中的Student对象,所以会直接对这个对象进行修改,造成一种TwoWay的效果,但是实际上我们不想这样。总结下来的话就是一句话,代码刚写出的来的不用考虑那么多,先实现就好,不用考虑太多细节,后面再修改就可以了

4、ViewModel

本案例的重点部分:这个类也是导入包的时候自动生成的。

  1. using GalaSoft.MvvmLight;
  2. using GalaSoft.MvvmLight.Command;
  3. using MVVMTestDemo.Db;
  4. using MVVMTestDemo.Models;
  5. using MVVMTestDemo.Views;
  6. using System.Collections.ObjectModel;
  7. using System.Linq;
  8. using System.Windows;
  9. namespace MVVMTestDemo.ViewModel
  10. {
  11. public class MainViewModel : ViewModelBase
  12. {
  13. public MainViewModel()
  14. {
  15. localDb = new localDb();
  16. QueryCommand = new RelayCommand(Query);
  17. ResetCommand = new RelayCommand(() =>
  18. {
  19. Search = string.Empty;
  20. this.Query();
  21. });
  22. EditCommand = new RelayCommand<int>(t => Edit(t));
  23. DelCommand = new RelayCommand<int>(t => Del(t));
  24. AddCommand = new RelayCommand(Add);
  25. }
  26. localDb localDb;
  27. private string _search = string.Empty;
  28. public string Search
  29. {
  30. get { return _search; }
  31. set
  32. {
  33. _search = value;
  34. RaisePropertyChanged();
  35. }
  36. }
  37. private ObservableCollection<Student> _gridModelList;
  38. public ObservableCollection<Student> GridModelList
  39. {
  40. get { return _gridModelList; }
  41. set
  42. {
  43. _gridModelList = value;
  44. RaisePropertyChanged();
  45. }
  46. }
  47. #region Command
  48. public RelayCommand QueryCommand { get; set; }
  49. public RelayCommand ResetCommand { get; set; }
  50. public RelayCommand<int> EditCommand { get; set; }
  51. public RelayCommand<int> DelCommand { get; set; }
  52. public RelayCommand AddCommand { get; set; }
  53. #endregion
  54. /// <summary>
  55. /// 将localDb中的学生列表传给GridModelList属性,用于给DataGrid控件显示数据
  56. /// </summary>
  57. public void Query()
  58. {
  59. var models = localDb.GetStudentsByName(Search);
  60. GridModelList = new ObservableCollection<Student>();
  61. if (models != null)
  62. {
  63. models.ForEach(arg =>
  64. {
  65. GridModelList.Add(arg);
  66. });
  67. }
  68. }
  69. public void Edit(int id)
  70. {
  71. var model = localDb.GetStudentById(id);
  72. if (model != null)
  73. {
  74. UserView view = new UserView(model);
  75. var r = view.ShowDialog();
  76. if (r.Value)
  77. {
  78. var newModel = GridModelList.FirstOrDefault(t => t.Id == model.Id);
  79. if (newModel != null)
  80. {
  81. newModel.Name = model.Name;
  82. }
  83. }
  84. }
  85. }
  86. public void Del(int id)
  87. {
  88. var model = localDb.GetStudentById(id);
  89. if (model != null)
  90. {
  91. var r = MessageBox.Show($"确认删除当前用户:{model.Name}?", "操作提示", MessageBoxButton.OK, MessageBoxImage.Question);
  92. if (r == MessageBoxResult.OK)
  93. localDb.DelStudent(model.Id);
  94. this.Query();
  95. }
  96. }
  97. public void Add()
  98. {
  99. Student student = new Student();
  100. UserView view = new UserView(student);
  101. var r = view.ShowDialog();
  102. if (r.Value)
  103. {
  104. student.Id = GridModelList.Max(t => t.Id) + 1;
  105. localDb.AddStudent(student);
  106. this.Query();
  107. }
  108. }
  109. }
  110. }
  1. <Window x:Class="MVVMTestDemo.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:MVVMTestDemo"
  7. mc:Ignorable="d"
  8. Title="MainWindow" Height="450" Width="800">
  9. <Grid>
  10. <Grid.RowDefinitions>
  11. <RowDefinition Height="60"/>
  12. <RowDefinition/>
  13. </Grid.RowDefinitions>
  14. <StackPanel Orientation="Horizontal">
  15. <TextBlock Text="搜索条件:" VerticalAlignment="Center"/>
  16. <TextBox Width="200" Text="{Binding Search}" Height="25" Margin="10 0 0 0"/>
  17. <Button Content="查找" Command="{Binding QueryCommand}" Width="70" Height="25" Margin="10 0 0 0"/>
  18. <Button Content="重置" Command="{Binding ResetCommand}" Width="70" Height="25" Margin="10 0 0 0"/>
  19. <Button Content="新增" Command="{Binding AddCommand}" Width="70" Height="25" Margin="10 0 0 0"/>
  20. </StackPanel>
  21. <DataGrid Grid.Row="1" ColumnWidth="*" AutoGenerateColumns="False"
  22. CanUserAddRows="False" ItemsSource="{Binding GridModelList}">
  23. <DataGrid.Columns>
  24. <DataGridTextColumn Header="序号" Binding="{Binding Id}"/>
  25. <DataGridTextColumn Header="姓名" Binding="{Binding Name}"/>
  26. <DataGridTemplateColumn Header="操作">
  27. <DataGridTemplateColumn.CellTemplate>
  28. <DataTemplate>
  29. <StackPanel Orientation="Horizontal">
  30. <Button Content="修改" Width="60" Height="25" Background="White" Foreground="Black"
  31. CommandParameter="{Binding Id}"
  32. Command="{Binding DataContext.EditCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}"/>
  33. <Button Content="删除" Width="60" Height="25" Background="Red" Foreground="White"
  34. CommandParameter="{Binding Id}"
  35. Command="{Binding DataContext.DelCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}"/>
  36. </StackPanel>
  37. </DataTemplate>
  38. </DataGridTemplateColumn.CellTemplate>
  39. </DataGridTemplateColumn>
  40. </DataGrid.Columns>
  41. </DataGrid>
  42. </Grid>
  43. </Window>
  1. MainViewModel这个类很多的命令、方法都是根据页面进展一点一点填充起来的。这里也用到了一些高级的用法。需要慢慢嚼透。
  1. using MVVMTestDemo.ViewModel;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Windows.Documents;
  11. using System.Windows.Input;
  12. using System.Windows.Media;
  13. using System.Windows.Media.Imaging;
  14. using System.Windows.Navigation;
  15. using System.Windows.Shapes;
  16. namespace MVVMTestDemo
  17. {
  18. /// <summary>
  19. /// MainWindow.xaml 的交互逻辑
  20. /// </summary>
  21. public partial class MainWindow : Window
  22. {
  23. public MainWindow()
  24. {
  25. InitializeComponent();
  26. MainViewModel viewModel = new MainViewModel();
  27. viewModel.Query();
  28. //添加上下文绑定,MainViewModel用于后台逻辑,MainWindow用于数据展示
  29. this.DataContext = viewModel;
  30. }
  31. }
  32. }

5、View

新建一个View文件夹,新建一个UserView的页面,用来展示”编辑用户信息”页面。

WPF MVVM框架实现增删改查 - 图6

  1. <Window x:Class="MVVMTestDemo.Views.UserView"
  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:MVVMTestDemo.Views"
  7. mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen"
  8. Title="UserView" Height="350" Width="400">
  9. <Grid>
  10. <Grid.RowDefinitions>
  11. <RowDefinition Height="60"/>
  12. <RowDefinition/>
  13. <RowDefinition Height="60"/>
  14. </Grid.RowDefinitions>
  15. <TextBlock Text="编辑用户信息" FontWeight="Bold" FontSize="30" Margin="10 0 0 0" VerticalAlignment="Center"/>
  16. <StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center">
  17. <TextBlock Text="姓名:" VerticalAlignment="Center"/>
  18. <TextBox Width="200" Height="25" Text="{Binding Model.Name}"/>
  19. </StackPanel>
  20. <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">
  21. <Button Width="70" Height="25" Content="确定" Click="btn_Save"/>
  22. <Button Width="70" Height="25" Content="取消" Margin="10 0 10 0" Click="btn_Cancel"/>
  23. </StackPanel>
  24. </Grid>
  25. </Window>
  1. using MVVMTestDemo.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Windows.Documents;
  11. using System.Windows.Input;
  12. using System.Windows.Media;
  13. using System.Windows.Media.Imaging;
  14. using System.Windows.Shapes;
  15. namespace MVVMTestDemo.Views
  16. {
  17. /// <summary>
  18. /// UserView.xaml 的交互逻辑
  19. /// </summary>
  20. public partial class UserView : Window
  21. {
  22. public UserView(Student stu)
  23. {
  24. InitializeComponent();
  25. this.DataContext = new
  26. {
  27. Model = stu
  28. };
  29. }
  30. private void btn_Save(object sender, RoutedEventArgs e)
  31. {
  32. this.DialogResult = true;
  33. }
  34. private void btn_Cancel(object sender, RoutedEventArgs e)
  35. {
  36. this.DialogResult = false;
  37. }
  38. }
  39. }

小结

案例中用到了很多许久没接触过的知识点,当然还要在熟悉熟悉。然后再来补