When I read this line:
The viewmodel exposes not only models, but other properties (such as state information, like the "is busy" indicator) and commands.
http://www.codeproject.com/Articles/100175/Model-View-ViewModel-MVVM-Explained
I am wondering whether current settings on the view should be considered a property of the view or part of the state and placed in a view model.
For example I currently have a preferences window bound to a view model containing the current colors with a bunch of color pickers bound to the view model. Commands then cancel changes and reapply the old settings if ok or cancel are clicked.
Should the current colors be properties on the view instead or is their current location ok?
public class PreferencesWindowViewModel:DependencyObject
{
private DelegateCommand updatePreferencesCommand;
public ICommand UpdatePreferencesCommand
{
get { return updatePreferencesCommand; }
}
private DelegateCommand cancelCommand;
public ICommand CancelCommand
{
get { return cancelCommand; }
}
public Color HighValuePenColor
{
get { return (Color)GetValue(HighValuePenColorProperty); }
set
{
SetValue(HighValuePenColorProperty, value);
}
}
I like them to be on the view model. For the simple reason that this functionality is testable in a unit testing harness. If they were on the view you would have to jump through so many more hoops to get it tested. In my opinion, the view model should contain most of the presentation logic. Colors and their relation to certain application states belong in that category of code.
Related
I have an application which holds objects in a list view. I want to enable user to change those objects using modal dialogs when an item is double clicked in the list view.
I am using the mvvm light toolkit. When I double click an item in the list view I know which object and therefor which model type is chosen. I am retrieving the corresponding ViewModel via a ServiceLocator and I am using Execute to "launch" the viewmodels' relay command passing the model as object with the needed data information. However, now, in the ViewModel, I am struggling how to open the corresponding view as a model dialog bound to the ViewModel?
Edit (add some code fragments)
public class ViewModelLocator
{
public ViewModelLocator
{
SimpleIoc.Default.Register<OptionSpecificViewModel>();
}
public OptionSpecificViewModel OptionSpecificView
{
get
{
return ServiceLocator.Current.GetInstance<OptionSpecificViewModel>();
}
}
}
the view locator works fine
public class MyListViewManager
{
public void CallMyDialog(Guid xxx)
{
var objModel = GetMyModelByGuid(xxx);
var vm = CommonServiceLocator.ServiceLocator.Current.GetInstance<ObjectSpecificViewModel>();
vm.EditCommand.Execute(objModel);
}
}
the "ListViewManager" works also
My Problem is, that I am in the correct ViewModel with the correct model (data).
public class OptionSpecificViewModel : ViewModelBase
{
public OptionSpecificViewModel()
{
InitRelayCommands();
RegisterMessages();
}
...
public void OnEditCommand(object model)
{
// I reach here in the correct view model with the correct model
// but how can I open the view here??
}
}
As far as I understood MVVM, the view model does know the model and the view has a "connection" to the ViewModel via binding.
But the ViewModel does not know the view. so how do I start the view?
I have a view in wpf, that has a range of different boxes, for example, First/Last Name (TextBox), Date of Birth (DatePickers), Marital Status (ComboBox) etc.
What I want to be able to do, is get the text entered into the TextBoxes and show them in a TextBlock on a seperate view.
I have added properties for all the corresponding items in there retrospective ViewModels, but from there on in, I'm unsure on how to implement this any further.
Other questions I have looked at aren't very clear or easy to follow.
You simply need to set the DataContext of both views to the same instance of your ViewModel.
<StackPanel>
<Local.EditableView DataContext={Binding Person} />
<Local.ReadOnlyView DataContext={Binding Person} />
</StackPanel>
There are multiple ways to achieve this. I assuming you are not using any framework like Caliburn.Micro.
Simple Approach:
Create a global static class that shares information across multiple ViewModel.
Now, from the first ViewModel, update the static class property using the ViewModel property setter, something like
private string _lastName;
public string LastName{
get{
return _lastName
}
set{
_lastName = value;
SharedClass.LastName = value;
}
}
Now access this shared class from the other ViewModel.
One approach is to use a Mediator to communicate between view models.
You would typically register a "target" view model -- "colleague" -- with the mediator for certain operations that the view model is interested in and provide a callback action for what is supposed to happen when that operation occurs. Then the other view model -- the one performing the operation that the target is interested in -- would notify the mediator when the operation happens, and the mediator would then perform the associated action on all the colleagues that are registered for that operation.
Here's an example of a mediator:
static class Mediator
{
private static Dictionary<string, List<Action<Object>>> _tokenCallbacks
= new Dictionary<string, List<Action<object>>>();
internal static void Register(string token, Action<Object> callback)
{
token = token.ToLower();
if (_tokenCallbacks.ContainsKey(token))
{
var l = _tokenCallbacks[token];
var found = false;
foreach (var existingCallback in l)
{
if (existingCallback.Equals(callback))
{
found = true;
break;
}
}
if (!found) l.Add(callback);
}
else
{
var l = new List<Action<Object>>(new[] { callback });
_tokenCallbacks.Add(token, l);
}
}
internal static void NotifyColleagues(string callbackToken, Object args)
{
callbackToken = callbackToken.ToLower();
if (_tokenCallbacks.ContainsKey(callbackToken))
_tokenCallbacks[callbackToken].ForEach((x) => x(args));
}
}
Those views and their view models should reference a shared model of the data.
So that when data is entered in one view, its view model updates the model and the model update triggers a update in the other view model and finally in the other view.
If you have lots of cross viewmodel communication, use Messenger. That acts as a mediator and simplified lots of issues like this. You can either implement one yourself or use either the MVVM light or Prism toolkits.
For my project I need to know which View is using my ViewModel
So i created this ViewModel:
public class HistoriqueViewModel : INotifyPropertyChanged
{
public HistoriqueViewModel(MetroWindow view)
{
this.MetroWindow = view;
this.ExportCommand = new RelayCommand(Export_Ex);
}
private MetroWindow _metroWindow;
public MetroWindow MetroWindow
{
get { return _metroWindow; }
set
{
if (Equals(value, _metroWindow)) return;
_metroWindow = value;
OnPropertyChanged();
}
}
//.........
}
And in the View constructor:
public partial class ViewHisto : MetroWindow
{
public ViewHisto()
{
InitializeComponent();
DataContext=new HistoriqueMV(this) ;
}
}
It Work perfectly for me but I want to know if this Break the MVVM Pattern?
Yes, this breaks MVVM. A properly constructed view model shouldn't care about what the view is.
Nothing in your code really suggests why you are passing that reference (other than exposing the view as a public property, which is an even bigger no-no) but there are several ways around it:
Pass the view as an interface and hold/expose that
Use a mediator to pass whatever messages necessary between the view model/view
Have the view invoke whatever methods it needs on the view model, and have the view model raise events that the view can listen to.
Any of the above approaches will provide far better decoupling than the one you are going with.
One other thing, its "View Model", not "Model View"
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 have created an application which uses WPF and MVVM following this article from CodeProject.
I have a view, TVSeriesView, which has a TVSeriesViewModel. These two are connected using a DataTemplate which is done following the article.
<DataTemplate DataType="{x:Type Implementation:TVSeriesViewModel}">
<TVSeriesLibrary:TVSeriesView />
</DataTemplate>
The idea is to pass my model, the TVSeries, to this ViewModel as I have a property named TVSeries in the ViewModel. When this property is set, I will populate other properties such as Title, Cover and so on. These properties are meant to be binded to controls in the view.
public class TVSeriesViewModel : ViewModelBase, ITVSeriesViewModel
{
private TVSeries _tvSeries;
private string _title;
private ImageSource _cover;
public TVSeries TVSeries
{
get
{
return this._tvSeries;
}
set
{
this._tvSeries = value;
}
}
public string Title
{
get
{
return this._title;
}
set
{
this._title = value;
OnPropertyChanged("Title");
}
}
public ImageSource Cover
{
get
{
return this._cover;
}
set
{
this._cover = value;
OnPropertyChanged("Cover");
}
}
}
First and foremost, does this sound like the right way to do it?
Next, does anyone know how to pass a parameter (a TVSeries object) to the ViewModel when the TVSeriesView is shown?
And lastly, does anyone know how I can directly access resources in the view? For example if I don't want to use data binding but instead want to set the image directly like this:
myImage.ImageSource = myImageSource
The View and ViewModel together are one of the possible representations of the Model.
You can pass a repository handle which would be eventually responsible for data access or
Concrete/abstract object of Model through Dependency Injection via Constructor or
Dependency Injection, via property/method or
In more crude way you can write a DB access code in your VM (obviously it's not suggested.)
I would prefer as the order given here. Your code is doing the third option.