在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()
{
//结束编辑,这里可以不做任何事情,也可以添加一些额外的逻辑
}
}
}
.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接口实现可撤销编辑的对象的更多相关文章
- SQL 横转竖 、竖专横 (转载) 使用Dapper.Contrib 开发.net core程序,兼容多种数据库 C# 读取PDF多级书签 Json.net日期格式化设置 ASPNET 下载共享文件 ASPNET 文件批量下载 递归,循环,尾递归 利用IDisposable接口构建包含非托管资源对象 《.NET 进阶指南》读书笔记2------定义不可改变类型
SQL 横转竖 .竖专横 (转载) 普通行列转换 问题:假设有张学生成绩表(tb)如下: 姓名 课程 分数 张三 语文 74 张三 数学 83 张三 物理 93 李四 语文 74 李四 数学 84 ...
- 捕捉WPF应用程序中XAML代码解析异常
原文:捕捉WPF应用程序中XAML代码解析异常 由于WPF应用程序中XAML代码在很多时候是运行时加载处理的.比如DynamicResource,但是在编译或者运行的过程中,编写的XAML代码很可能有 ...
- 在WPF应用程序中使用Font Awesome图标
Font Awesome 在网站开发中,经常用到.今天介绍如何在WPF应用程序中使用Font Awesome . 如果是自定义的图标字体,使用方法相同. 下载图标字体 在官方网站或github上下载资 ...
- 如何在WPF应用程序中使用视频处理控件TVideoGrabber
要在WPF 中使用 TVideoGrabber 组件,需要像下面的方法来使用 VS.NET(DLL) 版本的组件: ——复制TVideoGrabber_x.x.x.x_x86.dll到c:/windo ...
- 利用IDisposable接口构建包含非托管资源对象
托管资源与非托管资源 在.net中,对象使用的资源分为两种:托管资源与非托管资源.托管资源由CLR进行管理,不需要开发人员去人工进行控制,.NET中托管资源主要指"对象在堆中的内存" ...
- 微信小程序中利用时间选择器和js无计算实现定时器(将字符串或秒数转换成倒计时)
转载注明出处 改成了一个单独的js文件,并修改代码增加了通用性,点击这里查看 今天写小程序,有一个需求就是用户选择时间,然后我这边就要开始倒计时. 因为小程序的限制,所以直接选用时间选择器作为选择定时 ...
- 在java程序中利用线程
package 第十一章; import java.awt.Button; import java.awt.Color; import java.awt.Font; import java.awt.F ...
- .NET中利用反射来实现自动映射两个对象中的数据成员
在以前的项目开发之中,经常会遇到这样一个问题:比如在外面项目的架构设计之中,我们采用MVC和EntityFramework来构建一个Web应用程序.比如我们采用常用的多层架构,例如有Presentat ...
- 如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来
原文:如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来 title: "如何追踪 WPF 程序中当前获得键盘焦点的元素并显示出来" publishDate: 2019-06 ...
随机推荐
- ASP.Net的导出Excel的快速方法,DataTable导出Excel(亲测,非原创)
//使用方法 ExcelHelper.dataTableToCsv(dt,@"D:\1212.xls");System.Diagnostics.Process.Start(@&qu ...
- List<String[]>
1.List<Sttring[]>的用法 List<String[]> list = new ArrayList<String[]>();//声明一个List< ...
- go json null字段的转换
最近试了试go中对json null字段进行转换,代码如下: struct 转 json: package main import ( "encoding/json" " ...
- [Asp.net 开发系列之SignalR篇]专题六:使用SignalR实现消息提醒
一.引言 前面一篇文章我介绍了如何使用SignalR实现图片的传输,然后对于即时通讯应用来说,消息提醒是必不可少的.现在很多网站的都有新消息的提醒功能.自然对于SignalR系列也少不了这个功能的实现 ...
- Web端导出CSV
前端导出文件大部分还是通过服务器端的方式生成文件,然后传递到客户端.但很多情况下当我们导出CSV时并不需要后端参与,甚至没有后端. 做过WebGIS的同学经常会碰到这种场景,用户的兴趣点数据以csv文 ...
- [.net 面向对象编程基础] (4) 基础中的基础——数据类型转换
[.net面向对象编程基础] (4)基础中的基础——数据类型转换 1.为什么要进行数据转换? 首先,为什么要进行数据转换,拿值类型例子说明一下, 比如:我们要把23角零钱,换成2.30元,就需要把整形 ...
- node访问iis使用keep-alive设置不当
iis默认的连接超时时间为2分钟 ,因此node程序使用keep-alive访问时,keep-alive的时间不应该超过2分钟,否则在请求完成后,node端继续保持连接,2分钟后iis断开连接,会导致 ...
- the bundle at bundle path is not signed using an apple submission certificate
在app上架的时候,出现这个错误,也许只是你的Apple Worldwide Developer Relations Certification Authority Intermediate Cert ...
- 两个Fragment之间如何传递数据
FragmentA启动FragmentB,做一些选择操作后,返回FragmentA,需要把FragmentB里面选择的数据传回来.有什么办法? Fragment之间不能直接通信,必须通过Activit ...
- 内存角度探寻C++面向对象 之 继承、多态
一,简单继承: #include <iostream> class TableTennisPlayer { private: int id; public: TableTennisPlay ...