4

I have three types of Items as follows :

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 am developing an application that uses all of MVVM, WPF, PRISM, EF5, I'm a bit stuck on:

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

  • Do I need to expose a dynamic property in order to deal with the three types?
  • Is there a tip that I am missing in MVVM to overcome this ?


HichemSeeSharp
  • 3,240
  • 2
  • 22
  • 44

2 Answers2

1

First:

You can create appropriate DataTempates for each type. The runtime automatically selects them based on the type of the 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>

You obviously have to create a new DataTemplate for each new type.

Second:

Just define the ViewModel property as the base type.

Mohammad Dehghan
  • 17,853
  • 3
  • 55
  • 72
  • Do I need just to build a root UI for the super class and then add these datatemplates as subviews or use just one main view, duplicate datatemplate and customize depending on the type ? – HichemSeeSharp Apr 10 '13 at 09:40
  • @HichemC Honestly I have not much experience with large WPF applications, but I recommend using a single view. Actually that depends on your real scenario I guess. – Mohammad Dehghan Apr 10 '13 at 12:00
1

Another approach which doesn't break the open/closed principle is to create view models and views for each type of StockItem, and then have a type which collates all of the exposed sub types and their corresponding view models, and provides a factory method that takes a StockItem and returns the matching view model.

This would be easy to do with an IoC container or MEF 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 untested, but it shows you the general approach. You could use generics to make this tidier.

If you want to use Unity instead of MEF, then the concept would be the same, but your would need to register each implementation of IStockItemEditViewModelResolver (or use a Unity extension and do it with conventions), and then your factory would need a reference to the container so that it could do a ResolveAll (see Unity Resolve Multiple Classes).

Community
  • 1
  • 1
devdigital
  • 34,151
  • 9
  • 98
  • 120