I'm facing some design questions in my wpf MVVM (Prism based) application, would be happy to get your advice.
My model is very simple:
public class Customer
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
As you can see, I don't have any INotifyPropertyChnaged support for my Model class.
I also have ViewModel for the CustomerDetails screen, that support INotifyPropertyChanged.
public class CustomerDetailsViewModel:INotifyPropertyChanged /*Or NotificationObject*/
{
/*INotifyPropertyChanged event */
private Customer item;
public Customer Item
{
get{return item;}
set
{
item=value;
//Raise PropertyChanged
//Set IsDirty to true
}
}
}
In my view, i'm using binding to the Item.FirstName and my ViewModel being updated.
My problem is - since only the FirstName property is being updated via the View, and the Model itself does not support INotifyPropertyChanged, hence the Item setter not being called, and the IsDirty remains equal to false (and therefore does not update the IsDirty notification on the UI).
I know I can support INotifyPropertyChanged in the model, and then register to the Item.PropertyChanged event in the view model, and actually set the IsDirty to true, But -
Since I'm also using CodeFirst, and my Model class shared between my ServerSide and my client side (Not using Add Service Reference), I don't want to add the INotifyPreoprtyChanged stuff to my server side.
I'm considaring creating new project, that will use T4 templates to copy one by one all my Entities (as Customer) and adding INotifyPropertyChanged support to each and every model.
Is that something that seems reasonable or not? any other suggestions?
Thanks!
Option1.
Separate entities, which being transferred between client and server (DTO), from entities, which are models on the client side. Implement INPC in models. Use mapping between these entities.
Option2.
Bind view to view model properties only. Make view model properties, which wrap corresponding model properties.
Option 3.
Is a mix of first two options. Do not aggregate model in view model. Use mapping between model and view model. Make view model properties, which correspond to model properties.
Well your approach is simply not the best. Much better would be to use a VM like this
public class CustomerDetailsViewModel : INotifyPropertyChanged
{
public CustomerDetailsViewModel(Customer customer)
{
_item = customer;
}
private Customer _item;
public string FirstName
{
get { return _item != null ? _item.FirstName : null; }
set
{
if (_item == null)
_item = new Customer(); // just an example, probably it's not the desired behavior
_item.FirstName = value;
RaisePropertyChanged(...);
}
}
...
}
This would stick to the spirit of MVVM.
If you want your UI to notice when your model property changed, your model class MUST implement INotifyPropertyChanged and similar MVVM interfaces (IDataErrorInfo, etc...) in order to Notify to the UI that the property changed.
That's because you are not always updating your model from the the viewmodel, where you must implement INotifyProperyChanged and notify for changes.
Wrapping corresponding model properties in the viewmodel used when you cannot implement INotifyPropertyChanged in the model class, which makes the viewmodel to grow VERY fast and creates unnecessary code duplication.
Scenario for example:
public class Customer
{
public string FirstName {get;set;}
public string LastName {get;set;}
// Changes the first name.
public void ChangeFirstName(string newName)
{
FirstName = newName;
//The UI will never know that the property changed, and it won't update.
}
}
Solution:
Implement INotifyPropertyChanged in you model class, create backing fields to your properties, and for each property setter, AFTER the set operation, raise OnPropertyChanged invoked method with the property name.
If you don't like to clutter your model with INotifyPropertyChanged code you could try using a NUGet package called PropertyChanged.Fody
You can use it like this;
using PropertyChanged;
[ImplementPropertyChanged]
public class Customer
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
Any public property in this class will now support INotifyPropertyChanged
I think you are on the right track. In the server side, you do not need INotifyPropertyChanged, thus do not add it to the domain classes in the server side.
You may just add some build symbols such as "WPF" to your client projects; and in the code first definitions implement INotifyPropertyChanged only if there is "WPF" build symbol. Then just add your server side domain classes as links to your presentation application. Something like;
#if WPF
public class MyEntity : INotifyPropertyChanged
#else
public class MyEntity
....
Related
I am using MVVM with ReactiveUI. I have a property in the model that I want to display and be able to edit in the UI. Is there any simple way to do this using ReativeUI? The following properties should be fulfilled:
The model property implements INotifyPropertyChanged
The model property can be changed from the view model or from the model
Updates from within the model can be made on any thread
Updates from the view model should use a throttle so that not every keystroke becomes a model update
The application can be run with a UI or command line only, and the code should also be runnable in unit tests and integration tests
When using an UI, the PropertyChanged event of the ViewModel needs to be raised on the UI thread
The throttle can't be blocking in either run mode
The code should be robust, i.e. not have the risk of causing deadlocks or reverting back to old values.
I somehow imagined this would be a standard case of how to wire a view model to a model but haven't managed to get this to work, and can't really figure out any way to make it work without quite a lot of code for a seemingly simple task.
Sample code of a non-working implementation:
public interface IModel : INotifyPropertyChanged
{
string MyProperty { get; set; }
}
public class SomeViewModel : ReactiveObject
{
private readonly IModel model;
public SomeViewModel(IModel model)
{
MyProperty = String.Empty;
this.model = model;
var inputThrottleTime = TimeSpan.FromMilliseconds(500);
var scheduler = RxApp.MainThreadScheduler is AvaloniaScheduler
? RxApp.MainThreadScheduler
: RxApp.TaskpoolScheduler;
// This doesn't work. If updates are made in the model inputThrottleTime apart, the old value might be reassigned to the model.
// And also, WhenAnyValue shouldn't be used to listen on properties that might be updated on background threads according to ReactiveUI devs.
this.WhenAnyValue(x => x.model.MyProperty).ObserveOn(scheduler).Subscribe(p => MyProperty = p);
this.WhenAnyValue(x => x.MyProperty).Skip(1).Throttle(inputThrottleTime, scheduler)
.Subscribe(p => model.MyProperty = p);
}
[Reactive] public string MyProperty { get; set; }
}
You can use data binding. Chose your control component in view(xaml). After that you give a datacontext for this view. There are different ways for giving data context and item source for controls. You can do this in xaml file like this:
<Window.DataContext>
<vm:ViewModels.MainWindowViewModel />
</Window.DataContext>
<TextBlock Text="{Binding Name}"/>
After that you can access variables. This variables must have get set properties and you can use ReactiveUI in here.
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
I used this in Avalonia. For more information you can look this: https://docs.avaloniaui.net/docs/data-binding
Lets say I have two views: FileList and Editor.
They both have their own view models, bound using a DependencyProperty.
The view models look somewhat like this:
public class FileVM : INotifyPropertyChanged
{
public string FileName { get; set; }
}
public class FileListVM : INotifyPropertyChanged
{
public ObservableCollection<FileVM> Files { get; set; }
public FileVM SelectedFile { get; set; }
}
public public class EditorVM : INotifyPropertyChanged
{
string FileName { get; set; }
}
Imagine that they properly implement INotifyPropertyChanged.
Now I don't want FileListVM and EditorVM to know about each other. They could just as easily exist independently. But in one use case I would like to bind FileListVM.SelectedFile.FileName to EditorVM.FileName. Whenever the selected file or the filename changes, so should the filename in the editor. And whenever the editor changes the filename in the editor, so should the name of the selected file.
Now here is my question: how can I do this cleanly? I'm guessing the solution lies somewhere in their parent view model (e.g. MainVM), since that view model is aware of the connection between the two child view models. But I'm not sure if I can connect the two through xaml, and I'd like to avoid writing property changed event handlers if possible.
Please look at:
Communicate between View Models in MVVM
I advise to use MVVM frameworks like Galasoft because they have useful functionality and often do life with MVVM easier ;)
If you do not like using them, you can add event SelectedFileChanged in one VM and subscribe it from the other one.
I have a WPF application which is written with an implementation of MVVM. There's no extra framework for the MVVM pattern.
My entities from EF db first are wrapped in their own viewmodels and I have a modelcontroller to load them into their viewmodels from a 'window' viewmodel.
Example of an entity viewmodel:
public class PurchaseOrderViewModel : ViewModels.ViewModelBase
{
private someType _prop;
public someType Prop
{
get
{
return _prop;
}
set
{
_prop = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public PurchaseOrderViewModel() {
// default constructor for LINQ
}
public PurchaseOrderViewModel(purchaseorder entity)
{
// load values from entity in properties
}
}
Example of a window viewmodel:
public class MainViewModel: ViewModels.ViewModelBase
{
private IModelController modelController = new ModelController();
private List<PurchaseOrderViewModel> _poList;
public List<PurchaseOrderViewModel> POList
{
get
{
return _poList;
}
set
{
_poList = value;
OnPropertyChanged();
}
}
// ...
// Other Properties
// ...
public MainViewModel()
{
POList = modelController.GetPurchaseOrders();
}
}
Example of ModelController:
public class ModelController : IModelController
{
public List<PurchaseOrderViewModel> GetPurchaseOrders()
{
using (var model = new DBContext())
{
return model.purchaseorders
.Select(new PurchaseOrderViewModel { /* assign properties */ })
.ToList();
}
}
}
Where am I supposed to save this wrapped viewmodel (PurchaseOrderViewModel) once the user is done editing it? As I see it, there are 2 options:
Create a save function in each viewmodel that points back to the modelController, but this feels like an inappropriate approach.
Create a save function in the modelcontroller and pass the viewmodel as an argument
It's most likely that I'm missing something in the MVVM pattern, but please point me in the right direction. Thank you!
EDIT: I excluded the view (MainView) from the info provided, but this view binds directly to the properties exposed by MainViewModel.
First up, I problably wouldn't name it ModelController as that's slightly confusing makes people think you are speaking MVC. Instead, if you call it xxxxService (e.g. PurchaseOrdersService) it makes more sense and it no longer feels "inappropriate" because having a VM delegate the actual work is what many users of IoC do. Plus it keeps your VM clean.
NOTE: By "service" I don't necessarily mean that your VM will be calling a WCF service directly (nor should you). Service is just a means to achieve something in an abstract and encapsulated way on behalf of clients. Examples include:
saving information to a DB
getting the current log mechanism
They can even be facades whereby they create a WCF client proxy and call a remote service on your behalf without you having to know the details.
So a typical flow is:
Command >> View code behind >> VM >> Service
The reason I include the view's code behind is that typically this is where you:
Catch exceptions
The starting point of async/await for asynchonous calls to your VM and service
Now when you pass context fromt the VM back to the service, there is no rule on what exactly you pass however I see no reason to pass VM to the service because that contains information the service doesn't care about.
Just pass the M which your VM should have bound to in the first place and continued to update via binding.
I am new to MVVM pattern and Caliburn.Micro. I've read some tutorials on how to get started, but I'm confused about the Model part of MVVM in the context of Caliburn.
I want to create my first MVVM application and I have some design questions:
In tutorials, the Model was presented as simple property in
ViewModel. How should I manage more complex models? Is there any
naming convention? Obviously, there should be some external classes
made for my models, but how should I communicate between my models
and the view?
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be
visible from different ViewModels?
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Thanks in advance for any help :)
EDIT:-------------------------------------------------------
Thank you for your anwser. I uderstand the topic more clearly, but I'm still confused about some details.
For an example, let's assume this little application. I have a form that allows me to add a new Customer. It has a few fields like Name, Surname etc.
After pressing the button, I invoke the addCustomer command in the ViewModel. I want my program to store the newly created customer inside the database.
My view also has the List control (whatever), which displays my customers as raw strings (like "Name: John, Surname: Doe, Address: ..." I know it's dumb to make it like this, but i need an example of model manipulation (like .toString()))
For this example, I've created a bunch of stuff to illustrate my vision of that process:
fields - it's a set of form fields like Name, Surname etc.
customerSet - it's a set of Customer class to store all created
customers
.addToDatabase(fields) - a method which puts newly created customer
to the database
.getStrings - a method which prepares a set of strings to be
displayed by the list in CustomerView
I think about 2 approaches that would be good for a solution:
First approach. I don't like this one. The only advantage is, that
ViewModel handles all the logic inside application. Sharing model
would be a serious problem here, because saving methods are bound to
the ViewModel class.
Second, MVC like approach. To me it's the most intuitive one. But - I
don't know where should I store CustomersModel object, so few
ViewModels could have access to it.
Which is the better one? Or maybe another approach that is more suitable for MVVM?
Another problem is: Where should I put my method that will load all the Customers from the database, so they could be displayes on the list? In "get method" inside viewmodel, or inside a model class?
In tutorials, the Model was presented as simple property in ViewModel.
How should I manage more complex models? Is there any naming
convention? Obviously, there should be some external classes made for
my models, but how should I communicate between my models and the
view?
Your models should represent whatever it is they need to whether it's a customer, account, etc. The view models job is to handle the interaction between the view and models.
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Generally, you will map complex models to more friendly format for display, you can do it manually or use a tool like AutoMapper.
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be visible
from different ViewModels?
If you're working with a local db you can pass IDs around. If it's a service you could persist the model locally for other view models to work with. You could also inject a singleton, ISharedData, into view models that need to work with shared data.
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Create services for more complex model manipulation / business logic. Inject the services into view models that require them. ICustomerService, IAccountService, etc.
EDIT:-------------------------------------------------------
You're first approach is correct. To your point about sharing the model being a serious problem because saving methods are bound to the view model class. The view model will have a SaveCustomerCommand that is fired when the button is clicked, because of its binding.
The SaveCustomerCommand will persist the CustomerModel, regardless of how the CustomerModel is persisted. So if its a database, the view model might have a reference to a context and issue a _db.Save(CustomerModel). If another view model needs to manipulate a CustomerModel, it will do so by using the context. The view model could also have a reference to a CustomerService that handles the crud for the CustomerModel.
Here's how this might look:
public class AddCustomerViewModel : Screen
{
private readonly ICustomerService _customerService;
public AddCustomerViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
//If button is named x:Name="SaveCustomer" CM will
//bind it by convention to this method
public void SaveCustomer(Customer customer)
{
_customerService.Save(customer);
}
}
public class CustomerListViewModel : Screen
{
private readonly ICustomerService _customerService;
private List<CustomerDisplayModel> _customers;
public CustomerListViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
public List<CustomerDisplayModel> Customers
{
get { return _customers; }
set
{
_customers = value;
NotifyOfPropertyChange();
}
}
//only fires once, unlike OnActivate()
protected override void OnInitialize()
{
var customers = _customerService.LoadAllCustomers();
//could just use the model but this shows how one might map from
//the domain model to a display model, AutoMapper could be used for this
Customers = customers.Select(c => new CustomerDisplayModel(c)).ToList();
}
}
public interface ICustomerService
{
List<Customer> LoadAllCustomers();
void Save(Customer customer);
}
//same as button, Label named x:Name="CustomerName" will bind
// to CustomerName
public class CustomerDisplayModel
{
private readonly Customer _customer;
public CustomerDisplayModel(Customer customer)
{
_customer = customer;
}
public string CustomerName
{
get { return _customer.Name; }
set { _customer.Name = value; }
}
public string Surname
{
get { return _customer.Surname; }
set { _customer.Surname = value; }
}
public string Address
{
get { return _customer.Address; }
set { _customer.Address = value; }
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Address { get; set; }
}
I've been working on an MVVM application in C# but consistiently run into some problems when working with the collections of ViewModels my View digests. Specifically, they all tend to relate to the issue of the Model being a private member of the ViewModel.
An example of this is creating new ViewModels (as requested by the View). For some preamble (although you might not need these to help me) here are example Model and ViewModel classes:
Private Class Model()
{
public string Name { get; set; }
}
Public Class ViewModel()
{
Private Model _Model;
Public Void ViewModel(Model model)
{
_Model = model;
}
Public String Name
{
get
{
return _Model.Name;
}
set
{
_Model.Name = value;
}
}
}
The entire model is never directly exposed as a public member of the ViewModel. The MainWindowViewModel handles collections of Models (private, the view cant see these) and ViewModels (public for View digestion):
Public Class MainWindowViewModel
{
Private List<Model> _NamesModel;
Private ObservableCollection<ViewModel> _NamesViewModel;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
foreach(Model model in _NamesModel)
{
ViewModel viewmodel = new ViewModel(model);
_NamesViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
Now thats the preamble but now I have a problem. How do I add a new ViewModel? Do methods within my view create a new ViewModel and populate that? Being a purist, I'm assuming the View should not be allowed to create or populate Models at all. Should my ViewModel contain a constructor that accepts nothing (i.e. no underlying model) and instead creates a blank to populate?
These kinds of issues keep coming up with a "pure" MVVM approach. I've had to create a public method in my ViewModel (bool compare(Model model)) that will compare a model (ready for deletion etc.) to it's internal one. If the models were publicly exposed (breaking purity) then it would be much easier to do stuff like find the ViewModel thats connected to a Model.
I can sympathize with some of those problems. I recently wrote an MVVM application where similar questions came up frequently. One of the tricks is to decide - definitively - which class is going to be responsible for Model instances. Do you want it to be your MainWindowViewModel? Or your NameViewModel? You don't want to share the responsibilities of creating/deleting the model between both of those classes; you'll have quite a logistical nightmare.
Secondly, even a "pure" MVVM approach doesn't dictate that you can't expose the model publicly. You said yourself that doing so would save you a lot of headache: DO IT. MVVM dictates only that the ViewModel has no knowledge/access of the View. There are many "official" MVVM examples that go so far as to implement their Model using the INotifyPropertyChanged interface, and bind directly to properties on the Model.
Personally, I think I would dictate control of the NameModel to the NameViewModel. This means that you should remove the list of NameModels completely from the MainWindowViewModel. If you want to give the NameViewModel an optional constructor which takes a Model, that would be fine too.
I'm a fan of this approach:
public NameViewModel : ViewModelBase
{
public NameModel Model
{
get { /* get stuff */ }
set { /* set stuff */ }
}
// Default constructor creates its own new NameModel
public NameViewModel()
{
this.Model = new NameModel();
}
// Constructor has a specific model dictated to it
public NameViewModel(NameModel model)
{
this.Model = model;
}
//Model wrapper properties
public String Name
{
get { return Model.Name; }
set { Model.Name = value; }
}
}
and...
public class MainWindowViewModel
{
Private ObservableCollection<ViewModel> _NameViewModels;
Public Void MainWindowViewModel()
{
//Lets pretend we have a service that returns a list of models
var nameModels = Service.Request();
foreach(Model model in nameModels)
{
ViewModel viewmodel = new NameViewModel(model);
NameViewModel.Add(viewmodel);
}
}
Public ObservableCollection<ViewModel> NameViewModels
{
get
{
return _NameViewModels;
}
}
}
In this way your MainWindowViewModeldoesn't keep an entirely separate copy of the Models; it only tracks the NameViewModels. Each NameViewModel is responsible for its own underlying model, while still making the option available to have a specific model passed to it during construction.
All the creation-related issues can be resolved with introduction of factory design pattern. The factory will take care of creating view models basing on model that was provided.
public class MainWindowViewModel
{
private List<Model> _NamesModel;
private ObservableCollection<ViewModel> _NamesViewModel;
private IViewModelFactory factory;
public void MainWindowViewModel(IViewModelFactory factory)
{
//Lets pretend we have a service that returns a list of models
_NamesModel = Service.Request();
_NamesViewModel = factory.CreateNamesViewModels(_NamesModel);
}
public ObservableCollection<ViewModel> NamesViewModel
{
get
{
return _NamesViewModel;
}
}
}
What is more, you could even get rid of Service dependency in view model and move it to the factory itself, thus reducing the need to keep model in view model (admittedly though, removal of model might not work in more complex scenarios):
public ObservableCollection<ViewModel> CreateNamesViewModels()
{
var models = Service.Request();
return new ObservableCollection(models.Select(m => new ViewModel(m)));
}
Also, your main window view model can expose commands that utilize factory to create any new instances. This way, no model is leaking to view and also no creation details are exposed (since commands will hide actual implementation).