Generici C # con MVVM, tirando la T fuori da

My Model è una class generica che contiene una proprietà Value (ad esempio) che può essere int, float, string, bool, ecc. Quindi, naturalmente questa class è rappresentata come Model . Per motivi di collezioni, il Model implementa l’interfaccia IModel , sebbene IModel sia esso stesso vuoto di qualsiasi contenuto.

My ViewModel contiene e l’istanza del Model e viene passata attraverso il costruttore di ViewModel . Voglio ancora sapere cosa T è in ViewModel, quindi quando espongo il Model alla View conosco il tipo di dati della proprietà Value sepolto del Model . La class per ViewModel si presenta come la seguente:

 class ViewModel { private Model _model; public ViewModel(Model model) { ....blah.... } public T ModelsValue {get; set; } } 

Funziona bene, ma è limitato. Quindi ora ho bisogno di esporre una raccolta di IModels con Ts variabile alla mia View , quindi sto provando a impostare una ObservableCollection di new ViewModels su un elenco di IModels di IModels . Il problema è che non riesco a capire come ottenere T dal Model da IModel per build ViewModel(Model) in fase di esecuzione.

Nel debugger VS2010 posso IModel qualsiasi object di IModel e vedere il suo intero Model per esempio in fase di esecuzione, quindi so che i dati sono lì.

Qualche idea?

Ecco cosa sto utilizzando per le raccolte di modelli di viste:

Prefazione:

Gli oggetti del tuo modello di vista possono essere digitati debolmente. Assegna a IModel un object Value {get;} proprietà object Value {get;} e ModelViewModel : ViewModel in ModelViewModel : ViewModel che utilizzi per tutti IModel oggetti IModel (vedi l’implementazione ViewModel mio ViewModel seguito). Se hai varie combinazioni di ObservableCollection , ICollection> , ecc., Il framework mostrato qui è un salvagente. Se è ancora necessario un modello di visualizzazione generico, è ansible derivare un ModelViewModel : ModelViewModel che accetta un Model nel suo costruttore. La logica per creare il tipo appropriato andrebbe nel convertitore passato a ViewModelCollection.Create seguito. Tieni presente che questo design imporrà una penalizzazione delle prestazioni.

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

Esempio di utilizzo:

 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); } } 

Benefici:

  • Utilizza le implementazioni pigre per consentire associazioni efficienti e anche ricorsive negli alberi.
  • Le raccolte del modello di visualizzazione implementano solo INotifyCollectionChanged se la collezione del modello sottostante implementa INotifyCollectionChanged .

Panoramica delle classi (implementazioni complete collegate a github):

  • ViewModel : class base per le classi del mio modello di vista. Espone una proprietà Model che utilizzo nel codice di supporto del modello di visualizzazione.

  • ObservableViewModelCollection : Lazy (in realtà non attualmente, ma sicuramente dovrebbe essere), mapping osservabile da un modello a un modello di vista. Implementa INotifyCollectionChanged .

  • ViewModelCollection : mapping lenta da una raccolta di TModel a una raccolta di TViewModel .

  • ViewModelCollection : helper statico: restituisce un ICollection , utilizzando ObservableViewModelCollection quando la raccolta di origine implementa INotifyCollectionChanged , altrimenti utilizza ViewModelCollection .

Alcuni tipi extra che potrebbero essere utili per le raccolte di modelli di viste:

ConcatCollection : come ViewModelCollection, questo include un helper statico per scegliere automaticamente un’implementazione appropriata. ConcatCollection concatena le raccolte legandosi direttamente alle raccolte di origini.

  • ConcatCollection
  • ConcatCollection
  • ObservableConcatCollection

Ecco un esempio di come ho usato questo tipo per esporre una proprietà Children alla vista mantenendo le mie collezioni osservabili fino alla fonte originale.

 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); } } 

L’alternativa sarebbe avere un’interfaccia IModelValue che esporrebbe T dal Model . Quindi la class ViewModel sarà simile a:

 class ViewModel { private IModel _model; public ViewModel(IModel model) { ....blah.... } public IModelValue ModelsValue {get; set; } } 

I generici C # non consentiranno il tipo generico come parametro tipo:

 ObservableCollection> 

Sopra non è solo illegale in C #, ma non ha senso perché potrebbe violare i vincoli di tipo statico.

Immagino che quello che stai cercando di fare sia:

 class ViewModel : IMyViewModel {...} new ObservableCollection() 

di quanto tu abbia bisogno di una sorta di fabbrica che produrrebbe istanze di IMyViewModel basate sul tipo di runtime di IModel:

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

quindi, avendo a

 IEnumarable models = ... 

Puoi prendere

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

Come nota a margine, quando hai detto:

Quando espongo il Model alla View

Non stai seguendo le convenzioni MVVM, per quello che vale. In MVVM, il modello stesso non dovrebbe mai essere esposto alla vista.

Detto questo, puoi esporre il tipo di T in questo modo.

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

Se questo è adatto ai tuoi scopi.