在MVVM中使用一个视图对多种类型进行CRUD操作

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中是否有什么提示可以克服这个问题?
2个回答

1

首先:

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

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

你显然需要为每个新类型创建一个新的。

第二点:

只需将ViewModel属性定义为基础类型即可。


我只需为超类构建一个根UI,然后将这些数据模板作为子视图添加,还是只使用一个主视图,复制数据模板并根据类型进行自定义? - HichemSeeSharp
@HichemC 老实说,我对大型 WPF 应用程序没有太多经验,但我建议使用单个视图。实际上,这取决于你的实际情况,我猜。 - Mohammad Dehghan

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


请问您能否提供一个小例子,用于管理StockItem类型的viewModel?我正在使用Unity作为IoC容器。 - HichemSeeSharp
这是一个很好的方法,我希望我能重构视图!看起来我不能得到一切。 - HichemSeeSharp
谢谢,我会考虑这个对我的情况最合适。虽然我还没有实施它。 - HichemSeeSharp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接