Silverlight with MVVM overlay: ModelView and View matching model
Today I have a dedicated question about MVVM Silverlight (4 RC) and the concept of inheritance and looking for a best practice solution ... I think I understand the main idea and concepts of MVVM. My Model doesn't know anything about the ViewModel as the ViewModel itself doesn't know about the View. ViewModel knows Model and Views know ViewModels.
Imagine the following basic (example) scenario (I'm trying to keep something short and simple):
My model contains a class ProductBase
with a few basic properties, and SimpleProduct : ProductBase
adds a few more properties and ExtendedProduct : ProductBase
adds other properties. According to this model, I have several ViewModels, the most important SimpleProductViewModel : ViewModelBase
and ExtendedProductViewModel : ViewModelBase
. Last but not least, according to Views SimpleProductView
and ExtendedProductView
. In the future, I could add many types of products (and matching Views + VMs).
1. How do I know which ViewModel to create when getting a collection of models?
After calling the method of the data provider, it will finally get List<ProductBase>
. It contains, for example, one SimpleProduct and two ExtendedProducts. How do I convert the results to ObservableCollection<ViewModelBase>
with the correct ViewModel types (one SimpleProductViewModel and two ExtendedProductViewModels)?
I can check the model type and build the ViewModel accordingly, i.e.
foreach(ProductBase currentProductBase in resultList)
if (currentProductBase is SimpleProduct)
viewModels.Add(
new SimpleProductViewModel((SimpleProduct)currentProductBase));
else if (currentProductBase is ExtendedProduct)
viewModels.Add(
new ExtendedProductViewModels((ExtendedProduct)currentProductBase));
...
}
... but I consider this to be very bad practice as this code does not conform to OO design. Another way providing abstract Factory methods would reduce the code to:
foreach(ProductBase currentProductBase in resultList)
viewModels.Add(currentProductBase.CreateViewModel())
and would be perfectly extensible, but since the Model doesn't know the ViewModels, this is not possible. I could bring interfaces into play here, but I haven't seen an approach like this yet.
2. How do I know which view should be displayed when the ViewModel is selected?
This is pretty much the same problem, but at a higher level. Finally, if the desired collection ObservableCollection<ViewModelBase>
requires a master view to select the appropriate view for the ViewModel.
There is a concept in WPF DataTemplate
that can provide a representation for a specific data type. Unfortunately, it does not work in the Silverlight, and the only replacement, which I found was a ResourceSelector
tool SLExtensions , which is a mistake and does not satisfy.
In addition, all of the problems from Question 1 also apply.
Do you have any hints or even a solution to the problems I am describing that you can hopefully understand from my explanation?
Thank you in advance!
Thomas
a source to share
I do things like this to make MVVM strongly typed.
I am defining some basic interfaces
public interface IModel
{
}
public interface IViewModel
{
}
public interface IViewModel<M> : IViewModel
where M : IModel
{
void Bind(M model);
}
public interface IView
{
}
public interface IView<VM> : IView
where VM : IViewModel
{
void Bind(VM viewModel);
}
This provides a basic relationship between my models, model views and views.
I am creating abstract implementations for IModel
both generics IViewModel<>
and IView<>
interfaces.
public abstract class ModelBase : IModel
{
}
public abstract class ViewModelBase<M> : IViewModel<M>
where M : IModel
{
public abstract void Bind(M model);
}
public abstract class ViewBase<VM> : IView<VM>
where VM : IViewModel
{
public abstract void Bind(VM viewModel);
}
Then I use them to define specific concrete objects - with interfaces first.
public interface IPersonModel : IModel
{
}
public interface IPersonViewModel : IViewModel<IPersonModel>
{
}
public interface IPersonView : IView<IPersonViewModel>
{
}
Notice how interface inheritance is blocked in type relationships.
Now you can define specific classes.
public class PersonModel : ModelBase, IPersonModel
{
}
public class PersonViewModel : ViewModelBase<IPersonModel>, IPersonViewModel
{
public override void Bind(IPersonModel model)
{
throw new NotImplementedException();
}
}
public class PersonView : ViewBase<IPersonViewModel>, IPersonView
{
public override void Bind(IPersonViewModel viewModel)
{
throw new NotImplementedException();
}
}
So given a model, I can search for an object that implements IViewModel<M>
for that model, and given a view model, I can search IView<VM>
for that view model.
Dependency Injection frameworks can be used to search here.
Hope this helps.
a source to share