wpf -datagrid增删改的自我进阶过程(进阶偏)

前言:在上一篇中,我们实现了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

你可能感兴趣的:(wpf -datagrid增删改的自我进阶过程(进阶偏))