首页 技术 正文
技术 2022年11月10日
0 收藏 945 点赞 3,511 浏览 6062 个字

当为一个集合(通常绑定在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,这里的复制是深层拷贝,通过备份和恢复的方式实现数据的撤销编辑。

WPF数据编辑的提交与撤销

    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);
}
}

定义了数据类,还要了解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的调用:

WPF数据编辑的提交与撤销

 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();//取消编辑
}
}

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,086
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,561
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,410
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,183
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,820
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,903