I am struggling with a design desision while writing a WPF application. Here is the generic problem:
Consider an application needing to keep track of a list of abstract Vehicles that a person wants to buy. With a MVVM design, what is the best way to expose the different car types (e.g. Motorcycle, Car, Truck, etc.) to the View so that each of the Vehicle types can be shown and edited with a different form?
Currently I have this setup:
Model
abstract class Vehicle { .. }
class Car : Vehicle { .. }
class Truck : Vehicle { .. }
class Cart {
ObservableCollection<Vehicle> Vehicles;
}
ViewModel
class VehicleViewModel { .. }
class CartViewModel {
ObservableCollection<VehicleViewModel> Vehicles;
}
With this design though, I'm not sure how I can create a DataTemplate in my View of a Cart for each of the Vehicle types because the CartViewModel just contains a list of VehicleViewModel (and not CartViewModel, TruckViewModel, etc).
Can someone please guide me in a direction to take?
Related
If I create a Model Car with some associations:
hasOne Driver
hasMany Wheel
And then create a Model Truck extending Car with
class Truck extends Car { ... }
is it normal that the associations from Car are not inherited from Truck?
What are the possibilities to inherit model associations?
Thanks in advance!
Inheritance is PHP related; your custom classes will behave the same way regardless of anything in cakephp. Class inheritance in CakePHP is completely depended on How your classes are written. Inheritance is an important part of cakephp; the framework is very much object oriented.
Are you including the classes properly using App::uses() to load your car class into truck?
The only way your Truck class will not inherit from the Car class is if you have defined the properties in the Truck class.
class Car extends AppModel {
public $hasOne = array('Driver', ...);
}
class Truck extends Car {
public $hasOne = array(); // now you have no hasOne relations
}
I have a View / ViewModel where a ProductList is loaded. This list is not visible on the screen.
What I need to do is show a new View/ViewModel (e.g. SelectProductView / SelectProductViewModel), pass the ProductList to them, and after a user selects a particular Product, close this view, and make use of selected product.
What is the best way to achieve this?
I am using MVVMLight, but I guess the ideas should not be restricted just to it.
The easiest way is to create a view, and pass collection to it, but that doesn't sound MVVM friendly. I was thinking of creating a SelectProductViewModel from the first ViewModel and pass the collection to it, but I don't know how would I automatically create SelectProductView and bind it to created SelectProductViewModel.
Edit: in my application view structure is a bit complex. I have a main view, which basically needs to host a SelectProductView, since this view must cover whole screen. MainView contains lots of child and grandchild views (through tabs), so there could be 3 different child views or grand childViews that could issue a request for a product to be selected. Also, some view will not have products preloaded, so this task should probably be propagated to a SelectProductViewModel.
Example of Structure:
MainView
/ \
ChildViewA ChildViewB
/ \ / \
GrandChildViewA1 GrandChildViewA2 GrandChildViewB1 GrandChildViewB2
So, GrandChildViewA1, ChildViewB and GrandChildViewB2 could issue a request for a product to be selected. Only the view that issued a request should get the selected product, others should not bother with it. GrandChildViewA1 will have loaded products in it, but GrandChildViewB2 will not have ProductList loaded in it. This means, for performance sake, that GrandChildViewA1 should pass product list to SelectProductViewModel, while GrandCHildViewB2 will not have Product list in it, so SelectProductViewModel should fetch data from database.
I would create a generic viewModel which defines a contract for receiving data.
public abstract class PassDataViewModel<T> : ObservableObject
{
public T Data { get; }
}
I would then create a more general ViewModel for your product list like so:
public class SelectProductViewModel : PassDataViewModel<Product>
{
private Product _selectedProduct;
private ObservableCollection<Product> _products = new ObservableCollection<Product>();
public SelectProductViewModel(IList<Product> products)
{
_selectedProduct = _products.First();
}
public IEnumerable<Product> Products
{
get { return _products; }
}
public Product SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
OnPropertyChanged("SelectedProduct");
OnPropertyChanged("Data");
}
}
public Product Data
{
get { return _selectedProduct; }
}
}
You would use this in the following way:
Your first viewModel can create an instance of the SelectProductViewModel (when a command is invoked, for example)
You pass your products list to the new SelectProductViewModel instance.
Use a DataTemplate to change the view on your screen (this post will show you how to do this).
Have a property in the parent viewModel that returns the product returned from the data property of the SelectProductViewModel (you will need to propagate the PropertyChanged event to your parent viewModel).
the most easy way is to go the viewmodel first approach and use a dialogservice to show the selection view.
your viewmodel with ProductionList simply call the dialogservice and put a ProductSelectionViewmodel with ProductionList as parameter. because this is viewmodel first you have to create a datatemplate so WPF knows how to render your ProductSelectionViewmodel.
here is a link for a simple dialogservice.
btw: in my opinion viewmodel first approach is much easier when doing mvvm.
EDIT:
in your ProductionListViewModel in your SelectProductCommand
var selectProductViewModel = new SelectProductViewModel(this.ProductionList);
var result = this.uiDialogService.ShowDialog("Select Product", selectProductViewModel );
//if result true, simple get the selected product
this.SelectedProduct = selectProductViewModel.MySelectedProduct;
thats all - simple and easy
I am implementing a WPF based application using MVVMfor the UI.
I have a ViewModel that wraps each editable Model that can be edited. The VM contains all the logic for handling error notifications, "is dirty" management and so forth ..
This design supports well CRUD schenarios for simple domain Model objects that are anemic, that is, do not contain any logic.
Now, I am facing a more tricky problem cause I have a domain Model that contains logic and that logic can change the internal state of the domain Model.
Do someone have already faced this scenario ? If so, do you have some advices to handle this correctly ?
Riana
Here is how I usually deal with it:
The ViewModel layer is made of types that belong to this layer, meaning I don't ever directly use my business objects inside of a ViewModel. I map my business objects to ViewModel objects that may or may not be the exact same shape minus the behaviors. It can be argued that this violates Don't Repeat Yourself, but doing so allows you to adhere to the Single Responsibility Principle. In my opinion, SRP should usually trump DRY. The ViewModel exists to serve the view, and the model exists to serve business rules / behavior.
I create a facade/service layer that takes and returns ViewModels as arguments, but maps the ViewModels to-and-from their corresponding business object versions. This, way the non-anemic objects won't impose non view logic on the ViewModel
The dependencies would look like this:
ViewModel <--> Facade/ServiceLayer --> Business Objects
I think it is important to keep this in mind if you want to unleash the full potential of MVVM: The ViewModel is the model/abstraction of the view, not the model presented to the view.
Try using Command pattern. Your screen should be design not to edit an entity but to perform an action (command) on an entity. If you follow that principle when designing your screens, your ViewModel will have properties that should be mapped to a command object. Then, the command will be send to an (remote) facade of the domain model.
ViewModels for displaying the data could be mapped directly to the database (bypassing the domain model altogether) so that you don't need to put nasty getters in the domain model classes.
If the domain model is non-anemic, you will need to use events to communicate internal changes in the Model back to the ViewModel. That way you don't have to worry about keeping track of what operations could potentially make your VM out-of-sync with the model.
Here's a simple example:
First, a sample model:
public class NonAnemicModel
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
OnNameChanged(EventArgs.Empty);
}
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged(EventArgs e)
{
if (NameChanged != null)
NameChanged(this, e);
}
public void PerformNameCalculation(int chars)
{
//example of a complex logic that inadvertently changes the name
this.Name = new String('Z', chars); //makes a name of Z's
}
}
And here's a sample ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
private NonAnemicModel _model;
public NonAnemicModel Model
{
get { return _model; }
set
{
_model = value;
_model.NameChanged += (sender, args) => NotifyPropertyChanged("UserName");
}
}
public string UserName
{
get { return this.Model.Name; }
set { this.Model.Name = value; }
}
//this command would call out to the PerformNameCalculation method on the Model.
public ICommand PerformNameCalculation { get; private set; }
}
Notice that the PropertyChanged event is raised when the Name on the model changes. That way, regardless of whether the UserName setter was used, or the PerformNameCalculation command was used, the ViewModel stays in sync. The big downside to this is that you have to add many more events to your Model, but I've found that having these events in place is usually very helpful in the long run. Just be careful about memory leaks with events!
Imagine I have a UserControl that shows a parking lot (my favorite analogy) with cars of different colors. You can select a car, and in a separate UserControl (in a separate project) statistics of the selected car are displayed.
Now a user wants a button on the car statistics UC, 'Next car of same color'. When selected it should show the statistics of the next car (top to bottom, left to right) on the parking lot with that same color.
So if that makes sense, on to the question.
I am currently using MVVM Lite to send a message containing the selected car from the parking lot UC to the car statistic UC. All is good. Now though, with this new feature request, what should I do? The statistic UC needs to request the next car from the parking lot UC.
Would this be a good place to use dependency injection? Or is there another better approach?
If I am getting you right, what you want is a Command with proper CommandParameters.
public class Car
{
public Car(ParkingLot lot)
{
_parkingLot = lot;
}
public string Color { get; set; }
public ParkingLot ParkingLot
{
get
{
return _parkingLot;
}
}
private ParkingLot _parkingLot;
}
public class ParkingLot : ObservableCollection<Car>
{
public Car SelectedCar { get; set; }
public ICommand ShowNextCarCommand {
get
{
if (_showNextCar == null)
{
_showNextCar = new DelegateCommand(OnShowNextCar);
}
return _showNextCar;
}
}
private void OnShowNextCar()
{
string currentColor = SelectedCar.Color;
//Write proper logic to get the next Car. Here you got the currently selected car with you and the color
SelectedCar = this.NEXT(s => s.Color == currentColor); //Write the NEXT() logic
}
ICommand _showNextCar;
}
Now it is a matter of setting Button.Command="{Binding ParkingLot.ShowNextCarCommand}" now you got your control over to the ParkingLot viewmodel class and find the Next same colored car and set it again to SelectedCar property. I assume you will have RaisepropertyChanged in all these properties. I use simple DelegateCommand of Prism
I would use a Controller as mediator between the two ViewModels (ParkingLotViewModel and StatisticsViewModel). In your case the Controller is responsible to synchronize the selected car and it is responsible to pass the ‘Select next car of same color’ command to the ParkingLotViewModel.
The sample applications of the WPF Application Framework (WAF) show how this can work.
.
jbe
I'm struggling a little over naming classes for my MVVM application.
I have a TrainingCourse, which is called a TrainingCourseViewModel, but I can have many of these, so I have created a TrainingCourseViewManager to hold the list of courses and allow them to be added/removed. I also have an EmployeeViewController which has a reference to other view models as well as the TrainingCourseViewManager.
The EmployeeViewController essentially wraps all of the other view models and view managers and when its instantiated it gets the employee and in turn instantiates each of the view models and view managers.
The question is... What naming conventions are people using?
Should my TrainingCourseViewManager be called TrainingCoursesViewModel and should my EmployeeViewManager be called EmployeeViewModel?
Thanks
There might be a confusion over the role of view model.
Classes in your example (and in Orion's answer to that matter) seem more like actual data model. For example, it doesn't make sense for a view model to "hold the list of courses and allow them to be added/removed" - that's what data model should do. Add and remove operations on a view model wouldn't operate on the collection itself - instead, they would access and modify underlying data model.
Do properties of TrainingCourseViewModel class store actual data values, or wrap properties of some TrainingCourseDataModel class (with additional processing)? Or if you need to serialize data, would you serialize TrainingCourseViewModel objects? If former is true, you are binding directly to the data model, and there should be no 'ViewModel' suffix in names.
On the topic of naming conventions, if names become too complex, namespaces can help. For example:
namespace TrainingCourseView.ViewModel
{
class TrainingCourse {}
class Manager {}
class Controller {}
}
...
Data.TrainingCourse course;
new ViewModel.TrainingCourse(course);
Should my TrainingCourseViewManager be called TrainingCoursesViewModel and should my EmployeeViewManager be called EmployeeViewModel?
What are your window classes called? (what is your .xaml file called?)
The naming convention goes, that you create one ViewModel class per View (a view is a .xaml/.xaml.cs pair)
If you have a single window which displays a list of Employees and Training Courses, then you'd have something like this:
namespace Models
{
public class Employee : INotifyPropertyChanged { ... }
public class TrainingCourse : INotifyPropertyChanged { ... }
}
namespace ViewModels
{
// assuming you have TrainingWindow.xaml
public class TrainingWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<TrainingCourse> TrainingCourses
{ get{ return m_trainingCourses; } }
{ set{ m_trainingCourses = value; RaisePropertyChanged("TrainingCourses"); } }
...
}
// so on and so forth
}