WPF MVVM入门系列教程(六、ViewModel案例演示)

发布于:2025-05-09 ⋅ 阅读:(13) ⋅ 点赞:(0)

  🧭 WPF MVVM入门系列教程


在前面的文章中,介绍了ViewModel的基础概念

本文会使用一些实例来进行ViewModel的演示

一个基础的数据展示示例

假设我们要在界面上对一本书籍的详细信息进行展示。

首先我们定义一下View

MainWindow.xaml

界面上我们定义了两列,左边一列用于展示书籍封面,右边一列用于展示详细信息

 1 <Window x:Class="_1_ViewModelStartup.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:_1_ViewModelStartup"
 7         mc:Ignorable="d"
 8         Title="MainWindow" Height="500" Width="900" WindowStartupLocation="CenterScreen">
 9     <Grid>
10         <Grid.ColumnDefinitions>
11             <ColumnDefinition/>
12             <ColumnDefinition/>
13         </Grid.ColumnDefinitions>
14 
15         <Image Source="{Binding Book.CoverImageUrl}" Stretch="Uniform" Margin="20"></Image>
16 
17         <StackPanel Grid.Column="1">
18             <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Book.Title}" FontSize="25" FontWeight="Bold" Margin="10"></TextBlock>
19             <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Book.Descrption}" FontSize="20" Foreground="Silver" Margin="10"></TextBlock>
20             <Separator Height="1" Foreground="Silver" Margin="10"></Separator>
21             <StackPanel Orientation="Horizontal" Margin="10">
22                 <Label Content="作者:"></Label>
23                 <Label Content="{Binding Book.Author}" Foreground="Silver"  ></Label>
24             </StackPanel>
25             <StackPanel Orientation="Horizontal" Margin="10">
26                 <Label Content="出版社:"></Label>
27                 <Label Content="{Binding Book.Publish}" Foreground="Silver"  ></Label>
28             </StackPanel>
29             <StackPanel Orientation="Horizontal" Margin="10">
30                 <Label Content="价格 ¥: " Foreground="Red" FontWeight="Bold"></Label>
31                 <Label Content="{Binding Book.Price}" Foreground="Silver"  ></Label>
32             </StackPanel>
33             <StackPanel Orientation="Horizontal" Margin="10">
34                 <Label Content="出版日期:"></Label>
35                 <Label Content="{Binding Book.Date}" Foreground="Silver"  ></Label>
36             </StackPanel>
37         </StackPanel>
38     </Grid>
39 
40 </Window>

然后我们定义一下Model

Book.cs

 1 public class Book : INotifyPropertyChanged
 2 {
 3     private string title;
 4 
 5     public string Title
 6     {
 7         get => title;
 8         set
 9         {
10             title = value;
11             RaiseChanged();
12         }
13     }
14 
15     private string author;
16     public string Author
17     {
18         get => author;
19         set
20         {
21             author = value;
22             RaiseChanged();
23         }
24     }
25 
26     private string publish;
27 
28     public string Publish
29     {
30         get => publish;
31         set
32         {
33             publish = value;
34             RaiseChanged();
35         }
36     }
37 
38     private string price;
39 
40     public string Price
41     {
42         get => price;
43         set
44         {
45             price = value;
46             RaiseChanged();
47         }
48     }
49 
50     private string date;
51     public string Date
52     {
53         get => date;
54         set
55         {
56             date = value;
57             RaiseChanged();
58         }
59     }
60 
61     private string description;
62 
63     public string Descrption
64     {
65         get => description;
66         set
67         {
68             description = value;
69             RaiseChanged();
70         }
71     }
72 
73 
74     private string coverImageUrl;
75 
76     public string CoverImageUrl
77     {
78         get => coverImageUrl;
79         set
80         {
81             coverImageUrl = value;
82             RaiseChanged();
83         }
84     }
85 
86 
87     public event PropertyChangedEventHandler PropertyChanged;
88 
89 
90     public void RaiseChanged([CallerMemberName] string memberName = "")
91     {
92         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
93     }
94 }

定义ViewModel

MainWindowViewModel.cs

ViewModel里,定义一个Book对象,并进行数据初始化。

View上的元素分别绑定到了该对象的各个属性。

 1   public class MainWindowViewModel : INotifyPropertyChanged
 2   {
 3       private Book book;
 4 
 5       public Book Book 
 6       {
 7           get => book;
 8           set
 9           {
10               book = value;
11               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Book"));
12           }
13       }
14 
15       public MainWindowViewModel()
16       {
17           LoadBook();
18       }
19 
20       public event PropertyChangedEventHandler PropertyChanged;
21 
22       private void LoadBook()
23       {
24           Book = new Book();
25           Book.CoverImageUrl = "http://img3m1.ddimg.cn/16/5/29681701-1_w_1709623057.jpg";
26           Book.Title = "小学生C++创意编程(视频教学版)";
27           Book.Descrption = "本书让入门C++变得轻松易懂,逐步入学。学习一步一个台阶,让孩子不会被其中的难度而吓退。";
28           Book.Author = "刘凤飞";
29           Book.Publish = "清华大学出版社";
30           Book.Date = "2024年01月 ";
31           Book.Price = "74.00";
32       }
33   }

设置DataContext

1     public partial class MainWindow : TianXiaTech.BlurWindow
2     {
3         public MainWindow()
4         {
5             InitializeComponent();
6 
7             this.DataContext = new MainWindowViewModel();
8         }
9     }

运行效果如下:

带列表功能的示例

接下来我们将上面的示例进行升级,增加列表功能,并支持列表排序

首先我们升级一下界面,将书籍封面,移动到右上角,左侧增加一个ListBox。

其中ListBox使用数据模板定义了数据呈现方式。

 1 <Window x:Class="_2_ShowBookList.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:_2_ShowBookList"
 7         mc:Ignorable="d"
 8         Title="MainWindow" Height="450" Width="800">
 9     <Grid>
10         <Grid.ColumnDefinitions>
11             <ColumnDefinition Width="300"/>
12             <ColumnDefinition/>
13         </Grid.ColumnDefinitions>
14 
15         <Grid>
16             <Grid.RowDefinitions>
17                 <RowDefinition/>
18                 <RowDefinition Height="45"/>
19             </Grid.RowDefinitions>
20 
21             <ListBox ItemsSource="{Binding BookList}" SelectedItem="{Binding Book}" Margin="5">
22                 <ListBox.ItemTemplate>
23                     <DataTemplate>
24                         <Grid>
25                             <Grid.RowDefinitions>
26                                 <RowDefinition/>
27                                 <RowDefinition/>
28                             </Grid.RowDefinitions>
29 
30                             <!--使用数据模板,让书名支持换行-->
31                             <TextBlock Text="{Binding Title}" FontWeight="Bold" TextWrapping="WrapWithOverflow" Width="260"></TextBlock>
32                             <Grid Grid.Row="1" Margin="0,5,0,0">
33                                 <Grid.ColumnDefinitions>
34                                     <ColumnDefinition/>
35                                     <ColumnDefinition/>
36                                 </Grid.ColumnDefinitions>
37 
38                                 <Label Content="{Binding Author}"></Label>
39                                 <TextBlock Text="{Binding Price,StringFormat={}{0}元}" HorizontalAlignment="Left"  Grid.Column="1"></TextBlock>
40                             </Grid>
41                         </Grid>
42                     </DataTemplate>
43                 </ListBox.ItemTemplate>
44             </ListBox>
45 
46             <DockPanel LastChildFill="False" Grid.Row="1">
47                 <Button Content="价格升序" VerticalAlignment="Center" Margin="5,0,0,0" Command="{Binding OrderByPriceAscCommand}"></Button>
48                 <Button Content="价格降序" VerticalAlignment="Center" Margin="5,0,0,0" Command="{Binding OrderByPriceDescCommand}"></Button>
49             </DockPanel>
50         </Grid>
51 
52         <StackPanel Grid.Column="1">
53             <Image Source="{Binding Book.CoverImageUrl}" Stretch="Uniform" Margin="20" Height="150"></Image>
54 
55             <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Book.Title}" FontWeight="Bold" Margin="5"></TextBlock>
56             <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Book.Descrption}" Foreground="Silver" Margin="5"></TextBlock>
57             <Separator Height="1" Foreground="Silver" Margin="2"></Separator>
58             <StackPanel Orientation="Horizontal" Margin="5">
59                 <Label Content="作者:"></Label>
60                 <Label Content="{Binding Book.Author}" Foreground="Silver"  ></Label>
61             </StackPanel>
62             <StackPanel Orientation="Horizontal" Margin="5">
63                 <Label Content="出版社:"></Label>
64                 <Label Content="{Binding Book.Publish}" Foreground="Silver"  ></Label>
65             </StackPanel>
66             <StackPanel Orientation="Horizontal" Margin="5">
67                 <Label Content="价格 ¥: " Foreground="Red" FontWeight="Bold"></Label>
68                 <Label Content="{Binding Book.Price}" Foreground="Silver"  ></Label>
69             </StackPanel>
70             <StackPanel Orientation="Horizontal" Margin="5">
71                 <Label Content="出版日期:"></Label>
72                 <Label Content="{Binding Book.Date}" Foreground="Silver"  ></Label>
73             </StackPanel>
74         </StackPanel>
75     </Grid>
76 
77 </Window>

此外,我们还要注意一下界面绑定:

ListBox的数据源绑定到BookList集合,SelectedItem绑定到Book对象

1 ListBox ItemsSource="{Binding BookList}" SelectedItem="{Binding Book}"

另外还增加了排序按钮,分别绑定到两个不同的Command

1 <Button Content="价格升序" VerticalAlignment="Center" Margin="5,0,0,0" Command="{Binding OrderByPriceAscCommand}"></Button>
2 <Button Content="价格降序" VerticalAlignment="Center" Margin="5,0,0,0" Command="{Binding OrderByPriceDescCommand}"></Button>

然后我们在前面的基础上升级一下ViewModel

 1  public class MainWindowViewModel : INotifyPropertyChanged
 2  {
 3      private Book book;
 4 
 5      public Book Book
 6      {
 7          get => book;
 8          set
 9          {
10              book = value;
11              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Book"));
12          }
13      }
14 
15      private ObservableCollection<Book> bookList = new ObservableCollection<Book>();
16 
17      public ObservableCollection<Book> BookList
18      {
19          get => bookList;
20          set
21          {
22              bookList = value;
23              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BookList"));
24          }
25      }
26 
27      public ICommand OrderByPriceAscCommand { get; private set; }
28 
29      public ICommand OrderByPriceDescCommand { get; private set; }
30 
31 
32      public MainWindowViewModel()
33      {
34          OrderByPriceAscCommand = new RelayCommand(OrderByPriceAsc);
35          OrderByPriceDescCommand = new RelayCommand(OrderByPriceDesc);
36 
37          LoadBook();
38      }
39 
40      private void OrderByPriceDesc()
41      {
42          BookList = new ObservableCollection<Book>(BookList.OrderByDescending(x => x.Price));
43      }
44 
45      private void OrderByPriceAsc()
46      {
47          BookList = new ObservableCollection<Book>(BookList.OrderBy(x => x.Price));
48      }
49 
50      public event PropertyChangedEventHandler PropertyChanged;
51 
52      private void LoadBook()
53      {
54          Book book = new Book();
55          book.CoverImageUrl = "http://img3m1.ddimg.cn/16/5/29681701-1_w_1709623057.jpg";
56          book.Title = "小学生C++创意编程(视频教学版)";
57          book.Descrption = "本书让入门C++变得轻松易懂,逐步入学。学习一步一个台阶,让孩子不会被其中的难度而吓退。";
58          book.Author = "刘凤飞";
59          book.Publish = "清华大学出版社";
60          book.Date = "2024年01月 ";
61          book.Price = 74.00f;
62 
63          Book book2 = new Book();
64          book2.CoverImageUrl = "http://img3m4.ddimg.cn/64/13/29798074-1_u_1731275892.jpg";
65          book2.Title = "漫画趣读西游记(7-14岁)和大人一起读四大名著儿童文学,十万个为什么中小学课外阅读快乐读书吧";
66          book2.Descrption = "拼音标注、无障阅读、名著导读、有声伴读、Q版漫画、全彩印刷,鲜活的人物形象,激发兴趣,提升孩子的学习力!";
67          book2.Author = "陈春燕";
68          book2.Publish = "四川美术出版社";
69          book2.Date = "2024年09月";
70          book2.Price = 4.89f;
71 
72          BookList.Add(book);
73          BookList.Add(book2);
74      }
75  }

运行效果如下:

带新增功能的示例

我们在前面列表的基础上,再进行升级,支持从界面新增书籍。

 1   <Grid>
 2       <Grid.RowDefinitions>
 3           <RowDefinition/>
 4           <RowDefinition Height="35"/>
 5       </Grid.RowDefinitions>
 6 
 7       <Grid>
 8           <Grid.ColumnDefinitions>
 9               <ColumnDefinition/>
10               <ColumnDefinition/>
11           </Grid.ColumnDefinitions>
12 
13           <Image Stretch="Uniform" Margin="0,0,0,30" Source="{Binding NewBook.CoverImageUrl}"></Image>
14           <Button Content="浏览封面" HorizontalAlignment="Center" VerticalAlignment="Bottom" Command="{Binding BrowseCoverCommand}"></Button>
15 
16           <StackPanel Grid.Column="1">
17               <StackPanel Orientation="Horizontal" Margin="10">
18                   <Label Content="书名:"></Label>
19                   <TextBox Text="{Binding NewBook.Title}" Foreground="Silver" Width="200" ></TextBox>
20               </StackPanel>
21               <StackPanel Orientation="Horizontal" Margin="10">
22                   <Label Content="描述:"></Label>
23                   <TextBox Text="{Binding NewBook.Descrption}" Foreground="Silver" Width="200" ></TextBox>
24               </StackPanel>
25               <StackPanel Orientation="Horizontal" Margin="10">
26                   <Label Content="作者:"></Label>
27                   <TextBox Text="{Binding NewBook.Author}" Foreground="Silver" Width="200" ></TextBox>
28               </StackPanel>
29               <StackPanel Orientation="Horizontal" Margin="10">
30                   <Label Content="出版社:"></Label>
31                   <TextBox Text="{Binding NewBook.Publish}" Foreground="Silver"  Width="200"></TextBox>
32               </StackPanel>
33               <StackPanel Orientation="Horizontal" Margin="10">
34                   <Label Content="价格 ¥: " Foreground="Red" FontWeight="Bold"></Label>
35                   <TextBox Text="{Binding NewBook.Price}" Foreground="Silver"  Width="200"></TextBox>
36               </StackPanel>
37               <StackPanel Orientation="Horizontal" Margin="10">
38                   <Label Content="出版日期:"></Label>
39                   <TextBox Text="{Binding NewBook.Date}" Foreground="Silver" Width="200"></TextBox>
40               </StackPanel>
41           </StackPanel>
42       </Grid>
43 
44       <Button Content="新增" Width="88" Height="28" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Command="{Binding AddBookCommand}"/>
45   </Grid>

需要注意的是,这里我们将增加书籍的信息绑定到了一个NewBook对象。

此外,我们还定义了BrowseCoverCommand和AddBookCommand,分别用于浏览书籍封面和新增加书籍

然后我们更新ViewModel

这里为了防止混淆,将前面示例部分的代码移除了,完整的代码可以参考文末的示例代码

 1 public class MainWindowViewModel
 2 {
 3     ... 
 4 
 5     private Book newBook = new Book();
 6 
 7     public Book NewBook
 8     {
 9         get => newBook;
10         set
11         {
12             newBook = value;
13             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NewBook"));
14         }
15     }
16 
17     public ICommand BrowseCoverCommand { get; private set; }
18 
19     public ICommand AddBookCommand { get; private set; }
20 
21 
22     public event PropertyChangedEventHandler? PropertyChanged;
23 
24     public MainWindowViewModel()
25     {
26         BrowseCoverCommand = new RelayCommand(BrowseCover);
27         AddBookCommand = new RelayCommand(AddBook);
28      
29         LoadDemoData();   
30     }
31 
32     private void LoadDemoData()
33     {
34         Book book = new Book();
35         book.CoverImageUrl = "http://img3m1.ddimg.cn/16/5/29681701-1_w_1709623057.jpg";
36         book.Title = "小学生C++创意编程(视频教学版)";
37         book.Descrption = "本书让入门C++变得轻松易懂,逐步入学。学习一步一个台阶,让孩子不会被其中的难度而吓退。";
38         book.Author = "刘凤飞";
39         book.Publish = "清华大学出版社";
40         book.Date = "2024年01月 ";
41         book.Price = "74.00";
42 
43         Book book2 = new Book();
44         book2.CoverImageUrl = "http://img3m4.ddimg.cn/64/13/29798074-1_u_1731275892.jpg";
45         book2.Title = "漫画趣读西游记(7-14岁)和大人一起读四大名著儿童文学,十万个为什么中小学课外阅读快乐读书吧";
46         book2.Descrption = "拼音标注、无障阅读、名著导读、有声伴读、Q版漫画、全彩印刷,鲜活的人物形象,激发兴趣,提升孩子的学习力!";
47         book2.Author = "陈春燕";
48         book2.Publish = "四川美术出版社";
49         book2.Date = "2024年09月";
50         book2.Price = "4.89";
51 
52         BookList.Add(book);
53         BookList.Add(book2);
54     }
55 
56     private void AddBook()
57     {
58         var tempBook = new Book();
59         tempBook.Title = NewBook.Title;
60         tempBook.Descrption = NewBook.Descrption;
61         tempBook.Author = NewBook.Author;
62         tempBook.Publish = NewBook.Publish;
63         tempBook.Price = NewBook.Price;
64         tempBook.Date = NewBook.Date;
65         tempBook.CoverImageUrl = NewBook.CoverImageUrl;
66 
67         //将NewBook添加到列表中
68         BookList.Add(tempBook);
69 
70         //重置NewBook
71         NewBook.Title = "";
72         NewBook.Descrption = "";
73         NewBook.Author = "";
74         NewBook.Publish = "";
75         NewBook.Price = "";
76         NewBook.Date = "";
77         NewBook.CoverImageUrl = null;
78     }
79 
80     private void BrowseCover()
81     {
82         Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
83         openFileDialog.Filter = "图片文件|*.jpg;*.bmp;*.png";
84 
85         if (openFileDialog.ShowDialog() == true)
86         {
87             NewBook.CoverImageUrl = openFileDialog.FileName;
88         }
89     }
90     
91     ...
92 }

运行效果如下:

示例代码

https://github.com/zhaotianff/WPF-MVVM-Beginner/tree/main/6_ViewModelDemo


网站公告

今日签到

点亮在社区的每一天
去签到