• WPF数据编辑的提交与撤销


    当为一个集合(通常绑定在DataGrid或其它ItemsControl控件)添加或编辑一个项时,通常会弹出一个编辑界面编辑项的属性,编辑结束再提交,或者我们不想编辑数据了,此时选择取消,数据项的内容没有任何改变。

    在将数据项绑定到编辑界面时,我们可以定义绑定源更新的触发方式,如下代码所示,将TextBox的Text属性的绑定设置为 UpdateSourceTrigger="Explicit",此时需要手动触发数据源的更新。

       <TextBox.Text>
            <Binding Path="Age" UpdateSourceTrigger="Explicit" >
                <Binding.ValidationRules>
                    <validateRule:AgeRangeRule Min="21" Max="130" ValidationStep="ConvertedProposedValue"/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>

    但上述方法的缺点是不会触发验证,这个缺点很要命,我们通常需要数据验证来可视化反馈输入错误,并提示用户输入规范的内容。

    官方提供的解决方案是,使用BindingGroup及让数据类实现IEditableObject接口,实现该接口的方法以提供数据的提交、撤销和结束。下面的是一个数据类的定义,实际的数据由Empolyee类存储,它定义在EmployeeEditAgent的内部实现,EmployeeEditAgent相当于Empolyee的代理,它实现了IEditableObject接口,EmployeeEditAgent中定义了当前数据currentEmployee 和备份数据copyEmployee ,当调用BeginEdit方法时,currentEmployee复制给currentEmployee,在调用CancelEdit方法,currentEmployee 复制给currentEmployee,这里的复制是深层拷贝,通过备份和恢复的方式实现数据的撤销编辑。

        public class EmployeeEditAgent : INotifyPropertyChanged, IEditableObject
        {
        
            [Serializable]
            class Employee
            {
                internal string Name { get; set; }
                internal int Age { get; set; }
                internal float Salary { get; set; }
            }
            private Employee copyEmployee = null;
            private Employee currentEmployee = new Employee();
    
            public string Name
            {
                get { return currentEmployee.Name; }
                set
                {
                    if (currentEmployee.Name != value)
                    {
                        currentEmployee.Name = value;
                        RaisePropertyChanged("Name");
                    }
                }
            }
    
            public int Age
            {
                get { return currentEmployee.Age; }
                set
                {
                    if (currentEmployee.Age != value)
                    {
                        currentEmployee.Age = value;
                        RaisePropertyChanged("Age");
                    }
                }
            }
    
            public float Salary
            {
                get { return currentEmployee.Salary; }
                set
                {
                    if (currentEmployee.Salary != value)
                    {
                        currentEmployee.Salary = value;
                        RaisePropertyChanged("Salary");
                    }
                }
            }
    
            #region Implementation of INotifyPropertyChanged
    
            public event PropertyChangedEventHandler PropertyChanged = delegate { };
    
            public void RaisePropertyChanged(string propertyname)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
            #endregion
    
            #region Implementation of IEditableObject
    
            public void BeginEdit()
            {
                copyEmployee = DeepColone(currentEmployee);
            }
    
            public void EndEdit()
            {
                copyEmployee = null;
            }
    
            public void CancelEdit()
            {
                currentEmployee = DeepColone(copyEmployee);
                RaisePropertyChanged("");
            }
    
            #endregion
    
            private T  DeepColone<T>(T t)
            {
                MemoryStream stream = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, t);
                stream.Position = 0;
                return (T)formatter.Deserialize(stream);
            }
        }
    View Code

    定义了数据类,还要了解BindingGroup类,BindingGroup可以同时更新多个绑定源,如果某个绑定验证不通过,则提交失败。调用BindingGroup的方法会相应调用绑定源实现的IEditableObject接口的方法:

    BindingGroup IEditableObject  
    BeginEdit BeginEdit 开始编辑
    CommitEdit EndEdit 提交编辑
    CancelEdit CancelEdit 取消编辑

    FrameworkElementFrameworkContentElement 都有 BindingGroup 属性,对于上面的定义个数据类,通常我们将其三个属性分别绑定到三个TextBox上,而三个控件通常位于同一个容器(StackPanel或Grid设置Window)内,此时将容器的DataContext设置为数据源,在容器上创建BindingGroup,此时三个TextBox会继承容器的BindingGroup,即当我们为任意一个TextBox设置绑定时,该绑定会添加到容器的BindingGroup中,这样便实现绑定的同时提交,下面是XAML的实现,注意代码注释说明:

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:validateRule="clr-namespace:ValidateRule"
            xmlns:wpfApplication2="clr-namespace:WpfApplication2"
            Title="数据验证演示" Height="217" Width="332">
        <Window.Resources>
            <wpfApplication2:Employee Name="MJ" Age="25" Salary="2500" x:Key="employee"></wpfApplication2:Employee>
            <ControlTemplate x:Key="validationTemplate">
                <DockPanel>
                    <AdornedElementPlaceholder/>
                    <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
                </DockPanel>
            </ControlTemplate>
    
            <Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <Window.BindingGroup>
            <BindingGroup ></BindingGroup> <!--此处实例化了Window的BindingGroup属性-->
        </Window.BindingGroup>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="118*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="29"/>
                <RowDefinition Height="110"/>
                <RowDefinition Height="100*"/>
            </Grid.RowDefinitions>
            <StackPanel Name="stackPanel"  Grid.Row="1" Margin="5" Loaded="stackPanel_Loaded"  >
                <!--<StackPanel.BindingGroup>
                    <BindingGroup ></BindingGroup>
                </StackPanel.BindingGroup>-->
                <StackPanel Orientation="Horizontal"  >
                    <Label Height="30">姓名</Label>
                    <TextBox Width="70" Text="{Binding Path=Name}"></TextBox>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                    <Label Height="30">年龄</Label>
                    <TextBox Width="70" Validation.ErrorTemplate="{StaticResource validationTemplate}"
                                        Style="{StaticResource textBoxInError}">
                        <TextBox.Text>
                            <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" >
                                <!--该绑定会添加到Window的BindingGroup-->
                                <Binding.ValidationRules>
                                    <validateRule:AgeRangeRule Min="21" Max="130" ValidationStep="RawProposedValue"/>
                                </Binding.ValidationRules>
                            </Binding>
                        </TextBox.Text>
    
                    </TextBox>
                    <TextBlock TextWrapping="Wrap" Text="{Binding Path=Age}" Width="98"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                    <Label Height="30">工资</Label>
                    <TextBox Width="70" Text="{Binding Path=Salary}"></TextBox>
                </StackPanel>
            </StackPanel>
            <Label Content="员工信息" FontSize="15"/>
            <Button Content="提交" HorizontalAlignment="Left" Height="22" Margin="54,10,0,0" Grid.Row="2" VerticalAlignment="Top" Width="76" Click="Button_Click"/>
            <Button Content="取消" HorizontalAlignment="Left" Height="22" Margin="165,10,0,0" Grid.Row="2" VerticalAlignment="Top" Width="76" Click="Button_Click_1"/>
        </Grid>
    </Window>

    下面是代码的实现,注意BindingGroup三个方法BeginEdit、CommitEdit、CancelEdit的调用:

     public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
               // stackPanel.DataContext = new EmployeeEditAgent() { Name = "Mj", Age = 35, Salary = 2500 };
                this.DataContext = new EmployeeEditAgent() { Name = "Mj", Age = 35, Salary = 2500 };
    
              
            }
    
            private void stackPanel_Loaded(object sender, RoutedEventArgs e)
            {
              //  stackPanel.BindingGroup.BeginEdit();
                this.BindingGroup.BeginEdit();//开始编辑事务
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                //if (stackPanel.BindingGroup.CommitEdit())
                //{
                //    MessageBox.Show("success");
                //}
                //else
                //{
                //    MessageBox.Show("");
                //}
                if (this.BindingGroup.CommitEdit())//提交编辑
                {
                    MessageBox.Show("success");
                }
                else
                {
                    MessageBox.Show("failed");
                }
            }
    
            private void Button_Click_1(object sender, RoutedEventArgs e)
            {
                this.BindingGroup.CancelEdit();//取消编辑
            }
        }
    View Code
  • 相关阅读:
    P2523 [HAOI2011]Problem c
    P2518 [HAOI2010]计数
    P2513 [HAOI2009]逆序对数列
    P2519 [HAOI2011]problem a
    P5020 货币系统
    P2580 于是他错误的点名开始了(Trie)
    P3805 【模板】manacher算法
    基础
    白兔的字符串(hash入门)
    ACM的分类训练题集(转载)
  • 原文地址:https://www.cnblogs.com/guofeiji/p/5518425.html
Copyright © 2020-2023  润新知