C# generics with MVVM, pulling the T out of

后端 未结 4 2155
情深已故
情深已故 2021-02-11 09:42

My Model is a generic class that contains a (for example) Value property which can be int, float, string, bool, etc. So naturally this class is represe

4条回答
  •  攒了一身酷
    2021-02-11 10:39

    Here's what I'm using for view model collections:

    Preface:

    Your view model objects can be weakly typed. Give IModel a property object Value {get;} and expose that in a ModelViewModel : ViewModel that you use for all IModel objects (see my ViewModel implementation below). If you have various combinations of ObservableCollection, ICollection>, etc., the framework shown here is a lifesaver. If you still need generic view model, you can derive a ModelViewModel : ModelViewModel that takes a Model in its constructor. The logic to create the appropriate type would go in the converter passed to ViewModelCollection.Create below. Do be warned that this design will impose a performance penalty.

    ModelViewModel CreateModelViewModel(IModel model)
    {
        Type viewModelType = typeof(ModelViewModel<>).MakeGenericType(model.Type);
        ModelViewModel viewModel = Activator.CreateInstance(viewModelType, model);
        return viewModel;
    }
    

    Example usage:

    public class CatalogViewModel : ViewModel
    {
        public CatalogViewModel(ICatalog catalog)
            : base(catalog)
        {
            Func viewModelFactory = CreateProductViewModel;
    
            this.Products = ViewModelCollection.Create(catalog.Products, viewModelFactory);
        }
    
        public ICollection Products
        {
            get;
            private set;
        }
    
        private ProductViewModel CreateProductViewModel(ICatalogProduct product)
        {
            return new ProductViewModel(product, this);
        }
    }
    

    Benefits:

    • Uses lazy implementations to allow for efficient and even recursive bindings in trees.
    • The view model collections only implement INotifyCollectionChanged if the underlying model collection implements INotifyCollectionChanged.

    Overview of the classes (full implementations linked to github):

    • ViewModel: Base class for my view model classes. Exposes a Model property that I use in the view model's backing code.

    • ObservableViewModelCollection: Lazy (actually not currently, but definitely should be), observable mapping from a model to a view model. Implements INotifyCollectionChanged.

    • ViewModelCollection: Lazy mapping from a collection of TModel to a collection of TViewModel.

    • ViewModelCollection: Static helper - returns an ICollection, using ObservableViewModelCollection when the source collection implements INotifyCollectionChanged, otherwise using ViewModelCollection.

    A few extra types that might be useful for your view model collections:

    ConcatCollection: Like ViewModelCollection, this includes a static helper to automatically choose an appropriate implementation. The ConcatCollection concatenates collections by binding directly to the source collection(s).

    • ConcatCollection
    • ConcatCollection
    • ObservableConcatCollection

    Here is an example of how I used this type to expose a Children property to the view while maintaining my observable collections all the way to back to the original source.

    public class ProductViewModel : ViewModel
    {
        public ProductViewModel(IProduct product)
            : base(product)
        {
            Func productViewModelFactory = CreateProductViewModel;
            Func releaseViewModelFactory = CreateReleaseViewModel;
    
            this.Products = ViewModelCollection.Create(product.Products, productViewModelFactory);
            this.Releases = ViewModelCollection.Create(product.Releases, releaseViewModelFactory);
            this.Children = ConcatCollection.Create((ICollection)this.Products, (ICollection)this.Releases);
        }
    
        public IList Products
        {
            get;
            private set;
        }
    
        public IList Releases
        {
            get;
            private set;
        }
    
        public IEnumerable Children
        {
            get;
            private set;
        }
    
        private ProductViewModel CreateProductViewModel(IProduct product)
        {
            return new ProductViewModel(product);
        }
    
        private ReleaseViewModel CreateReleaseViewModel(IRelease release)
        {
            return new ReleaseViewModel(release);
        }
    }
    
        

    提交回复
    热议问题