Today I have a special question on Silverlight (4 RC) MVVM and inheritance concepts and looking for a best practice solution... I think that i understand the basic idea and concepts behind MVVM. My Model doesn't know anything about the ViewModel as the ViewModel itself doesn't know about the View. The ViewModel knows the Model and the Views know the ViewModels.
Imagine the following basic (example) scenario (I'm trying to keep anything short and simple):
My Model contains a ProductBase
class with a few basic properties, a SimpleProduct : ProductBase
adding a few more Properties and ExtendedProduct : ProductBase
adding another properties. According to this Model I have several ViewModels, most essential SimpleProductViewModel : ViewModelBase
and ExtendedProductViewModel : ViewModelBase
. Last but not least, according Views SimpleProductView
and ExtendedProductView
. In future, I might add many product types (and matching Views + VMs).
1. How do i know which ViewModel to create when receiving a Model collection?
After calling my data provider method, it will finally end up having aList<ProductBase>
. It containts, for example, one SimpleProduct and two ExtendedProducts. How can I transform the results to an ObservableCollection<ViewModelBase>
having the proper ViewModel types (one SimpleProductViewModel and two ExtendedProductViewModels) in it?
I might check for Model type and construct 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 very bad practice as this code doesn't follow the object oriented design. The other way round, 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, that's not possible. I might bring interfaces into game here, but I haven't seen such approach proven yet.
2. How do i know which View to display when selecting a ViewModel?
This is pretty the same problem, but on a higher level. Ended up finally having the desiredObservableCollection<ViewModelBase>
collection would require the main view to choose a matching View for the ViewModel.
In WPF, there is a DataTemplate
concept which can supply a View upon a defined DataType. Unfortunately, this doesn't work in Silverlight and the only replacement I've found was the ResourceSelector
of the SLExtensions toolkit which is buggy and not satisfying.
Beside that, all problems from Que开发者_高级运维stion 1 apply as well.
Do you have some hints or even a solution for the problems I describe, which you hopefully can understand from my explanation?
Thank you in advance!
Thomas
I do this kind of thing to make MVVM strongly-typed.
I define 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 the basic relationships between my models, model views & views.
I create abstract implementations for IModel
and the generic IViewModel<>
& 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);
}
I then use these to define the actual concrete objects - with interfaces first of course.
public interface IPersonModel : IModel
{
}
public interface IPersonViewModel : IViewModel<IPersonModel>
{
}
public interface IPersonView : IView<IPersonViewModel>
{
}
Note how the inheritance of the interface locks in the type relationships.
Now the concrete classes can be defined.
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 look for an object that implements the IViewModel<M>
for that model & given a view model I can look for the IView<VM>
for that view model.
Dependency injection frameworks can be used here to do the look ups.
I hope this helps.
精彩评论