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

4
我有一组复杂模型,每个模型都包含对其他复杂模型的接口实例集合,我需要展示这些父子复杂模型,并允许编辑父子复杂模型的所有属性。
如何最好地显示这些数据,并允许分别编辑父子对象的属性,以及通过选择多个单元格和上下文菜单单击的组合来更改子模型上相同属性值的多个父级?我还需要能够通过在编辑机制中进行搜索(当前为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代码后台使用反射动态生成了列。子复杂对象实例的列绑定使用CustomObservableCollection<>上的下标(在这种情况下允许按通用值[enum ChildType]进行索引的自定义集合)。特别是绑定使得跨多个父级子实例正确设置同一属性的值变得困难(通过对列进行多选和上下文菜单单击以设置值)。同样,我正在处理这些大量更改的方式是在View上的代码后台,使用反射绑定路径解析来设置属性值(感觉不对,讨厌以这种方式做)。我想能够在ViewModel上设置所选的子项,并将属性名称和属性的新值传递给ViewModel中的命令以进行更改。甚至能够传递命令的子类型、属性和新值也会很好(我想)。
我的谷歌搜索、stackoverflow、Code Project等研究指向了我当前的解决方案,但我觉得我对问题的思考方式不正确,应该有更好的MVVM方法来解决这个问题。
编辑
该应用程序的主要重点是允许在视图中编辑多个父和子模型实例,用户可以比较几个实例的值,并允许将相同类型的多个对象的父或子属性的值设置为相同的值(即,Parent1和Parent2都有ChildModel1,用户想要将两个父对象的ChildModel1的Property3的名称设置为“X”)。尽管应用程序仍必须允许对父和子对象上的属性进行单独编辑(DataGrid似乎很好地满足了要求)。为了满足这些要求,我在视图中实现了动态列创建。下面是此逻辑的通用示例。
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”的列。您能否添加(简化版本的)列生成代码或者截图? - Markus Jarderot
1个回答

1

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

你必须考虑用户如何查看/编辑数据,并想出一个更简单的UI。例如,如果用户大多数时间查看/编辑ParentChildModels,很少查看/编辑ComplexType,那么你可以放置文本框来编辑父级和一个DataGrid来编辑其ChildModels

这样,你就有了更简单的UI和更容易编写的代码。我认为编写保存多个Parent的代码要复杂得多,就像这个例子一样。


1
很遗憾,这个应用程序并非如此。该应用程序的主要重点是允许在一个视图中编辑多个父模型和子模型实例,用户可以比较几个实例的值,并允许设置相同类型的多个对象的父属性或子属性的值(即Parent1和Parent2都有ChildModel1,用户想将两个父对象的ChildModel1的Property3的名称设置为“X”)。我会在问题中添加更多细节。 - James Raider

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