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
The alternative would be to have an interface IModelValue
that would expose T from Model<T>
. Then your ViewModel class would look like:
class ViewModel
{
private IModel _model;
public ViewModel(IModel model) { ....blah.... }
public IModelValue ModelsValue {get; set; }
}
C# generics won't allow generic type as type parameter:
ObservableCollection<ViewModel<T>>
Above is not only illegal in C#, but also makes no sense because it would break static type constraints.
I'm guessing what you really are trying to do is:
class ViewModel<T> : IMyViewModel {...}
new ObservableCollection<IMyViewModel>()
than you need some kind of factory that would produce IMyViewModel instances based on IModel runtime type:
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..
}
thus, having a
IEnumarable<IModel> models = ...
you can get
var myVMs =
from m in models select CreateMyViewModel(m);
myCollection = new ObservableCollection<IMyViewModel>(myVMs);
As a side note, when you said:
When I expose
Model
to theView
You aren't following MVVM conventions, for whatever that's worth. In MVVM, the model itself should never be exposed to the view.
That being said, you can expose the type of T
in this way.
public Type ModelType
{
get { return typeof(T); }
}
If that suits your purposes.
Here's what I'm using for view model collections:
Your view model objects can be weakly typed. Give IModel
a property object Value {get;}
and expose that in a ModelViewModel : ViewModel<IModel>
that you use for all IModel
objects (see my ViewModel<T>
implementation below). If you have various combinations of ObservableCollection<IModel>
, ICollection<Model<T>>
, etc., the framework shown here is a lifesaver. If you still need generic view model, you can derive a ModelViewModel<T> : ModelViewModel
that takes a Model<T>
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;
}
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
if the underlying model collection implements INotifyCollectionChanged
.ViewModel<TModel>: Base class for my view model classes. Exposes a Model
property that I use in the view model's backing code.
ObservableViewModelCollection<TViewModel, TModel>: Lazy (actually not currently, but definitely should be), observable mapping from a model to a view model. Implements INotifyCollectionChanged
.
ViewModelCollection<TViewModel, TModel>: Lazy mapping from a collection of TModel to a collection of TViewModel
.
ViewModelCollection: Static helper - returns an ICollection<TViewModel>
, using ObservableViewModelCollection<TViewModel, TModel>
when the source collection implements INotifyCollectionChanged
, otherwise using ViewModelCollection<TViewModel, TModel>
.
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).
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<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);
}
}