【问题标题】:Doing CRUD to many types using one view in MVVM在 MVVM 中使用一个视图对多种类型进行 CRUD
【发布时间】:2013-04-10 08:27:09
【问题描述】:

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

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 中是否缺少解决此问题的技巧?


【问题讨论】:

    标签: c# wpf entity-framework mvvm prism


    【解决方案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 types->viewModel 的类型的小示例?我使用 Unity 作为 IoC 容器。
    • 这是一个很好的方法,我希望我可以重构视图!好像我什么都得不到。
    • 谢谢,我认为这是最适合我的情况。虽然我还没有实现它。
    【解决方案2】:

    第一:

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

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

    【讨论】:

    • 我只需要为超类构建一个根 UI,然后将这些数据模板添加为子视图,还是只使用一个主视图,复制数据模板并根据类型进行自定义?
    • @HichemC 老实说,我对大型 WPF 应用程序没有太多经验,但我建议使用单一视图。其实这取决于我猜你的真实情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多