WPF:数据绑定在模态对话框中是否值得考虑?

5

我对WPF还不太熟悉,所以这个问题可能显而易见或者不一致。

有一个需求是从子模态窗口编辑应用程序的某些基础业务数据,并且仅在用户按下该窗口上的“确定”按钮时更新数据。我们称此窗口为SettingsDialog。

在这种情况下,是否仍然合理使用WPF数据绑定将SettingsDialog的控件绑定到业务数据?(如果是这样,如何仅在用户按下SettingsDialog的“确定”按钮时更新业务数据?)

还是最好在SettingsDialog显示时手动从业务数据中分配SettingsDialog的控件值,然后仅在用户按下“确定”按钮时将它们分配回去?

正确选择的论据是什么(代码更小或更清晰、性能、可扩展性)?

是否有类似情况的公认的设计模式?

编辑:我选择了Bubblewrap的答案,因为它最符合我的具体情况。尽管Guard和John的答案也似乎可行。

总结一下:使用数据绑定有一些优点。它使SettingsDialog无需了解业务对象的内部连接和依赖关系(如果有),允许轻松地从模态切换到非模态模式,减少了GUI和业务数据之间的依赖关系。

要在单击“确定”按钮时实现对象更改,可以使用对象克隆/分配,或者对象可以实现IEditableObject接口。

在某些微不足道的情况下,使用数据绑定可能会有一些不必要的开销。

5个回答

4

我之前遇到过类似的需求,更喜欢使用数据绑定来实现两种变体。

在第一种变体中,我们克隆对象并将其绑定到克隆对象。当用户按下“确定”时,克隆对象替换为真正的对象。这在一次编辑一个对象且该对象没有被其他对象直接引用时非常有用。如果它被引用了,那么您可以将克隆体的值复制到原始对象中,以保持引用不变。这样节省了工作量,因为编辑器中不需要额外的工作,最多只需要在对象上定义2个方法:Clone和可能的Apply方法,以从克隆体中复制值。

在第二种变体中,我们将绑定到原始对象,但是我们会将原始值存储在我们的编辑对话框中,可以是本地字段或临时数据对象中。当用户按下“确定”时,什么特别的事情都不需要发生,但是当用户按下“取消”时,我们会还原这些值。这在您仅在对话框中编辑少数简单属性时非常有用。

我更喜欢数据绑定解决方案,因为它不会使编辑对话框中混杂着应用/取消逻辑,如果在编辑器中实现,这非常依赖于您的XAML:控件可以被重命名,组合框可以被文本框替换等等,所有这些都会影响手动从控件中分配数据的代码。

Clone/Apply方法只需要在您的业务对象上定义,理论上这些对象比您的UI编辑器更稳定。即使XAML发生更改,在大多数情况下,您的绑定仍然可以保持不变。例如,从组合框到文本框的更改仅意味着您将Text绑定而不是SelectedValue,但实际绑定是相同的。


4

一种选择是让您的业务对象实现IEditableObject

  • 在显示窗口之前,调用对象上的BeginEdit
  • 如果用户单击确定,则调用EndEdit
  • 如果用户单击取消,则调用CancelEdit

IEditableObject通常用于数据网格方案,但它也适用于您描述的情况。此外,如果您的UI将来更改以允许在DataGrid中进行内联编辑,您不必更改业务对象。


我选择了IEditableObject路线,我并不后悔。 - Andrei Rînea

1

你可以使用数据绑定来单向更新GUI,但如果你想要在仅在按下“确定”后延迟更新业务模型,最好在代码中完成。在这种情况下,数据绑定可能是不必要的。

FolderBrowserDialog为例。在调用ShowDialog()之前,您可以将初始值设置为SelectedPath,但在处理数据之前,您需要等待对话框返回DialogResult.OK。类似的方法也适用于您的情况。


0

我最近遇到了同样的问题 - 我有弹出式对话框表单来编辑对象属性。经过一些调整,我最终得到了下面的代码,似乎运行良好。希望能帮助到某些人。

我使用这个来显示弹出式表单。

    public static bool EditTask(MyTask task)
    {
        //make a backup object in case user clicks Cancel
        MyTask backupTask = new MyTask();
        CloneObject(task, backupTask);

        //dialog form uses data binding to the backupTask object
        MyTaskWindow f = new MyTaskWindow(backupTask);

        f.ShowDialog();
        if (f.DialogResult.HasValue && f.DialogResult.Value)
        {
            //user clicked "Ok" - clone everything back
            CloneObject(backupTask, task);
            return true;
        }
        else
        {                
            return false;
        }
    }

    //Reflection is used to clone object
    //copied from http://www.c-sharpcorner.com/UploadFile/ff2f08/deep-copy-of-object-in-C-Sharp/
    private static void CloneObject(object objSource, object objTarget)
    {
        //step : 1 Get the type of source object and create a new instance of that type
        Type typeSource = objSource.GetType();
        //object objTarget = Activator.CreateInstance(typeSource);

        //Step2 : Get all the properties of source object type
        PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

        //Step : 3 Assign all source property to taget object 's properties
        foreach (PropertyInfo property in propertyInfo)
        {
            //Check whether property can be written to
            if (property.CanWrite)
            {
                //Step : 4 check whether property type is value type, enum or string type
                if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
                {
                    property.SetValue(objTarget, property.GetValue(objSource, null), null);
                }
                //else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
                else
                {
                    object objPropertyValue = property.GetValue(objSource, null);
                    if (objPropertyValue == null)
                    {
                        property.SetValue(objTarget, null, null);
                    }
                    else
                    {
                        Type newTypeSource = objPropertyValue.GetType();
                        object newObjTarget = Activator.CreateInstance(newTypeSource);
                        CloneObject(objPropertyValue, newObjTarget);
                        property.SetValue(objTarget, newObjTarget, null);
                    }
                }
            }
        }            
    }

0
在表单加载处理程序中,遍历绑定集合并将每个DataSourceUpdateMode设置为Never。 在OK处理程序中设置为相反的值(DataSourceUpdateMode.OnValidation),并调用表单ValidateChildren方法。
如果通过GUI进行绑定,则会有一个新的类BindingSource的表单成员。 它简化了一些操作-可以通过CurrencyManager.Bindings访问绑定,并且可以使用BindingSource.EndEdit代替ValidateChildren。

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