原文: 在WPF應用程式中利用IEditableObject接口實作可撤銷編輯的對象
這是我輔導的一個項目開發中的例子,他們是用WPF做界面開發,在學習了如何使用MVVM來實作界面與邏輯的分離,并且很好的資料更新之後,有一個疑問就是,這種雙向的資料更新确實很不錯,但如果我們希望使用者可以撤銷修改怎麼辦呢?其實這個功能,很早就有,甚至在原先的Windows Forms裡面也可以實作。秘密就是實作IEditableObject這個接口。
關于這個接口的官方文檔在這裡:
http://msdn.microsoft.com/zh-cn/library/vstudio/system.componentmodel.ieditableobject.aspx 我做了一個小的例子,幫助大家來了解。該例子使用了MVVM這種設計模式,如果你對此不熟悉,請先參考: http://www.cnblogs.com/chenxizhang/archive/2011/10/01/2197786.html 這個例子,你可以通過 http://files.cnblogs.com/chenxizhang/WpfApplicationBindingSample.zip 進行下載下傳Model:Employee
using System.ComponentModel;
namespace WpfApplicationBindingSample.Models
{
/// <summary>
/// 業務實體(Business Entity)
/// </summary>
class Employee : INotifyPropertyChanged,IEditableObject
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
PropertyChanged(this, new PropertyChangedEventArgs("FullName"));
}
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName != value)
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
PropertyChanged(this, new PropertyChangedEventArgs("FullName"));
}
}
}
}
public string FullName
{
get
{
return FirstName + "," + LastName;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private Employee backup;//用這個字段來儲存一個備份資料
public void BeginEdit()
{
//開始編輯,此時将目前的狀态儲存起來,以便後續可以根據情況送出或者撤銷更改
backup = this.MemberwiseClone() as Employee;//通過克隆的方式直接地複制一份資料
}
public void CancelEdit()
{
//撤銷編輯,此時将對象狀态恢複到備份的狀态
this.FirstName = backup.FirstName;
this.LastName = backup.LastName;
}
public void EndEdit()
{
//結束編輯,這裡可以不做任何事情,也可以添加一些額外的邏輯
}
}
}
ViewModel:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using WpfApplicationBindingSample.Models;
namespace WpfApplicationBindingSample.ViewModels
{
/// <summary>
/// 視圖模型:專門用來為界面(視圖)來服務的,這裡用來包含一些業務邏輯
/// </summary>
class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
CurrentEmployee = new Employee()
{
FirstName = "ares",
LastName = "chen"
};
}
public Employee CurrentEmployee { get; set; }
public RelayCommand EditCommand {
get {
return new RelayCommand(() => {
//将該員工設定為開始編輯
CurrentEmployee.BeginEdit();
});
}
}
/// <summary>
/// 使用指令的機制代替了事件
/// </summary>
public RelayCommand SubmitCommand
{
get
{//使用匿名方法
return new RelayCommand(() =>
{
//結束編輯,讓更改生效
CurrentEmployee.EndEdit();
MessageBox.Show(CurrentEmployee.FullName);
});
}
}
public RelayCommand CancelCommand
{
get
{
return new RelayCommand(() =>
{
CurrentEmployee.CancelEdit();//取消編輯,此時可以看到FullName那個标簽的文本恢複到原來的值
});
}
}
}
}
View:
<Window x:Class="WpfApplicationBindingSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplicationBindingSample.ViewModels"
Title="MainWindow"
Height="350"
Width="525">
<Window.DataContext>
<!--綁定資料上下文-->
<vm:MainWindowViewModel></vm:MainWindowViewModel>
</Window.DataContext>
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin"
Value="3"></Setter>
</Style>
<Style TargetType="TextBox">
<Setter Property="Width"
Value="200"></Setter>
<Setter Property="HorizontalAlignment"
Value="Left"></Setter>
</Style>
<Style TargetType="Button">
<Setter Property="Width"
Value="100"></Setter>
<Setter Property="HorizontalAlignment"
Value="Left"></Setter>
</Style>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock FontSize="30"
Text="編輯員工"></TextBlock>
<TextBlock Text="姓氏"></TextBlock>
<TextBox Text="{Binding CurrentEmployee.FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<!--匈牙利命名法-->
<TextBlock Text="名稱"></TextBlock>
<TextBox Text="{Binding CurrentEmployee.LastName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="全稱"></TextBlock>
<TextBlock Text="{Binding CurrentEmployee.FullName}"></TextBlock>
<Button Content="編輯"
Command="{Binding EditCommand}"></Button>
<Button Content="送出"
Command="{Binding SubmitCommand}"></Button>
<Button Content="取消"
Command="{Binding CancelCommand}"></Button>
</StackPanel>
</Window>