4

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

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 中缺少的提示来克服这个问题?


4

2 回答 2

1

第一的:

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

    <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 属性定义为基本类型。

于 2013-04-10T08:44:46.473 回答
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)。

于 2013-04-10T09:00:03.600 回答