0%

这是我辅导的一个项目开发中的例子,他们是用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 进行下载

using System.ComponentModel;

namespace WpfApplicationBindingSample.Models
{
///


/// 业务实体(Business Entity)
///

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()
    {
        //结束编辑,这里可以不做任何事情,也可以添加一些额外的逻辑
    }
}

}

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using WpfApplicationBindingSample.Models;

namespace WpfApplicationBindingSample.ViewModels
{
///


/// 视图模型:专门用来为界面(视图)来服务的,这里用来包含一些业务逻辑
///

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>