使用MVVM中的一个视图对许多类型执行CRUD

时间:2013-04-10 08:27:10

标签: c# wpf entity-framework mvvm prism

我有三种类型的项目如下:

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

当我开发一个使用MVVM,WPF,PRISM,EF5全部的应用程序时,我有点坚持:

首先:我将如何使用一个根据类型更改(显示/隐藏控件)的视图对这些类型执行CRUD,因为我知道稍后我可能会继续使用新类型类型?
第二:我如何将视图绑定到视图模型:

  • 我是否需要公开动态属性才能处理这三种类型?
  • 我是否在MVVM中缺少一个提示来克服这个问题?


2 个答案:

答案 0 :(得分:1)

<强>首先

您可以为每种类型创建适当的DataTempate。运行时根据对象的类型自动选择它们:

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

您显然必须为每种新类型创建一个新的DataTemplate

<强>第二

只需将ViewModel属性定义为基本类型。

答案 1 :(得分:1)

另一种不破坏开放/封闭原则的方法是为每种类型的StockItem创建视图模型和视图,然后使用一种类型来整理所有暴露的子类型及其相应的视图模型,并提供采用StockItem并返回匹配视图模型的工厂方法。

例如,使用IoC容器或MEF很容易做到这一点。

<强>更新

作为使用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);
    }
}

这是未经测试的,但它向您展示了一般方法。您可以使用泛型来使这更整洁。

如果你想使用Unity而不是MEF,那么概念将是相同的,但你需要注册IStockItemEditViewModelResolver的每个实现(或使用Unity扩展并使用约定),然后你的工厂需要对容器的引用,以便它可以执行ResolveAll(请参阅Unity Resolve Multiple Classes)。