这是我辅导的一个项目开发中的例子,他们是用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. 攒机I7

    CPU : I7 4790K +Z97 = 3200 散热器 :九州风神玄冰400 = 99 硬盘 :希捷 1TB 64M = 310 机箱: 金河田超越白 = 200 内存 DDR3金士顿8G = ...

  2. 【转】【WebDriver】不可编辑域和日历控件域的输入 javascript

    http://blog.csdn.net/fudax/article/details/8089404 今天用到日历控件,用第一个javascript执行后页面上的日期控件后,在html中可以看到生效日 ...

  3. Texture2D.GetPixelBilinear(float u, float v)的使用,官方例子注释

    using UnityEngine; using System.Collections; public class TEST : MonoBehaviour { public Texture2D so ...

  4. Hibernate Id Generator and Primary Key

    Use automate id by hibernate: If you want the tables' id be created automation. How to do it? When u ...

  5. Oracle Profile 使用

    一.目的: Oracle系统中的profile可以用来对用户所能使用的数据库资源进行限制,使用Create Profile命令创建一个Profile,用它来实现对数据库资源的限制使用,如果把该prof ...

  6. 解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译)

    解剖SQLSERVER 第七篇  OrcaMDF 特性概述(译) http://improve.dk/orcamdf-feature-recap/ 时间过得真快,这已经过了大概四个月了自从我最初介绍我 ...

  7. 一个事务复制的bug--更新丢失

    有两种情况会造成更新丢失,第一种是不正确的设置,例如外键或触发器的“Not For Replication” (NFR)属性没有开启.详情请参考http://blogs.msdn.com/b/apgc ...

  8. Quartz.net 定时调度时间配置格式说明与实例

    格式: [秒] [分] [小时] [日] [月] [周] [年] 序号 说明 是否必填 允许填写的值 允许的通配符 1 秒 是 0-59 , - * / 2 分 是 0-59 , - * / 3 小时 ...

  9. C#中的线程一(委托中的异步)

    C#中的线程一(委托中的异步) 一.同步委托 我们平时所用的委托以同步居多,我们编写一个方法和相关委托进行演示: publicdelegatevoid DoSomethingDelegate(stri ...

  10. comet在asp.net中的实现

    网上有关“服务器推送”的介绍非常多,其中一种实现方式就是采用comet技术,在浏览器与服务端之间建立一个http协议的“长连接”,所谓“长连接”,就是指浏览器到服务端的http请求不会马上得到服务端的 ...