c# - 使用 MVVM 在 WPF Toolkit DataGrid 中显示/编辑复杂对象

标签 c# wpf mvvm wpfdatagrid wpftoolkit

我有一组复杂模型,每个模型都包含一组与其他复杂模型的接口(interface)实例,我需要显示这些父子复杂模型,允许编辑父子复杂模型的所有属性。

我怎样才能最好地显示这些数据并允许单独编辑父对象和子对象的属性,以及通过选择多个单元格和单击上下文菜单的组合(即在整个子模型上更改相同的属性值)多个 parent )?我还需要能够通过从编辑机制(当前是 DataGrid 单元格)中搜索来执行将模型属性值设置为其他复杂模型实例的操作吗?

下面是一个类的通用示例,它近似于我在应用程序中使用的内容。

enum ChildType 
{ 
    One,
    Two,
    Three
}

class ComplexType
{
    public long ID { get; set; }
    public string Name { get; set; }

    public override string ToString()
    { 
        return Name;
    }
}        

class IChildModel
{
    ChildType Type { get; set; }
    string Name { get; set; }
}

class ChildModel1 : IChildModel
{
    public ChildType Type { get; set; }
    public string Name { get; set; }
    public string Property1 { get; set; }
    public decimal Property2 { get; set; }
    public ComplexType Property3 { get; set; }
}

class ChildModel2 : IChildModel
{
    public ChildType Type { get; set; }
    public long Property1 { get; set; }
    public string Property2 { get; set; }
}

class Parent
{
    public long ID { get; set; }
    public string Name { get; set; }
    public CustomObservableCollection<IChildModel> Children { get; set; }
}

class ViewModel
{
    public CustomObservableCollection<Parent> Parents { get; set; }
}

到目前为止,我已经使用 DataGrid 实现了应用程序,并使用反射在 View 代码隐藏中动态生成了列。子复杂对象实例的列绑定(bind)使用 CustomObservableCollection<> 上的下标(在这种情况下,自定义集合允许通过通用值 [enum ChildType] 进行索引)。特别是绑定(bind)使得很难在多个父级的子实例中正确设置同一属性的值(通过在列上进行多选并单击上下文菜单来设置值)。同样,我在 View 的代码隐藏中处理这些大规模更改,使用反射绑定(bind)路径解析来设置属性值(感觉不对;讨厌那样做)。我希望能够在 ViewModel 上设置选定的子项,并将属性名称和属性的新值传递给 ViewModel 中的命令以进行更改。即使能够将子类型、属性和新值传递给命令也很好(我认为)。

我通过 Google、stackoverflow、Code Project 等进行的研究使我找到了当前的解决方案,但我觉得我对问题的思考不正确,应该有更好的 MVVM 方法来解决这个问题。

编辑

此应用程序的主要重点是允许在 View 中编辑多个父模型实例和子模型实例,用户可以在其中比较多个实例的值,并允许跨多个对象设置父或子属性的值相同类型的相同值(即 Parent1 和 Parent2 都有一个 ChildModel1 并且用户想要将两个父对象的 ChildModel1 的 Property3 上的名称设置为“X”)。尽管如此,应用程序仍然必须允许对父对象和子对象的属性进行单独编辑(DataGrid 似乎确实很好地满足了要求)。为了满足这些要求,我在 View 中实现了动态列创建。下面是此逻辑的通用示例。

private void DataGrid_TargetUpdated(object sender, DataTransferEventArgs e)
{
    var vm = DataContext as ViewModel;

    if (vm != null && vm.Parents != null) {
        List<ChildType> processedChildTypes = new List<ChildType>();

        foreach (var parent in vm.Parents) {
            for (int childIndex = 0; childIndex < parent.Children.Count; ++childIndex) {
                var child = vm.Children[childIndex];

                if (!processedChildTypes.Contains(child.Type)) {    // Ensure each child type is only processed once
                    processedChildTypes.Add(child.Type);
                    CreateChildPropertyColumns(processedChildTypes, child);
                }
        }
    }
}

private void CreateChildPropertyColumns(List<ChildType> processedChildTypes, IChildModel child)
{
    PropertyInfo[] childProperties = child.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); // Only use properties declared on the child type
    Type childInterfaceType = typeof(IChildModel);

    foreach (PropertyInfo childProperty in childProperties) {   
        // Only create a column if the property is editable
        if (childProperty.CanWrite) {
            if (childInterfaceType.IsAssignableFrom(childProperty.PropertyType)) {
                var subChild = childProperty.GetValue(child, null) as IChildModel;

                if (subChild != null && !processedChildTypes.Contains(subChild.Type)) {
                    processedChildTypes.Add(subChild.Type);
                    CreateChildPropertyColumns(processedChildTypes, subChild);
                }
            }
            else
                dataGrid.Columns.Add(CreateChildPropertyColumn(child.Type, childProperty));
        }
    }
}

private DataGridColumn CreateChildPropertyColumn(ChildType childType, PropertyInfo propertyInfo)
{
    DataGridColumn column = null;
    var binding = new Binding(string.Format("Children[{0}].{1}", childType, propertyInfo.Name));

    /* Create column based on PropertyInfo here */
    /* Default case is a text column */
    column = new DataGridTextColumn() { Binding = binding };
    column.Header = propertyInfo.Name;

    return column;
}

最佳答案

我认为在这种情况下使用 DataGrid 不是一个好主意。大多数时候,用户很少同时查看/编辑MULTIPLE ParentChildModel2ComplexType

您必须考虑用户将如何查看/编辑数据并提出更简单的 UI。例如,如果用户大部分时间查看/编辑 ParentChildModels 而很少查看/编辑 ComplexType 那么您可以放置​​文本框来编辑parent 和一个 DataGrid 来编辑它的 ChildModels

这样一来,您的 UI 就更简单了,代码的编写也更容易了。我认为编写保存多个 Parent 的代码要复杂得多,如本例所示。

关于c# - 使用 MVVM 在 WPF Toolkit DataGrid 中显示/编辑复杂对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12966211/

相关文章:

c# - 如何使用 USB 和/或 WPD 将文件从 Android 设备传输到 PC

c# - 删除按键上的 ListView SelectedItem

c# - Protobuf-net - 仅序列化一些属性

c# - 显式设置 ItemTemplate 的 Datacontext

wpf - 我们如何将文本写入 WPF 组合框?

c# - 反序列化 GUID 数组时出现 JSON.NET 异常

wpf - 我们可以将 WinForms 应用程序与 WPF 应用程序集成吗?

c# - ViewModel 中的 MediaElement.play()

javascript - knockout/MVVM 设计问题

c# - WPF中MVVM下的父 subview 通信