一、案例1(.Net Framework)
1.MainWindow.xaml中
<Window
<!--添加-->
xmlns:vm="clr-namespace:INotifyPropertyChanged和ICommand.ViewModel"
/>
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel HorizontalAlignment="Left">
<TextBlock Text="名称:" />
<TextBox Text="{Binding MyName}"/>
<TextBlock Text="{Binding MyName}"/>
<TextBlock Text="年龄:"/>
<TextBox Text="{Binding Age}"/>
<TextBlock Text="{Binding Age}"/>
<Button
Width="100"
Height="30"
Content="更改"
Command="{Binding SaveCommand}"/>
<!-- 把业务逻辑从.cs中转换到VM中, Binding一个命令,相当于传统的事件 -->
<!-- Command属性类型是ICommand,所以SaveCommand必须是ICommand类型 -->
</StackPanel>
</Grid>
2.ViewModel文件夹中
1)MainWindowViewModel
public class MainWindowViewModel:ViewModelBase
{
public List<Student> Students { get; set; }
private string myName = "张三";
public string MyName
{
// get获取,主要负责把Model中的数据,给视图渲染。
get { return myName; }
// set设置,用户在视图上搜集新数据,去更新Model,Model更新之后,会重新通知视图重新渲染
set { myName = value; OnPropertyChanged(); }
}
private int age = 20;
public int Age
{
get { return age; }
set {
age = value;
OnPropertyChanged(); // 数据变化,通知视图去重新渲染
}
}
//构造函数中建立 SaveCommand和Save 的联系,并不是让Save去执行的,所以RelayCommand参数不能使用Save()
//用户点击XAML中的按钮的时候才执行Save
public MainWindowViewModel()
{
SaveCommand = new RelayCommand(Save);
}
// 为什么SaveCommand没有set?因为直接在VM构造函数中给SaveCommand赋值了,其他位置不需要给SaveCommand赋值,不需要set
public ICommand SaveCommand { get;}
// 不需要对外公开,因为XAML中不直接调用Save(),而是调用SaveCommand
private void Save()
{
MessageBox.Show("更改成功!");
}
}
2)ViewModelBase
// ViewModelBase所有ViewModel的基类,此基类实现了INotifyPropertyChanged接口,将来每个ViewModel继承此基类,就实现了INotifyPropertyChanged接口
public class ViewModelBase:INotifyPropertyChanged
{
// 事件是委托实例 PropertyChangedEventHandler委托, 主要负责通知
public event PropertyChangedEventHandler PropertyChanged;
// propertyName属性名,表示是哪个属性发生了变化
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
3)RelayCommand
public class RelayCommand:ICommand
{
public RelayCommand(Action action)
{
CanExecuteChanged += (sender, e) => { action(); };
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, null);
}
}
二、案例2(.Net Core)
1.App.config 配置字符串,连接数据库
<configuration>
<connectionStrings>
<add name="connString" connectionString="server=.;database=db_INPC;uid=sa;pwd=123456;" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
2.MainWindow.xaml 主窗体
<!--<Window>标签中添加-->
xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
d:DataContext="{d:DesignInstance vm:MainViewModel}"
WindowStartupLocation="CenterScreen"
WindowStyle="SingleBorderWindow"
<!--上下文-->
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<!--渐变色背景-->
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#9db2dc" Offset="0"/>
<GradientStop Color="#0e2d6c" Offset="1"/>
</LinearGradientBrush>
</Window.Background>
<Window.Resources>
<Style x:Key="VerticalAlignmentRowStyle" TargetType="DataGridRow">
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition/>
</Grid.RowDefinitions>
<WrapPanel VerticalAlignment="Center">
<TextBlock
VerticalAlignment="Center"
Text=" 用户名:"/>
<TextBox
Width="100"
Height="30"
Text="{Binding UserName}"
VerticalContentAlignment="Center"
Style="{StaticResource DefaultTextBox}"/>
<Button
Background="CornflowerBlue"
Content="查询"
Command="{Binding SerachCommand}"
Style="{StaticResource TwoButton}"/>
<Button
Background="CornflowerBlue"
Content="添加"
Command="{Binding OpenCommand}"
Style="{StaticResource TwoButton}"/>
</WrapPanel>
<DataGrid
Grid.Row="1"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding Users}"
Background="Transparent"
GridLinesVisibility="None"
RowHeight="30"
RowHeaderWidth="30"
>
<!--定义表头样式-->
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Height" Value="40"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn
Width="100"
Header="编号"
Binding="{Binding Id}"
ElementStyle="{StaticResource DataGridTextBlock}"/>
<DataGridTextColumn
Width="*"
Header="用户名"
Binding="{Binding UserName}"
ElementStyle="{StaticResource DataGridTextBlock}"/>
<DataGridTextColumn
Width="*"
Header="密码"
Binding="{Binding Password}"
ElementStyle="{StaticResource DataGridTextBlock}"/>
<DataGridTemplateColumn Header="操作" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button
Background="CornflowerBlue"
Command="{Binding DataContext.DeleteCommand,RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding RelativeSource={RelativeSource self}}"
Tag="{Binding Id}"
Style="{StaticResource DefaultButton}"
>删除</Button>
<Button
Background="CornflowerBlue"
Command="{Binding DataContext.EditCommand,RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding Id}"
Style="{StaticResource DefaultButton}"
>编辑</Button>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
3.App.xaml 全局资源
<Application.Resources>
<!--单元格内文本样式-->
<Style x:Key="DataGridTextBlock" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Padding" Value="10,0,0,0"/>
</Style>
<!--TextBox标签的样式-->
<Style x:Key="DefaultTextBox" TargetType="TextBox">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="15"/>
</Style>
</Style.Resources>
</Style>
<!--查询,添加按钮样式-->
<Style x:Key="TwoButton" TargetType="Button">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="15"/>
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Margin" Value="15"/>
</Style>
</Style.Resources>
</Style>
<!--Button标签的样式-->
<Style x:Key="DefaultButton" TargetType="Button">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="Width" Value="70"/>
<Setter Property="Height" Value="20"/>
</Style>
</Style.Resources>
</Style>
</Application.Resources>
4.Model模型文件夹 User
public class User
{
public int Id { get; set; }
public string UserName { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
5.ViewModel 视图模型文件夹
1)MainViewModel
public class MainViewModel : ViewModelBase
{
UserService userService = new UserService();
public MainViewModel()
{
Users = userService.GetUsers();
OpenCommand = new RelayCommand(Open);
SerachCommand = new RelayCommand(Serach);
DeleteCommand = new RelayCommand(Delete);
EditCommand = new RelayCommand(Edit);
}
private List<User> users = new List<User>();
public List<User> Users
{
get { return users; }
set { users = value; OnPropertyChanged(); }
}
private string userName;
public string UserName
{
get { return userName; }
set { userName = value; OnPropertyChanged(); }
}
public ICommand OpenCommand { get; }
private void Open(object? obj = null)
{
// 毛病:VM对某个Window窗体强依赖
UserAdd win = new UserAdd();
if(win.ShowDialog() == true)
{
Users = userService.GetUsers();
}
}
public ICommand SerachCommand { get; }
private void Serach(object? obj = null)
{
string strWhere = "";
if (!string.IsNullOrWhiteSpace(UserName))
{
strWhere = $" and UserName like '%{UserName}%' ";
}
Users = userService.GetUsers(strWhere);
}
public ICommand DeleteCommand { get; }
private void Delete(object obj)
{
MessageBoxResult mbr = MessageBox.Show("确定要删除吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if(mbr == MessageBoxResult.Yes)
{
Button btnDelete = (Button)obj;
int id = (int)btnDelete.Tag;
if(userService.Delete(id))
{
Users = userService.GetUsers();
}
}
}
public ICommand EditCommand { get; }
private void Edit(object obj)
{
int id = (int)obj;
UserEditViewModel vm = new UserEditViewModel(id);
UserEdit win = new UserEdit();
win.DataContext = vm; // 给窗体绑定上下文
if (win.ShowDialog() == true)
{
Users = userService.GetUsers();
}
}
}
2)ViewModelBase 视图模型基类
实现INotifyPropertyChanged接口,以便在属性变化时通知View:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string? propertyName = null)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
3)RelayCommand
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler? CanExecuteChanged;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
CanExecuteChanged = null;
}
public bool CanExecute(object? parameter)
{
return _canExecute == null || (parameter != null ? _canExecute(parameter) : true);
}
public void Execute(object? parameter)
{
if (_execute != null)
_execute(parameter != null ? parameter : "");
else
if (CanExecuteChanged != null) CanExecuteChanged(null, new EventArgs());
}
}
6.Service文件夹 实现增删查改逻辑
1)UserService
public class UserService : IUserService
{
string connString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
public bool Add(User user)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();// 打开连接
var sql = "insert into [User] (UserName,[Password]) values(@UserName,@Password)"; // SQL 插入语句
int row = conn.Execute(sql, user);// 执行 SQL 语句
return row > 0;
}
}
public bool Delete(int id)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
var sql = "delete form [User] where Id=@Id";
int row = conn.Execute(sql, id);
return row > 0;
}
}
public bool Update(User user)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
var sql = "update [User] set UserName=@UserName,[Password]=@Password where Id=@Id";
int row = conn.Execute(sql, user);
return row > 0;
}
}
public List<User> GetUsers(string? strWhere="")
{
using(SqlConnection conn = new SqlConnection(connString))
{
conn.Open ();
var sql = "select * from [User] where 1=1 ";
if (!string.IsNullOrWhiteSpace(strWhere)) sql += strWhere;
var uaers = conn.Query<User>(sql).ToList();// 执行查询并转换为对象列表
return uaers;
}
}
}
2)IUserService
public interface IUserService
{
/// <summary>
/// 添加一个用户
/// </summary>
/// <param name="user">user模型实例</param>
/// <returns>是否添加成功</returns>
bool Add(User user);
/// <summary>
/// 修改一个用户
/// </summary>
/// <param name="user">user模型实例</param>
/// <returns>是否修改成功</returns>
bool Update(User user);
/// <summary>
/// 删除一个用户
/// </summary>
/// <param name="user">id主键</param>
/// <returns>是否删除成功</returns>
bool Delete(int id);
/// <summary>
/// 查询用户
/// </summary>
/// <param name="strWhere">查询条件</param>
/// <returns>用户列表</returns>
List<User> GetUsers(string strWhere);
}
7. UserAdd.xaml 添加页面的窗体
<!--<Window>标签中添加-->
xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
Name="UserAddWindow"
<Window.DataContext>
<vm:UserAddViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<WrapPanel>
<Label Content="用户:"/>
<TextBox Width="100" Text="{Binding UserName}"/>
</WrapPanel>
<WrapPanel>
<Label Content="密码:"/>
<TextBox Width="100" Text="{Binding Password}"/>
</WrapPanel>
<WrapPanel>
<Button
Command="{Binding SaveCommand}"
CommandParameter="{Binding ElementName=UserAddWindow}"
Content="添加"/>
</WrapPanel>
</StackPanel>
</Grid>
8.UserEdit.xaml 修改页面的窗体
<!--<Window>标签中添加-->
xmlns:vm="clr-namespace:INotifyPC和ICommand案例.ViewModel"
d:DataContext="{d:DesignInstance vm:UserAddViewModel}"
<Window.DataContext>
<vm:UserEditViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<WrapPanel>
<Label Content="用户名:" />
<TextBox Width="100" Text="{Binding User.UserName}" />
</WrapPanel>
<WrapPanel>
<Label Content="密码:" />
<TextBox Width="100" Text="{Binding User.Password}" />
</WrapPanel>
<WrapPanel>
<Button
Command="{Binding SaveCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Content="保存" />
</WrapPanel>
</StackPanel>
</Grid>
9.ViewModel
1)UserAddViewModel
public class UserAddViewModel : ViewModelBase
{
UserService userService = new UserService();
public UserAddViewModel()
{
SaveCommand = new RelayCommand(Save);
}
private string userName;
public string UserName
{
get { return userName; }
set { userName = value;OnPropertyChanged(); }
}
private string password;
public string Password
{
get { return password; }
set { password = value; OnPropertyChanged(); }
}
public ICommand SaveCommand { get; }
private void Save(object obj)
{
if (string.IsNullOrEmpty(UserName))
{
MessageBox.Show("请填写用户名");
return;
}
if (string.IsNullOrEmpty(Password))
{
MessageBox.Show("请填写密码");
return;
}
User user = new User()
{
UserName = UserName,
Password = Password
};
if(userService.Add(user))
{
var win = (UserAdd)obj;
win.DialogResult = true;
}
}
}
2)UserEditViewModel
public class UserEditViewModel:ViewModelBase
{
UserService userService = new UserService();
public UserEditViewModel(int id)
{
User = userService.GetUsers($" and Id={id}")[0];
SaveCommand = new RelayCommand(Save);
}
public UserEditViewModel(){ }
#region 属性
private User? user;
public User? User
{
get { return user; }
set { user = value; OnPropertyChanged(); }
}
#endregion
#region 事件
public ICommand? SaveCommand { get;}
private void Save(object obj)
{
UserEdit win = (UserEdit)obj;
if(User != null)
{
if (userService.Update(User))
{
win.DialogResult = true;
}
}
}
#endregion
}