前言:在上一篇中,我们实现了datagrid的增删改超小白版本。在这个进阶版本中我会一个一个优化。(让代码看起来不那么low,我真的是故意的,虽然low,但是没那么low啊)。MVVM是一个数据操作很方便的框架。WPF的数据绑定,前端框架vue。使用起来比以前的控件赋值、dom操作方便的多。废话不多说,反正这个是你要掌握的技能点就OK了。
1、咱们先给自己定一个原则,页面上所有的数据源都来自页面本身的DataContext。咱们只准给页面赋值一次DataContext;
public MainWindow()
{
InitializeComponent();
//初始化数据
for(int i = 1; i <= 10; i++) {
Students.Add(new Student()
{
ID = i,
Name = Guid.NewGuid().ToString(),
Sex = (EnumSex)(i%3)
});
}
//页面所有控件最开始的数据来源只有这一个
DataContext = Students;
}
///xaml代码。
<DataGrid ItemsSource="{Binding}" CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False"
CanUserDeleteRows="False" CanUserSortColumns="False" AutoGenerateColumns="True" SelectedCellsChanged="DGstudent_SelectedCellsChanged">
</DataGrid>
2、修改编辑模块,这块功能是要绑定到Datagrid的选择项,嗯,比上面的复杂点。但是也就是复杂一点点。
<!--Datagrid列表模块删除了SelectedCellsChanged事件,完全不需要了,是不是超方便-->
<DataGrid ItemsSource="{Binding}" x:Name="DGstudent" CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False"
CanUserDeleteRows="False" CanUserSortColumns="False" AutoGenerateColumns="True" >
</DataGrid>
<!--修改模块 datacontext绑定DGstudent的选中项是不是超简单-->
<WrapPanel DataContext="{Binding ElementName=DGstudent,Path=SelectedItem}">
<Label>ID:</Label>
<TextBox Width="100" IsReadOnly="True" x:Name='TbModifyID' Text="{Binding ID}"></TextBox>
<Label>姓名:</Label>
<TextBox Width="300" x:Name="TbModifyName" Text="{Binding Name}"></TextBox>
<Label>性别:</Label>
<ComboBox Width="100" x:Name="TbModifySex" Text="{Binding Sex}">
<ComboBoxItem Content="未知" ></ComboBoxItem>
<ComboBoxItem Content="男" ></ComboBoxItem>
<ComboBoxItem Content="女" ></ComboBoxItem>
</ComboBox>
<Button Content="修改" x:Name="BtnModify" Click="BtnModify_Click"></Button>
<Button Content="删除" x:Name="BtnRemove" Click="BtnRemove_Click"></Button>
</WrapPanel>
友情提示:大家可以一步一步运行看效果哦,这样的话理解会身体一点。
3、完善修改功能。那就然都需要优化了,那肯定不能用事件一个一个改,这么low的办法;INotifyPropertyChanged。修改类如下
//ID是不能被修改的,所以不需要通知。
public class Student: INotifyPropertyChanged
{
public int ID { get; set; }
private string _name { get; set; }
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged();
}
}
private EnumSex _sex { get; set; }
public EnumSex Sex
{
get
{
return _sex;
}
set
{
_sex = value;
NotifyPropertyChanged();
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
///xaml 编辑按钮已经不需要了,你会发现修改文本框的值的时候,表格就以及修改了。
<WrapPanel DataContext="{Binding ElementName=DGstudent,Path=SelectedItem}">
<Label>ID:</Label>
<TextBox Width="100" IsReadOnly="True" x:Name='TbModifyID' Text="{Binding ID}"></TextBox>
<Label>姓名:</Label>
<TextBox Width="300" x:Name="TbModifyName" Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Label>性别:</Label>
<ComboBox Width="100" x:Name="TbModifySex" Text="{Binding Sex,UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem Content="未知" ></ComboBoxItem>
<ComboBoxItem Content="男" ></ComboBoxItem>
<ComboBoxItem Content="女" ></ComboBoxItem>
</ComboBox>
<!--<Button Content="修改" x:Name="BtnModify" Click="BtnModify_Click"></Button>-->
<Button Content="删除" x:Name="BtnRemove" Click="BtnRemove_Click"></Button>
</WrapPanel>
4、删除、新增。这个可必须要触发事件的,怎么办呢,虽然现有已经支持,但是要改嘛,就要改的彻底点。那命令能不能也绑定呢,当然可以 ICommand了解下,亲。
public class AddCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
throw new NotImplementedException();
}
}
public class RemoveCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
throw new NotImplementedException();
}
}
5、定义俩个命令,添加和删除。继承ICommand后。就发现问题了,诶?这个我怎么操作页面的数据集合Students呢?咱们可以把students放到全局变量里面(还有其他方式,咱们先用最简单的)。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = GlobalViewModel.Students;
}
}
public static class GlobalViewModel {
public static List<Student> Students { get; set; } = new List<Student>();
static GlobalViewModel() {
//初始化数据
for (int i = 1; i <= 10; i++)
{
Students.Add(new Student()
{
ID = i,
Name = Guid.NewGuid().ToString(),
Sex = (EnumSex)(i % 3)
});
}
}
}
public class AddCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (parameter is Student) {
GlobalViewModel.Students.Add(parameter as Student);
}
}
}
public class RemoveCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
int.TryParse(parameter?.ToString(), out int id);
var student = GlobalViewModel.Students.FirstOrDefault(x => x.ID == id);
if (student !=null)
{
GlobalViewModel.Students.Remove(student);
}
}
}
6、那强迫症的我们肯定表示DataContext = GlobalViewModel.Students;这个代码能不能也在XAML页面绑定呢。那肯定也是可以的(顺带把按钮的命令也绑定上);
<Window x:Class="DataGridDemo.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:DataGridDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" DataContext="{Binding Source={x:Static local:GlobalViewModel.Students}}">
<!--绑定全局静态变量为页面数据源-->
<StackPanel>
<!--Datagrid列表模块-->
<DataGrid ItemsSource="{Binding}" x:Name="DGstudent" CanUserAddRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False"
CanUserDeleteRows="False" CanUserSortColumns="False" AutoGenerateColumns="True" >
</DataGrid>
<!--修改模块-->
<WrapPanel DataContext="{Binding ElementName=DGstudent,Path=SelectedItem}">
<Label>ID:</Label>
<TextBox Width="100" IsReadOnly="True" x:Name='TbModifyID' Text="{Binding ID}"></TextBox>
<Label>姓名:</Label>
<TextBox Width="300" x:Name="TbModifyName" Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Label>性别:</Label>
<ComboBox Width="100" x:Name="TbModifySex" Text="{Binding Sex,UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem Content="未知" ></ComboBoxItem>
<ComboBoxItem Content="男" ></ComboBoxItem>
<ComboBoxItem Content="女" ></ComboBoxItem>
</ComboBox>
<!--删除按钮绑定删除命令-->
<Button Content="删除" x:Name="BtnRemove">
<Button.Command>
<local:RemoveCommand></local:RemoveCommand>
</Button.Command>
</Button>
</WrapPanel>
<!--新增模块-->
<WrapPanel x:Name="WpAdd">
<Label>姓名:</Label>
<TextBox Width="300" x:Name="TbAddName"></TextBox>
<Label>性别:</Label>
<ComboBox Width="100" x:Name="TbAddSex">
<ComboBoxItem Content="未知" ></ComboBoxItem>
<ComboBoxItem Content="男" ></ComboBoxItem>
<ComboBoxItem Content="女" ></ComboBoxItem>
</ComboBox>
<!--新增按钮绑定新增命令-->
<Button Content="新增">
<Button.Command>
<local:AddCommand></local:AddCommand>
</Button.Command>
</Button>
</WrapPanel>
</StackPanel>
</Window>
7、上一步结束后我们运行,可以发现点击按钮后可以触发事件了。但是因为没有设置参数,所以参数都是null的。删除命令我们只需要带上ID就行了,新增我们的话确实要带上姓名和性别俩个参数。就需要借助IMultiValueConverter了。
<!--删除按钮 参数ID-->
<Button Content="删除" x:Name="BtnRemove" CommandParameter="{Binding ID}">
<Button.Command>
<local:RemoveCommand></local:RemoveCommand>
</Button.Command>
</Button>
<!--新增按钮 多个参数,使用转换器转换为对象-->
<Button Content="新增">
<Button.CommandParameter>
<MultiBinding>
<MultiBinding.Converter >
<local:StudentConverter></local:StudentConverter>
</MultiBinding.Converter>
<Binding ElementName="TbAddName" Path="Text"></Binding>
<Binding ElementName="TbAddSex" Path="Text"></Binding>
</MultiBinding>
</Button.CommandParameter>
<Button.Command>
<local:AddCommand></local:AddCommand>
</Button.Command>
</Button>
///转换器,姓名和性别构建一个Student新对象
public class StudentConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string name = values[0]?.ToString();
if (Enum.TryParse(values[1]?.ToString(), out EnumSex sex) && !string.IsNullOrEmpty(name)) {
return new Student()
{
ID = GlobalViewModel.Students.Count + 1,
Name = name,
Sex = sex
};
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
8、新增和删除功能调试时已经实现了,集合已经修改了。但是页面并没有发生改变。那是因为我们的数组类型还是List。我们只需要修改为:
public static ObservableCollection<Student> Students { get; set; } = new ObservableCollection<Student>();
结语:增删改已经差不多很完善了,当然还有许多可以优化的点。
https://download.csdn.net/download/qq_29198233/12062726