Running CRUD for many types using a single view in MVVM

I have three types of elements:

public class StockItem { public int Id { get; set; } public string Name { get; set; } public decimal UnitPrice { get; set; } } public class LotItem : StockItem { public virtual ICollection<Lot> Lots { get; set; } } public class DetailedItem : StockItem { public virtual ICollection<SerialNumber> SerialNumbers { get; set; } } 

As I develop an application that uses all MVVM, WPF, PRISM, EF5, I'm a little stuck on:

First one . How will I do CRUD for these types, using one view that changes (shows / hides the controls) depending on the type, knowing that I can have new types later that inherit from the same type?
Second : how do I bind View to View-Model:

  • Do I need to show a dynamic property to deal with three types?
  • Is there any advice that I lack in MVVM to overcome this?


+4
source share
2 answers

Another approach that does not violate the open / closed principle is to create view models and views for each type of StockItem, and then a type that compares all open subtypes and their corresponding view models and provides a factory that accepts the StockItem element and returns the corresponding view model.

This would be easy to do with an IoC or MEF container, for example.

Update

As a quick example using MEF:

 public class StockItemEditViewModelFactory : IPartImportsSatisfiedNotification { private Dictionary<Type, IStockItemEditViewModelResolver> resolvers; [ImportMany(IStockItemEditViewModelResolver)] private IEnumerable<IStockItemEditViewModelResolver> importedResolvers; public void OnImportsSatisfied() { // Build dictionary of StockItem -> StockItemEditViewModel // Do error handling if no imported resolvers or duplicate types resolvers = new Dictionary<Type, IStockItemEditViewModelResolver> foreach(var importedResolver in importedResolvers) { resolvers.Add(importedResolver.StockItemType, importedResolver); } } public IStockItemEditViewModel Create(StockItem stockItem) { // Find the appropriate resolver based on stockItem.GetType(), handle errors var type = stockItem.GetType(); var entry = this.resolvers.FirstOrDefault(kv => kv.Key == type); var resolver = entry.Value; return resolver.CreateEditViewModel(stockItem); } } [InheritedExport] public interface IStockItemEditViewModelResolver { Type StockItemType { get; } IStockItemEditViewModel CreateEditViewModel(StockItem stockItem); } public class LotItemEditViewModelResolver : IStockItemEditViewModelResolver { Type StockItemType { get { return typeof(LotItem); } } IStockItemEditViewModel CreateEditViewModel(StockItem stockItem) { return new LotItemEditViewModel(stockItem); } } public class MainViewModel { public IStockItemEditViewModel ActiveItem { get; private set; } public MainViewModel(StockItemEditViewModelFactory editViewModelfactory) { StockItem stockItem = new LotItem(); this.ActiveItem = editViewModelFactory.Create(myStockItem); } } 

This is not tested, but shows a general approach. You can use generics to make this tidier.

If you want to use Unity instead of MEF, then the concept will be the same, but you will need to register each implementation of IStockItemEditViewModelResolver (or use the Unity extension and do this with the conventions), and then your factory will need a reference to the container so that it can execute ResolveAll ( see Unity Resolve for several classes ).

+1
source

Firstly:

You can create an appropriate DataTempate for each type. The runtime automatically selects them depending on the type of object:

  <Window.Resources> <DataTemplate DataType="{x:Type local:LotItem}"> <ItemsControl ItemsSource="{Binding Path=Lots}"> <ItemsControl.ItemTemplate> <DataTemplate> ... </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> <DataTemplate DataType="{x:Type local:DetailedItem}"> <ItemsControl ItemsSource="{Binding Path=SerialNumbers}"> <ItemsControl.ItemTemplate> <DataTemplate> ... </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </Window.Resources> 

Obviously, you need to create a new DataTemplate for each new type.

The second:

Just define the ViewModel property as the base type.

+1
source

All Articles