热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在WPF应用程序中利用IEditableObject接口实现可撤销编辑的对象

这是我辅导的一个项目开发中的例子,他们是用WPF做界面开发,在学习了如何使用MVVM来实现界面与逻辑的分离,并且很好的数据更新之后,有一个疑问就是,这种双向的数据更新确实很不错,但如果我们希望用户可以

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

ViewModel:

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();
});
}
}

///
/// 使用命令的机制代替了事件
///

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>

推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文详细介绍了git常用命令及其操作方法,包括查看、添加、提交、删除、找回等操作,以及如何重置修改文件、抛弃工作区修改、将工作文件提交到本地暂存区、从版本库中删除文件等。同时还介绍了如何从暂存区恢复到工作文件、恢复最近一次提交过的状态,以及如何合并多个操作等。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • Iamtryingtocreateanarrayofstructinstanceslikethis:我试图创建一个这样的struct实例数组:letinstallers: ... [详细]
  • python3 nmap函数简介及使用方法
    本文介绍了python3 nmap函数的简介及使用方法,python-nmap是一个使用nmap进行端口扫描的python库,它可以生成nmap扫描报告,并帮助系统管理员进行自动化扫描任务和生成报告。同时,它也支持nmap脚本输出。文章详细介绍了python-nmap的几个py文件的功能和用途,包括__init__.py、nmap.py和test.py。__init__.py主要导入基本信息,nmap.py用于调用nmap的功能进行扫描,test.py用于测试是否可以利用nmap的扫描功能。 ... [详细]
author-avatar
問露旧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有