使用WPF数据绑定时实现事务性编辑(提交/还原)的好方法

3
我有一个非常标准的需求 - 我需要能够打开一个对话框,在该对话框中用户可以更改数据绑定字段的值,然后选择单击“确定”或“取消”,其中单击“取消”会撤消更改。
我查看了IEditableCollectionView、IEditableObject和BindingGroup,但它们似乎都是用于同时编辑单个项。我的程序提供了一个对象列表中的对象集合,用户从列表中选择一个项目,并使用SelectedItem绑定的TextBox进行编辑。这意味着可以编辑任意数量的项目,包括将它们添加到列表中和从列表中删除它们,并且如果用户按下取消,则需要撤销所有更改。
起初,我只是通过深度复制(序列化)进行对象备份,并在取消时进行恢复,但现在对象必须包含对其他共享对象的引用,使得这种方法存在问题。
在不手动来回复制对象和/或值的情况下,如何最好地处理这种情况?
3个回答

1
在这种情况下,DataTable类将完美地发挥作用。它可以保存更改,返回(逐步),或还原所有更改以及许多其他功能。 DataTable类具有与XML很好配合的嵌套特性。
如果您想要保存到数据库中,请查看EntityFramework

谢谢指点,我看了一下 DataTable,可能会在未来的大型项目中使用它们。但对于这个应用程序来说,它有些过度,因为我还在学习 C# 和 WPF,所以我想保持它小而简单。 - Modus Operandi

0

经过更多的思考,我得出结论,至少对于小规模实现来说,最好的方法是编写一个“按值深度复制”的方法,该方法复制对象字段和属性的值而不替换对象本身(这样即使数据恢复,对编辑后的对象的任何引用也仍然保持完整)。

为此,我编写了以下扩展方法:

public static void CopyDataTo(this Object source, Object target) {
    // Recurse into lists
    if (source is IList) {
        var a = 0;
        foreach (var item in (IList)source) {
            if (a >= ((IList)target).Count) {
                var type = item.GetType();
                var assembly = Assembly.GetAssembly(type);
                var newItem = assembly.CreateInstance(type.FullName);
                ((IList)target).Add(newItem);
            }
            item.CopyDataTo(((IList)target)[a]);
            a++;
        }
        while (a < ((IList)target).Count) {
            ((IList)target).RemoveAt(a);
        }
    }

    // Copy over fields
    foreach (var field in source.GetType().GetFields())
        field.SetValue(target, field.GetValue(source));
    // Copy properties
    foreach (var property in source.GetType().GetProperties().Where(
        property => property.CanWrite && !property.GetMethod.GetParameters().Any()))
    {
        property.SetValue(target, property.GetValue(source));
    }
}

它并不是万能的解决方案:它只适用于相同类型的对象,列表项必须具有无参构造函数,并且没有办法控制递归深度。此外,我还没有机会在任何长期或更复杂的情况下进行测试,但到目前为止,它可以完成它应该做的事情(在对象之间复制值),并可用于简单的备份/恢复场景:

var backup = new TypeOfVariableToEdit();
data.CopyDataTo(backup);
var clickedOK = RunDataEditor(data);
if (!clickedOK)
    backup.CopyDataTo(data);

-1

最好的方法是不要这样做:

如果您需要这些项目,请从数据库或任何数据存储中获取一个新的副本,允许用户进行更改,如果他们按取消,只需放弃更改。如果他们按保存,则将数据保存到存储中,然后刷新现有屏幕或其他内容。


我正在使用DataContractSerializer将对象图存储在XML文件中。一开始,每当对任何对象的任何属性进行编辑时,重新加载整个图形是我的做法,但是一旦数据增长,它可能会变得非常昂贵。因此,我希望避免它,并且只在启动时读取存储(并仅在按下“确定”时保存)。 - Modus Operandi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接