程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> WPF數據編輯的提交與撤銷,wpf編輯撤銷

WPF數據編輯的提交與撤銷,wpf編輯撤銷

編輯:C#入門知識

WPF數據編輯的提交與撤銷,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 取消編輯

FrameworkElement 或 FrameworkContentElement 都有 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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved