Display / edit complex objects in the DataGrid Toolkit WPF using MVVM

I have a set of complex models, each of which contains a collection of interface instances for other complex models, and I need to display these parent and child complex models, allowing me to edit all the properties of the parent and child complex models.

What is the best way to display this data and allow editing the properties of the parent and child objects separately, as well as by combining the selection of several cells and clicking the context menu (i.e. change the same property value on the child model to several parents)? Do I also need to be able to perform actions such as setting the values โ€‹โ€‹of model properties in some other instance of a complex model through a search from the editing mechanism (currently in the DataGrid cell)?

Below is a general example of classes that comes close to what I'm working with in an application.

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; } } 

So far, I have implemented the application using a DataGrid, and dynamically generated columns in the Code view using reflection. Column binding for instances of child complex objects uses the index in CustomObservableCollection <> (a custom set that allows indexing on the common value [enum ChildType] in this case). Binding, in particular, made it difficult to correctly set the value for the same property for multiple parent child instances (via multiple selection in the column and context menu to set the value). Again, I handle such massive changes in the code in the view, using reflection from parsing bindings to set property values โ€‹โ€‹(this is wrong, hate does it this way). I would like to be able to set the selected child elements in the ViewModel and pass the property name and new property value to the command in ViewModel to make changes. Even with the ability to convey the command, the type of child, the property and the new value would be good (I think).

My research is through Google, stackoverflow, Code Project, etc. pointed me to my current solution, but I feel that I am thinking wrong about the problem, and for this there should be a better MVVM approach.

EDIT

The main focus of this application is to allow editing multiple instances of parent and child models in a view where the user can compare the values โ€‹โ€‹of several instances and be able to set the value of the parent or child property for several objects of the same type with the same value (that is, Parent1 and Parent2 both have ChildModel1, and the user wants to set the name on Property3 of both parent objects "ChildModel1" to "X"). Although the application still needs to allow separate changes to the properties of the parent and child objects (DataGrid really fills the requirement nicely). In accordance with these requirements, I introduced the dynamic creation of columns in the view. The following is a general example of what this logic looks like.

 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; } 
+4
source share
1 answer

I think that in this situation it is not recommended to use a DataGrid . In most cases, users rarely view / edit MULTIPLE Parent , ChildModel2 and ComplexType right away.

You need to think about how users will view / edit data and create a simpler interface. For example, if users view / edit Parent and ChildModels most of the time and rarely view / edit ComplexType , then you can put text fields to edit the parent element and DataGrid to edit its ChildModels .

This way you have a simpler interface and it is much easier to write code. I think it is much more difficult to write code that saves several Parent , as in this example.

+1
source

Source: https://habr.com/ru/post/1440677/


All Articles