解析(不同类型)模型集合的ViewModels

本文关键字:集合 ViewModels 模型 同类型 解析 | 更新日期: 2023-09-27 18:21:20

我对与PRISM组合的MVVM模式有点困惑。简言之:我正在尝试基于由单独的服务生成的现有模型来创建视图和视图模型。该服务不知道也不应该知道任何关于视图和/或视图模型的信息。此服务创建不同类型的模型,为了简单起见,我们将其称为SquareModel和CircleModel。这些类型都共享相同的抽象BaseModel。假设该服务生成一个BaseModel类型的列表,其中包含Square和Circle模型。现在的问题是如何将这些模型转换为相应的ViewModels,并将它们放在一个新的列表中。每种类型都应该有自己的视图模型;所以:

  • SquareModel->SquareViewModel
  • CircleModel->CircleViewModel

这是因为这两个模型公开了不同的属性,我想使用ViewModel绑定到这些属性。此外,我如何将这两种ViewModel类型组合到一个列表中以呈现给我的视图?

视图包含一个列表框,该列表框根据视图模型类型加载适当的数据模板。

为了让事情更清楚,我把下面的示例代码放在一起,展示我所做的工作。第一种方法是通过开关类型,第二种方法使用MEF2.0的ExportFactory。两者都失败了,原因在代码中。我真的很感激任何帮助!

/*
 * Models (these are generated by a service, the service doesn't and should not now about views or view models)
 * 
 * 
 */
abstract class BaseModel { }
class SquareModel : BaseModel { }
class CircleModel : BaseModel { }

/*
 *  View Models
 * 
 * 
 */
abstract class BaseViewModel<TModel> // : INOtificationPropertyChanged, etc
{
    protected TModel Model;
    public void SetModel(TModel model)
    {
        Model = model;
        OnChangeModel();
    }
    protected virtual void OnChangeModel()
    {
        // Assignment of base properties here, based on Model
    }
    // Declarate some base properties here
}
[Export(typeof(BaseViewModel<BaseModel>))]
[TypeMetadata(Type = "CircleViewModel")]
class CircleViewModel : BaseViewModel<CircleModel>
{
    protected override void OnChangeModel()
    {
        // Assignment of circle specific properties here, based on Model
    }
    // Declarate some circle specific properties here
}
[Export(typeof(BaseViewModel<BaseModel>))]
[TypeMetadata(Type = "SquareViewModel")]
class SquareViewModel : BaseViewModel<SquareModel>
{
    protected override void OnChangeModel()
    {
        // Assignment of square specific properties here, based on Model
    }
    // Declarate some square specific properties here
}
class Program
{
    [ImportMany]
    protected IEnumerable<ExportFactory<BaseViewModel<BaseModel>, ITypeMetadata>> Factories { get; set; }
    public BaseViewModel<BaseModel> Create(string viewModelType)
    {
        var factory = (from f in Factories where f.Metadata.Type.Equals(viewModelType) select f).First();
        // Factory is able to create View Models of type viewModelType using CreateExport() function
        var vm = factory.CreateExport().Value;
        return vm;
        // Same error as with solution A
        // cannot convert from 'ConsoleApplication1.SquareViewModel' to 'ConsoleApplication1.BaseViewModel<ConsoleApplication1.BaseModel>'
        // This error is actually displayed in ExportFactory context, but it means the same
    }
    public BaseViewModel<BaseModel> CreateFrom(Type type)
    {
        var vmTypeName = type.Name + "ViewModel";
        return Create(vmTypeName);
    }
    public BaseViewModel<BaseModel> CreateVMUsingExportFactory(BaseModel model)
    {
        var vm = CreateFrom(model.GetType());
        vm.SetModel(model);
        return vm;
    }
    public void DoStuff()
    {
        // Suppose service gives me this
        var serviceOutput = new List<BaseModel>
                                {
                                    new SquareModel(),
                                    new CircleModel(),
                                    new CircleModel(),
                                    new SquareModel(),
                                    new CircleModel(),
                                    new SquareModel(),
                                    new SquareModel()
                                    // may be longer but not the point
                                };
        // viewModelCollection is bound to a listbox, by using datatemplates everthing is nicely placed on the canvas; no problem there
        // Actually this is a ObserveableCollection
        List<BaseViewModel<BaseModel>> viewModelCollection = new List<BaseViewModel<BaseModel>>();

        //
        // What to do here?
        //
        //
        // A. Switch-on-type
        foreach (var model in serviceOutput)
        {
            // Note there are beter implementations of this, using dicationaries and delegates, main goal of that is to not break when refactoring;
            switch (model.GetType().Name)
            {
                case "SquareModel":
                    SquareViewModel vm = new SquareViewModel();
                    vm.SetModel((SquareModel)model); // another cast..... :(
                    viewModelCollection.Add(vm);
                    // Error: 
                    // cannot convert from 'ConsoleApplication1.SquareViewModel' to 'ConsoleApplication1.BaseViewModel<ConsoleApplication1.BaseModel>'
                    break;
                case "CircleModel":
                    // same
                    break;
            }
        }
        // B. MEF ExportFactory<>
        //
        foreach (var model in serviceOutput)
        {
            var vm = CreateVMUsingExportFactory(model);
            viewModelCollection.Add(vm);
        }
        // C. Something else?!
        //
        // Please help ;-).
    }

解析(不同类型)模型集合的ViewModels

xaml绑定对对象类型非常宽容,您可以使用对象而不是定义类型

如果你的数据模板代码有任何问题(我想这应该不是问题),请使用以下

List<object> viewModelCollection = new List<object>(serviceOutput.Select(model=> CreateVMUsingExportFactory(model) as object));

上面使用linq和lambda创建一个对象列表。