WPF的MVVM设计模式(从登录到增删改查Demo)
原文:
https://blog.csdn.net/Dear200892/article/details/101533249
什么是MVVM
MVVM即Model-View-ViewModel,通常适用于WPF或Silverlight开发。
MVC或MVP都是基于面向对象的设计模式,而MVVM是基于组件,数据驱动的设计模式。
View:使用XAML呈现给用户的界面,负责与用户交互,接收用户输入,把数据展现给用户。
Model:事物的抽象,开发过程中涉及到的事物都可以抽象为Model,例如姓名、年龄、性别、地址等属性。不包含方法,也不需要实现INotifyPropertyChanged接口。
ViewModel:负责收集需要绑定的数据和命令,聚合Model对象,通过View类的DataContext属性绑定到View,同时也可以处理一些UI逻辑。
MVVM优点
低耦合:View合一独立于Model变化和修改。一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成XAML代码。
可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
MVVM(Demo)
1.新建WPF项目,名称WpfMVVM。在Model中添加登录类(Login)以及用户类(User):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfMVVM.Model
{
public class Login : ICloneable
{
private string accountNumber;
public string AccountNumber
{
get
{
return accountNumber;
}
set
{
accountNumber = value;
}
}
private string password;
public string Password
{
get
{
return password;
}
set
{
password = value;
}
}
/// <summary>
/// 使数据的复制变为深复制,需要实现ICloneable接口
/// </summary>
/// <returns></returns>
public object Clone()
{
return (Object)this.MemberwiseClone();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfMVVM.Model
{
public class User : ICloneable
{
private int id;
private string name;
private int age;
private string sex;
private string address;
/// <summary>
/// 编号
/// </summary>
public int ID { get { return id; } set { id = value;} }
/// <summary>
/// 姓名
/// </summary>
public string Name { get { return name; } set { name = value;} }
/// <summary>
/// 年龄
/// </summary>
public int Age { get { return age; } set { age = value; } }
/// <summary>
/// 性别
/// </summary>
public string Sex { get { return sex; } set { sex = value; } }
/// <summary>
/// 地址
/// </summary>
public string Address { get { return address; } set { address = value; } }
/// <summary>
/// 使数据的复制变为深复制,需要实现ICloneable接口
/// </summary>
/// <returns></returns>
public object Clone()
{
return (Object)this.MemberwiseClone();
}
}
}
2.在ViewModel中创建命令绑定(DelegateCommands):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfMVVM.ViewModel
{
public class DelegateCommands : ICommand
{
//当命令可执行状态发生改变时,应当被激发
public event EventHandler CanExecuteChanged;
public Action<object> ExecuteCommand = null;
public Func<object, bool> CanExecuteCommand = null;
/// <summary>
/// 用于判断命令是否可以执行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (CanExecuteCommand !=null)
{
return CanExecuteCommand(parameter);
}
else
{
return true;
}
}
/// <summary>
/// 命令执行
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
if (ExecuteCommand !=null)
{
ExecuteCommand(parameter);
}
}
}
}
实现接口ICommand。
3.在ViewModel中进行业务处理(LoginViewModel,UserViewModel):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfMVVM.Model;
using WpfMVVM.View;
namespace WpfMVVM.ViewModel
{
public class LoginViewModel : INotifyPropertyChanged
{
public LoginWindow log;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
private Login landerLogin;
public Login LanderLogin
{
get
{
if (landerLogin == null)
{
landerLogin = new Login();
}
return landerLogin;
}
set
{
if (landerLogin == null)
{
landerLogin = new Login();
}
landerLogin = value;
OnPropertyChanged("LanderLogin");
}
}
public DelegateCommands LoginCommand { get; set; }
public LoginViewModel()
{
LoginCommand = new DelegateCommands();
LoginCommand.ExecuteCommand = new Action<object>(UserLogin);
}
public void UserLogin(object pamer)
{
if (LanderLogin.AccountNumber == "admin" && LanderLogin.Password == "123456")
{
MainWindow win = new MainWindow();
win.ShowDialog();
}
else
{
MsgShow("用户名或密码错误");
}
}
/// <summary>
/// 显示提示信息
/// </summary>
/// <param name="txt"></param>
public void MsgShow(string txt)
{
System.Windows.MessageBox.Show(txt);
//删除后清空选择用户
LanderLogin = null;
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using WpfMVVM.Model;
namespace WpfMVVM.ViewModel
{
public class UserViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
private ObservableCollection<User> userList;
/// <summary>
/// 用户集合
/// </summary>
public ObservableCollection<User> UserList
{
get
{
if (userList == null)
{
userList = new ObservableCollection<User>();
userList = new ShowDataViewModel().GetUsers();
}
return userList;
}
set {
if (userList == null)
{
userList = new ObservableCollection<User>();
}
userList = value;
OnPropertyChanged("UserList");
}
}
public User selectUser;
/// <summary>
/// 被选中的用户
/// </summary>
public User SelectUser
{
get
{
if (selectUser == null)
{
selectUser = new User();
}
return selectUser;
}
set
{
if (selectUser == null)
{
selectUser = new User();
}
selectUser = value; OnPropertyChanged("SelectUser");
}
}
/// <summary>
/// 注册命令
/// </summary>
private void RegisterCommands()
{
AddCommand = new DelegateCommands();
AddCommand.ExecuteCommand = new Action<object>(AddUser);
UpdateCommand = new DelegateCommands();
UpdateCommand.ExecuteCommand = new Action<object>(UpdateUser);
DeleteCommand = new DelegateCommands();
DeleteCommand.ExecuteCommand = new Action<object>(DeleteUser);
SelectionCommand = new DelegateCommands();
SelectionCommand.ExecuteCommand = new Action<object>(SelectionUser);
}
public UserViewModel()
{
RegisterCommands();
}
/// <summary>
/// 显示提示信息
/// </summary>
/// <param name="txt"></param>
public void MsgShow(string txt)
{
System.Windows.MessageBox.Show(txt);
//删除后清空选择用户
SelectUser = null;
}
#region 注册命令
/// <summary>
/// 新增用户
/// </summary>
public DelegateCommands AddCommand { get; set; }
/// <summary>
/// 更新用户
/// </summary>
public DelegateCommands UpdateCommand { get; set; }
/// <summary>
/// 删除用户
/// </summary>
public DelegateCommands DeleteCommand { get; set; }
/// <summary>
/// 选择用户
/// </summary>
public DelegateCommands SelectionCommand { get; set; }
public void AddUser(object parameter)
{
//新增用户的ID为【最小】编号
int id = 0;
for (int i = 0; i < UserList.Count; i++)
{
if (UserList[i].ID != (i + 1))
{
id = (i + 1);
break;
}
}
if (id == 0)
{
id = UserList.Max(a => a.ID) + 1;
}
UserList.Insert((id - 1), new User() { ID = id, Name = SelectUser.Name, Age = SelectUser.Age, Sex = SelectUser.Sex, Address = SelectUser.Address });
MsgShow("用户编号:" + id + "\n新增成功!");
}
public void UpdateUser(object parameter)
{
if (SelectUser.ID < 1)
{
MsgShow("请选择修改项!");
return;
}
//查找需要修改的对象
User user = UserList.Where(a => a.ID == SelectUser.ID).SingleOrDefault();
//查找需要修改的对象索引
int index = UserList.IndexOf(user);
//使数据的复制变为深复制,需要实现ICloneable接口
UserList[index]= (User)SelectUser.Clone();
MsgShow("用户编号:" + SelectUser.ID + "\n修改成功!");
}
public void DeleteUser(object parameter)
{
if (SelectUser.ID < 1)
{
MsgShow("请选择修改项!");
return;
}
User user = UserList.Where(a => a.ID == SelectUser.ID).SingleOrDefault();
UserList.Remove(user);
MsgShow("用户编号:" + SelectUser.ID + "\n删除成功!");
}
public void SelectionUser(object parameter)
{
DataGrid grid = (DataGrid)parameter;
//使数据的复制变为深复制,需要实现ICloneable接口
if (grid.SelectedItem!=null)
{
SelectUser = (User)((User)grid.SelectedItem).Clone();
}
}
#endregion
}
}
注意事项:需要实现接口INotifyPropertyChanged,不然数据更新后界面无法更新。
4,在View中创建登录界面(LoginWindow.xaml) :
<Window x:Class="WpfMVVM.View.LoginWindow"
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:WpfMVVM.View"
mc:Ignorable="d"
Title="Login" Height="300" Width="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30">MVVM设计模式登录</Label>
<Label Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="40,0,0,0" FontSize="18">账号:</Label>
<TextBox Text="{Binding LanderLogin.AccountNumber}" Grid.Row="1" Width="300" Height="30" Margin="0,0,60,0" VerticalContentAlignment="Center"></TextBox>
<Label Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="40,0,0,0" FontSize="18" VerticalContentAlignment="Center">密码:</Label>
<TextBox Text="{Binding LanderLogin.Password}" Grid.Row="2" Width="300" Height="30" Margin="0,0,60,0"></TextBox>
<Button Grid.Row="3" Command="{Binding LoginCommand}">登录</Button>
</Grid>
</Window>
cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Shapes;
using WpfMVVM.ViewModel;
namespace WpfMVVM.View
{
/// <summary>
/// Login.xaml 的交互逻辑
/// </summary>
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
this.DataContext = new LoginViewModel();
}
}
}
5,在View中创建用户界面(MainWindow.xaml) ::
<Window x:Class="WpfMVVM.View.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:WpfMVVM.View"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="450" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="1.5*"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgUser" IsReadOnly="True" AutoGenerateColumns="False" CanUserAddRows="False" HorizontalAlignment="Left" ItemsSource="{Binding UserList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<i:InvokeCommandAction Command="{Binding SelectionCommand}" CommandParameter="{Binding ElementName=dgUser}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}" Header="编号"/>
<DataGridTextColumn Binding="{Binding Name}" Header="姓名" Width="100"/>
<DataGridTextColumn Binding="{Binding Age}" Header="年龄"/>
<DataGridTextColumn Binding="{Binding Sex}" Header="性别" Width="60"/>
<DataGridTextColumn Binding="{Binding Address}" Header="地址" Width="172"/>
</DataGrid.Columns>
</DataGrid>
<GroupBox Header="用户基本信息" Grid.Row="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0" Orientation="Horizontal">
<Label x:Name="lblID" Content="编号:" VerticalAlignment="Center" />
<TextBox x:Name="tbxID" IsEnabled ="False" Text="{Binding SelectUser.ID}" VerticalAlignment="Center" MaxLength="5" Height="22" Width="90"/>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Horizontal">
<Label x:Name="lblName" Content="姓名:" VerticalAlignment="Center" />
<TextBox x:Name="tbxName" Text="{Binding SelectUser.Name}" VerticalAlignment="Center" Height="22" MaxLength="6" Width="90"/>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="0" Orientation="Horizontal">
<Label x:Name="lblAge" Content="年龄:" VerticalAlignment="Center" />
<TextBox x:Name="tbxAge" Text="{Binding SelectUser.Age}" VerticalAlignment="Center" Height="22" MaxLength="4" Width="90"/>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal">
<Label x:Name="lblSex" Content="性别:" VerticalAlignment="Center"/>
<ComboBox Text="{Binding SelectUser.Sex}" SelectedIndex="0" x:Name="cbxSex" VerticalAlignment="Center" Width="90">
<ComboBoxItem Tag="1" Content="男"/>
<ComboBoxItem Tag="2" Content="女"/>
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2" Orientation="Horizontal">
<Label x:Name="lblRemarks" Content="地址:" VerticalAlignment="Center"/>
<TextBox x:Name="tbxRemarks" Text="{Binding SelectUser.Address}" VerticalAlignment="Center" Height="22" Width="235" />
</StackPanel>
</Grid>
</GroupBox>
<GroupBox Header="功能操作" Grid.Row="2">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="btnSave" Command="{Binding AddCommand}" Content="新增" VerticalAlignment="Center" Width="69" Height="19" Margin="10,0,10,0" />
<Button x:Name="btnUpdate" Command="{Binding UpdateCommand}" Content="修改" VerticalAlignment="Center" Width="69" Height="19" Margin="10,0,10,0"/>
<Button x:Name="btnDelete" Command="{Binding DeleteCommand}" Content="删除" VerticalAlignment="Center" Width="69" Height="19" Margin="10,0,10,0"/>
</StackPanel>
</GroupBox>
</Grid>
</Window>
cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Shapes;
using WpfMVVM.ViewModel;
namespace WpfMVVM.View
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new UserViewModel();
}
}
}
6,界面展示