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

简介: 原文:在WPF应用程序中利用IEditableObject接口实现可撤销编辑的对象 这是我辅导的一个项目开发中的例子,他们是用WPF做界面开发,在学习了如何使用MVVM来实现界面与逻辑的分离,并且很好的数据更新之后,有一个疑问就是,这种双向的数据更新确实很不错,但如果我们希望用户可以撤销修改怎么办呢?其实这个功能,很早就有,甚至在原先的Windows Forms里面也可以实现。

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

 

 

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

 

 

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>

 

目录
相关文章
|
29天前
|
C# 开发者 Windows
WPF 应用程序开发:一分钟入门
本文介绍 Windows Presentation Foundation (WPF),这是一种用于构建高质量、可缩放的 Windows 桌面应用程序的框架,支持 XAML 语言,方便 UI 设计与逻辑分离。文章涵盖 WPF 基础概念、代码示例,并深入探讨常见问题及解决方案,包括数据绑定、控件样式与模板、布局管理等方面,帮助开发者高效掌握 WPF 开发技巧。
150 65
|
2月前
|
开发框架 前端开发 JavaScript
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据
|
2月前
|
C# 微服务 Windows
模块化革命:揭秘WPF与微服务架构的完美融合——从单一职责原则到事件聚合器模式,构建高度解耦与可扩展的应用程序
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中借鉴微服务架构思想,实现模块化设计。通过将WPF应用分解为独立的功能模块,并利用事件聚合器实现模块间解耦通信,可以有效提升开发效率和系统可维护性。文中还提供了具体示例代码,展示了如何使用事件聚合器进行模块间通信,以及如何利用依赖注入进一步提高模块解耦程度。此方法不仅有助于简化复杂度,还能使应用更加灵活易扩展。
80 0
|
2月前
|
测试技术 C# 开发者
“代码守护者:详解WPF开发中的单元测试策略与实践——从选择测试框架到编写模拟对象,全方位保障你的应用程序质量”
【8月更文挑战第31天】单元测试是确保软件质量的关键实践,尤其在复杂的WPF应用中更为重要。通过为每个小模块编写独立测试用例,可以验证代码的功能正确性并在早期发现错误。本文将介绍如何在WPF项目中引入单元测试,并通过具体示例演示其实施过程。首先选择合适的测试框架如NUnit或xUnit.net,并利用Moq模拟框架隔离外部依赖。接着,通过一个简单的WPF应用程序示例,展示如何模拟`IUserRepository`接口并验证`MainViewModel`加载用户数据的正确性。这有助于确保代码质量和未来的重构与扩展。
54 0
|
2月前
|
C# 开发者 Windows
震撼发布:全面解析WPF中的打印功能——从基础设置到高级定制,带你一步步实现直接打印文档的完整流程,让你的WPF应用程序瞬间升级,掌握这一技能,轻松应对各种打印需求,彻底告别打印难题!
【8月更文挑战第31天】打印功能在许多WPF应用中不可或缺,尤其在需要生成纸质文档时。WPF提供了强大的打印支持,通过`PrintDialog`等类简化了打印集成。本文将详细介绍如何在WPF应用中实现直接打印文档的功能,并通过具体示例代码展示其实现过程。
197 0
|
2月前
|
C# 开发者 Windows
WPF与PDF文档:解锁创建和编辑PDF文件的新技能——从环境配置到代码实践,手把手教你如何在WPF应用中高效处理PDF,提升文档管理效率
【8月更文挑战第31天】随着数字文档的普及,PDF因跨平台兼容性和高保真度成为重要格式。WPF虽不直接支持PDF处理,但借助第三方库(如iTextSharp)可在WPF应用中实现PDF的创建与编辑。本文通过具体案例和示例代码,详细介绍了如何在WPF中集成PDF库,并展示了从设计用户界面到实现PDF创建与编辑的完整流程。不仅包括创建新文档的基本步骤,还涉及在现有PDF中添加页眉页脚等高级功能。通过这些示例,WPF开发者可以更好地掌握PDF处理技术,提升应用程序的功能性和实用性。
79 0
|
2月前
|
C# 开发者 数据处理
WPF开发者必备秘籍:深度解析数据网格最佳实践,轻松玩转数据展示与编辑大揭秘!
【8月更文挑战第31天】数据网格控件是WPF应用程序中展示和编辑数据的关键组件,提供排序、筛选等功能,显著提升用户体验。本文探讨WPF中数据网格的最佳实践,通过DevExpress DataGrid示例介绍其集成方法,包括添加引用、定义数据模型及XAML配置。通过遵循数据绑定、性能优化、自定义列等最佳实践,可大幅提升数据处理效率和用户体验。
57 0
|
2月前
|
开发者 C# 自然语言处理
WPF开发者必读:掌握多语言应用程序开发秘籍,带你玩转WPF国际化支持!
【8月更文挑战第31天】随着全球化的加速,开发多语言应用程序成为趋势。WPF作为一种强大的图形界面技术,提供了优秀的国际化支持,包括资源文件存储、本地化处理及用户界面元素本地化。本文将介绍WPF国际化的实现方法,通过示例代码展示如何创建和绑定资源文件,并设置应用程序语言环境,帮助开发者轻松实现多语言应用开发,满足不同地区用户的需求。
52 0
|
2月前
|
开发者 C# UED
WPF多窗口应用程序开发秘籍:掌握窗口创建、通信与管理技巧,轻松实现高效多窗口协作!
【8月更文挑战第31天】在WPF应用开发中,多窗口设计能显著提升用户体验与工作效率。本文详述了创建新窗口的多种方法,包括直接实例化`Window`类、利用`Application.Current.MainWindow`及自定义方法。针对窗口间通信,介绍了`Messenger`类、`DataContext`共享及`Application`类的应用。此外,还探讨了布局控件与窗口管理技术,如`StackPanel`与`DockPanel`的使用,并提供了示例代码展示如何结合`Messenger`类实现窗口间的消息传递。总结了多窗口应用的设计要点,为开发者提供了实用指南。
157 0
|
2月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
68 0