C # дженерики с MVVM, вытягивая T из <T> - PullRequest
1 голос
/ 23 апреля 2010

My Model - это универсальный класс, который содержит (например) свойство Value, которое может быть int, float, string, bool и т. Д. Поэтому, естественно, этот класс представлен как Model<T>.Ради коллекций Model<T> реализует интерфейс IModel, хотя IModel сам по себе пуст от любого контента.

My ViewModel содержит и экземпляр Model<T>, и он передается через ViewModel конструктор.Я все еще хочу знать, что T находится во ViewModel, поэтому, когда я выставляю Model для View, я знаю тип данных скрытого свойства Model Value.Класс ViewModel в конечном итоге выглядит следующим образом:

class ViewModel<T>
{
   private Model<T> _model;

   public ViewModel(Model<T> model) { ....blah.... }

   public T ModelsValue {get; set; }

}

Это прекрасно работает, но ограничено.Так что теперь мне нужно выставить коллекцию IModels с переменным Ts моему View, поэтому я пытаюсь настроить ObservableCollection из <strong>new</strong> ViewModel<T>s в изменяющемся списке IModels.Проблема в том, что я не могу понять, как получить T из Model<T> из IModel для построения ViewModel<T>(Model<T>) во время выполнения.

В отладчике VS2010 я могу навести курсор мыши на любой объект IModelи увидеть его полный Model<int>, например, во время выполнения, так что я знаю, что данные там.

Есть идеи?

Ответы [ 4 ]

7 голосов
/ 24 апреля 2010

Вот что я использую для просмотра коллекций моделей:

Предисловие:

Ваши объекты модели вида могут быть слабо напечатаны. Присвойте IModel свойство object Value {get;} и выставьте его в ModelViewModel : ViewModel<IModel>, который вы используете для всех IModel объектов (см. Мою реализацию ViewModel<T> ниже). Если у вас есть различные комбинации ObservableCollection<IModel>, ICollection<Model<T>> и т. Д., Показанная здесь структура спасет вас. Если вам все еще нужна модель общего вида, вы можете получить ModelViewModel<T> : ModelViewModel, который принимает Model<T> в своем конструкторе. Логика для создания соответствующего типа будет идти в преобразователе, переданном в ViewModelCollection.Create ниже. Имейте в виду, что этот дизайн налагает штраф производительности.

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

Пример использования:

public class CatalogViewModel : ViewModel<ICatalog>
{
    public CatalogViewModel(ICatalog catalog)
        : base(catalog)
    {
        Func<ICatalogProduct, ProductViewModel> viewModelFactory = CreateProductViewModel;

        this.Products = ViewModelCollection.Create(catalog.Products, viewModelFactory);
    }

    public ICollection<ProductViewModel> Products
    {
        get;
        private set;
    }

    private ProductViewModel CreateProductViewModel(ICatalogProduct product)
    {
        return new ProductViewModel(product, this);
    }
}

Преимущества:

  • Использует ленивые реализации, чтобы обеспечить эффективные и даже рекурсивные привязки в деревьях.
  • Коллекции моделей представлений реализуют INotifyCollectionChanged, только если базовая коллекция моделей реализует INotifyCollectionChanged.

Обзор классов (полные реализации, связанные с github):

  • ViewModel<TModel>: Базовый класс для классов моделей моего вида. Предоставляет свойство Model, которое я использую в коде поддержки модели представления.

  • ObservableViewModelCollection<TViewModel, TModel>: Ленивый (на самом деле не в настоящее время, но определенно должен быть), наблюдаемый отображение из модель для просмотра модели. Реализует INotifyCollectionChanged.

  • ViewModelCollection<TViewModel, TModel>: Ленивый отображение из коллекции TModel в коллекцию TViewModel.

  • ViewModelCollection: Статический помощник - возвращает ICollection<TViewModel>, используя ObservableViewModelCollection<TViewModel, TModel>, когда исходная коллекция реализует INotifyCollectionChanged, в противном случае используя ViewModelCollection<TViewModel, TModel>.

Несколько дополнительных типов, которые могут быть полезны для ваших коллекций моделей представлений:

ConcatCollection: Как и ViewModelCollection, он включает в себя статический помощник для автоматического выбора подходящей реализации. ConcatCollection объединяет коллекции, связываясь непосредственно с исходными коллекциями.

Вот пример того, как я использовал этот тип для представления свойства Children представлению, сохраняя мои наблюдаемые коллекции вплоть до исходного источника.

public class ProductViewModel : ViewModel<IProduct>
{
    public ProductViewModel(IProduct product)
        : base(product)
    {
        Func<IProduct, ProductViewModel> productViewModelFactory = CreateProductViewModel;
        Func<IRelease, ReleaseViewModel> releaseViewModelFactory = CreateReleaseViewModel;

        this.Products = ViewModelCollection.Create(product.Products, productViewModelFactory);
        this.Releases = ViewModelCollection.Create(product.Releases, releaseViewModelFactory);
        this.Children = ConcatCollection.Create<object>((ICollection)this.Products, (ICollection)this.Releases);
    }

    public IList<ProductViewModel> Products
    {
        get;
        private set;
    }

    public IList<ReleaseViewModel> Releases
    {
        get;
        private set;
    }

    public IEnumerable<object> Children
    {
        get;
        private set;
    }

    private ProductViewModel CreateProductViewModel(IProduct product)
    {
        return new ProductViewModel(product);
    }

    private ReleaseViewModel CreateReleaseViewModel(IRelease release)
    {
        return new ReleaseViewModel(release);
    }
}
0 голосов
/ 23 апреля 2010

Альтернативой может быть наличие интерфейса IModelValue, который бы выставлял T из Model<T>.Тогда ваш класс ViewModel будет выглядеть так:

class ViewModel
{
   private IModel _model;

   public ViewModel(IModel model) { ....blah.... }

   public IModelValue ModelsValue {get; set; }
}
0 голосов
/ 24 апреля 2010

C # generics не допускает универсальный тип в качестве параметра типа:

ObservableCollection<ViewModel<T>>

Выше не только недопустимо в C #, но и не имеет смысла, потому что это нарушит ограничения статического типа.

Я предполагаю, что вы действительно пытаетесь сделать так:

class ViewModel<T> : IMyViewModel {...}

new ObservableCollection<IMyViewModel>()

, чем вам нужна какая-то фабрика, которая будет генерировать экземпляры IMyViewModel на основе типа времени выполнения IModel:

public IMyViewModel CreateMyViewModel( IModel model){
    if (model is Model<A>)
        return new ViewModel(model as Model<A>);
    if (model is Model<B>)
        return new ViewModel(model as Model<B>);
    ...etc..
}

Таким образом, имея

IEnumarable<IModel> models = ...

, вы можете получить

var myVMs = 
    from m in models select CreateMyViewModel(m);

myCollection = new ObservableCollection<IMyViewModel>(myVMs);
0 голосов
/ 23 апреля 2010

В качестве примечания, когда вы сказали:

Когда я выставляю Model на View

Вы не соблюдаете соглашения MVVM, чего бы это ни стоило. В MVVM сама модель никогда не должна быть открыта для просмотра.

Как говорится, вы можете раскрыть тип T таким образом.

public Type ModelType
{
    get { return typeof(T); }
}

Если это соответствует вашим целям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...