这是我辅导的一个项目开发中的例子,他们是用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()
{
//结束编辑,这里可以不做任何事情,也可以添加一些额外的逻辑
}
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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那个标签的文本恢复到原来的值
});
}
}
}
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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>

在WPF应用程序中利用IEditableObject接口实现可撤销编辑的对象的更多相关文章

  1. SQL 横转竖 、竖专横 (转载) 使用Dapper.Contrib 开发.net core程序,兼容多种数据库 C# 读取PDF多级书签 Json.net日期格式化设置 ASPNET 下载共享文件 ASPNET 文件批量下载 递归,循环,尾递归 利用IDisposable接口构建包含非托管资源对象 《.NET 进阶指南》读书笔记2------定义不可改变类型

    SQL 横转竖 .竖专横 (转载)   普通行列转换 问题:假设有张学生成绩表(tb)如下: 姓名 课程 分数 张三 语文 74 张三 数学 83 张三 物理 93 李四 语文 74 李四 数学 84 ...

  2. 捕捉WPF应用程序中XAML代码解析异常

    原文:捕捉WPF应用程序中XAML代码解析异常 由于WPF应用程序中XAML代码在很多时候是运行时加载处理的.比如DynamicResource,但是在编译或者运行的过程中,编写的XAML代码很可能有 ...

  3. 在WPF应用程序中使用Font Awesome图标

    Font Awesome 在网站开发中,经常用到.今天介绍如何在WPF应用程序中使用Font Awesome . 如果是自定义的图标字体,使用方法相同. 下载图标字体 在官方网站或github上下载资 ...

  4. 如何在WPF应用程序中使用视频处理控件TVideoGrabber

    要在WPF 中使用 TVideoGrabber 组件,需要像下面的方法来使用 VS.NET(DLL) 版本的组件: ——复制TVideoGrabber_x.x.x.x_x86.dll到c:/windo ...

  5. 利用IDisposable接口构建包含非托管资源对象

    托管资源与非托管资源 在.net中,对象使用的资源分为两种:托管资源与非托管资源.托管资源由CLR进行管理,不需要开发人员去人工进行控制,.NET中托管资源主要指"对象在堆中的内存" ...

  6. 微信小程序中利用时间选择器和js无计算实现定时器(将字符串或秒数转换成倒计时)

    转载注明出处 改成了一个单独的js文件,并修改代码增加了通用性,点击这里查看 今天写小程序,有一个需求就是用户选择时间,然后我这边就要开始倒计时. 因为小程序的限制,所以直接选用时间选择器作为选择定时 ...

  7. 在java程序中利用线程

    package 第十一章; import java.awt.Button; import java.awt.Color; import java.awt.Font; import java.awt.F ...

  8. .NET中利用反射来实现自动映射两个对象中的数据成员

    在以前的项目开发之中,经常会遇到这样一个问题:比如在外面项目的架构设计之中,我们采用MVC和EntityFramework来构建一个Web应用程序.比如我们采用常用的多层架构,例如有Presentat ...

  9. 如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来

    原文:如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来 title: "如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来" publishDate: 2019-06 ...

随机推荐

  1. 浅谈产品测试人员的KPI

                                                                                                   浅谈产品测 ...

  2. 安利eclipse插件之log4E

    敲完代码之后,据说要加注释.加log:OTL~~~~~~~~,在我仰天长叹之际,师父发给我了一个插件压缩包,解压-->拷贝-->重启-->了事.安装方法已经如此之便捷,简直是我辈懒癌 ...

  3. xml note

          10. 两种不同的XML分析模型: DOM:所有数据位于内存 SAX:流程性分析,不必把所有数据Load到内存中,可分析大型的XML文件,常用于Servlet-side的XML-xhtml ...

  4. sshd 错误

    140669086946976:error:0E079065:configuration file routines:DEF_LOAD_BIO:missing equal sign:conf_def. ...

  5. Spring声明式事务管理基于@Transactional注解

    概述:我们已知道Spring声明式事务管理有两种常用的方式,一种是基于tx/aop命名空间的xml配置文件,另一种则是基于@Transactional 注解.         第一种方式我已在上文为大 ...

  6. Java 虚拟机并发编程

    chap 1. 竞争条件:不同的执行得到不同的结果.规避共享可变性(即对共享状态的修改)可避免不必要的竞争条件. chap 2. balance between 一致性.准确性和性能.过犹不及!线程数 ...

  7. Python小爬虫练习

    # coding: utf-8 __author__ = 'zhangcx' from urllib3 import PoolManager import codecs import json cla ...

  8. Remote Desktop Connection Manager (RDCMan)

    当前最新版本是 v2.7. 通过这款软件,我们便可以轻松的管理和访问数个RDP.左边的列表中我们可以创建总的分区列表(即 RDCMan Group),该列表保存采用的是RDG扩展名,使用时通过&quo ...

  9. 打造Ubuntu下Java开发环境

    一.了解JDK 不同的java软件和类库对jdk有不同要求,在了解如何安装Java之前,让我们快速地了解JRE.OpenJDK和Oracle JDK之间的不同之处. JRE(Java Runtime ...

  10. 压力测试工具ab使用

    ab全名是apache bench,是apache自带的一款压力测试工具.它通过创建多个线程来模拟并发,测试目标是基于URL的,因此不论是什么web服务器都可以支持. 使用ab非常简单,进入apach ...