WPF:对于模态对话框,数据绑定是否相当可观?

时间:2009-08-10 16:42:03

标签: c# wpf data-binding modal-dialog

(我对WPF很新,所以这个问题可能看起来很明显或不一致。)

需要从子模式窗口编辑应用程序的某些基础业务数据,并且只有在用户按下此窗口中的“确定”按钮时才更新数据。我们将此窗口称为SettingsDialog。

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

或者在显示SettingsDialog时从业务数据手动分配SettingsDialog的控件值是否更好,然后仅在用户按下OK按钮时将其分配回来?

正确选择的参数是什么(更小或更清晰的代码,性能,可扩展性)?

类似案例是否有一些公认的设计模式?

编辑:我将Bubblewrap的答案标记为已被接受,因为它最适合我自己的具体案例。虽然,卫兵和约翰的答案似乎也可以接受。

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

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

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

5 个答案:

答案 0 :(得分:4)

之前我遇到过类似的要求,并且更喜欢使用数据绑定的两种变体。

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

我们绑定到原始对象的第二个变体,但是我们将原始值存储在编辑对话框中,可以是本地字段,也可以是临时数据对象。当用户按下OK时,不会发生任何特殊情况,但是当用户按下取消时,我们还原值。当您在对话框中仅编辑几个简单属性时,这非常有用。

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

克隆/应用方法只需要在您的业务对象上,理论上它们比您的UI编辑器更稳定。即使XAML发生变化,在大多数情况下,您的绑定也可以保持不变。例如,从组合框到文本框的更改仅表示您绑定到Text而不是SelectedValue,但实际绑定是相同的。

答案 1 :(得分:4)

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

  • 在显示窗口之前,请在对象上调用BeginEdit
  • 如果用户点击“确定”,请致电EndEdit
  • 如果用户点击取消,请致电CancelEdit

IEditableObject通常用于数据网格场景,但它适用于您所描述的情况。此外,如果您的UI发生变化以允许在DataGrid中进行内联编辑,则无需更改业务对象。

答案 2 :(得分:1)

您可以使用数据绑定对GUI进行单向更新,但如果您希望仅在按下“确定”后推迟更新业务模型,则最好在代码中执行此操作。在这种情况下,数据绑定可能是不必要的。

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

答案 3 :(得分:0)

在表单上加载处理程序转到Bindings集合并将每个DataSourceUpdateMode设置为Never。 在OK处理程序上设置为反向(DataSourceUpdateMode.OnValidation)并调用表单ValidateChildren

如果通过GUI完成绑定,则会有类BindingSource的新表单成员 它简化了CurrencyManager.Bindings可以访问的位绑定,并且可以使用BindingSource.EndEdit代替ValidateChildren。

答案 4 :(得分: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);
                    }
                }
            }
        }            
    }