I am trying to synchronize data between seperate Views in MVVM.
Here is the scenario:
There're two ViewModels and two Views respectively:
ViewModel1: contains a list of "Person" object, named "People", which is defined in Entity Framework and is retrieved via WCF Service.
View1: contains an ItemsControl wich is bound to "People" of ViewModel. Item's color may vary depending on its value, for example: person who is over 60 years old may appear red color, while person under 18 years old may appear green. This is achieved by a Value Converter.
(there is a button named "Show Detail Info" in View1, when it's clicked, a ChildWindow dialog is poped up and detail information of currently selected "Person" is displayed )
ViewModel2: contains a "SelectedPerson" object, and implements a "Save" method.
View2: contains several input fields bound to corresponding fields of "SelectedPerson" object, such as TextBox for "Person.FirstName", DateTimePicker for "Person.Birthday", RadioButton for "Person.Gender", etc.
Problem:
when i changed some fields (i.e. "Name" field) and clicked “Save” button, i can even see that changes have been committed onto Database. However, the corresponding item in View1 failed to update its color.
Is there better way to fix this problem?
You can adress such problems usually in three ways depending on your setup:
1 & 2) If the 2 VMs are instantiated by the same parent object these can be connected via INotifyPropertyChanged or Events for the updated Properties
3) If they are disconnected you can use an EventAggregator to message between the VMs. CaliburnMicro has a pretty good implementation but you can build one yourself with just two classes. For more information see: Caliburn.Micro Soup to Nuts Part 8–The EventAggregator
It's hard to answer without knowing exactly how your SelectedPerson and your People list are related. So I assume that SelectedPerson is one item out of your People list. If this is the case, your problem is that your view control that displays the colour depending on the persons age doesn't get informed about the value change.
This information is usually done with a view model that implements INotifyPropertyChanged. Each time a property changes, in your case the property Age of your PersonViewModel, the PropertyChanged event is raised and thus all bound items know about the value change and therefore will requery their values.
Related
As far as I know, the default way to use a ObservableCollection that is bound to a listview is with model classes as elements (ObservableCollection<MyModel>). So when a listview element is selected, we use NavigateAsync and pass the model, which then can be used by the ViewModel to "fill itself".
The problem with this approach is, that it's not possible to use ViewModel properties for binding in the listview.
For example:
I have a View, ViewModel and Model "PickList", which contains a collection of "PickLine" objects - each having a View, ViewModel and Model themselves. The PickLine object contains a property "PickedQuantity" and a property "OpenQuantity". Now in my PickList view, I don't want to bind these two to separate items (e.g. two labels), but I want to have one label to display both I a format like for example "PickedQuantity / OpenQuantity". I know this example can be solved by using multi binding or something like this. But that's not the meaning of it all.
My PickLine ViewModel already has a property "QuantityString", that I want to bind to the label of a listview element via DataTemplate. But how can I do this. Is it even possible?
Make a property that combines the two other properties and bind to that. E.g.:
public string FullQuantity {get {return $"{PickedQuantity} / {OpenQuantity}";}}
Then in the setter for PickedQuantity and OpenQuantity, you will want to call whatever PropertyChanged method you have set up to notify the bindings of a property change and pass in the FullQuantity property name so elements that are bound to FullQuantity get updated when either PickedQuantity or OpenQuantity are changed.
This way, you are only binding one label's text to one property and that label would get updated when either of the two quantity properties are changed.
Note: I am unfamiliar with Prism, but this approach should work regardless of the Mvvm framework in use.
Your PickListViewModel should expose a collection property whose items are of type PickLineViewModel (not PickLine).
Whether you need an ObservableCollection<PickLineViewModel> depends on where changes can happen - in service / model that initially created the PickLines or in the GUI or both. In any way, you have to make sure the changes are propagated from one side (the collection of view models) to the other (the collection of models). Google wrapping observable collection as a starter (hint: avoid two-way sync if possible). These blog posts are old but still relevant and make a good reading. A trivial wrapping is described in this answer.
I'm trying to learn some Qt programming (C++) and for everything UI-related I come from C# with WPF/MVVM. The problem is that I'm having problems switching the reasoning process behind my choices.
What I'm trying to do is to link a list (or vector or some kind of ObservableCollection) of Objects to a list of Buttons so that the UI will display a button for each element in the list.
Let's say that I have a Customer class (which in C# would be my Model) with 2 variables (plus get/ set methods, or "Properties" as they are called in C#): Name and Type. Each button will display the corresponding Name and the click will send the Type to the method that handles the call.
Now, I cannot have access to a machine with VS these days but in C# I would do something like creating a View and a ViewModel (i.e. the Controller). In the VM I would create an ObservableCollection of Customers that raises the RaisePropertyChanged event when it's modified and in the View I would create an ItemsControl binded to the ObservableCollection with as DataTemplate the Button. The button would have its Content property binded to the Name of the Customer and with CommandParameter (i.e. the parameter sended with the click event) the Model itself or the Customer Type, to make different choices based on its value. This way changing the ObservableCollection would modify the number of Buttons showed.
Now my problem is: how to do the same in Qt? The best I came up with is to create a vector of this Customer class and in a for cycle:
for (unsigned int i = 0; i < model_vector.size(); ++i)
{
QPushButton* btn = new QPushButton(this);
btn->setText(model_vector[i].Name);
ui->verticalLayout->addWidget(btn);
connect(btn, SIGNAL (released()),this, SLOT (handleButton(model_vector[i])));
btn->show();
}
I would put this cycle in a method that is called to update the model_vector so I would clear the verticalLayout and re-add all the Buttons that are contained in the vector at the moment.
Anyhow this doesn't seem to me a real Model/View approach (in the sense I read on the Qt docs), but maybe I'm just misunderstanding how Qt works.
Is what I'm doing correct or is there a better way?
Thanks!
Your original approach sounds a lot like what you would do with a QtQuick based UI.
A model would provide the data, a QtQuick ListView (or similar) would provide the general view capability and mediate between the model and the actual entry visualizations, called delegates.
The delegate in this case would be a button.
I have multiple of views (user controls), each with its own ViewModel. To navigate between them I am using buttons. Buttons display image and text from corresponding view model and also need column and row (because there are like 10 views: 10 columns with different number of rows each).
Right now buttons are created dynamically (I made a Navigator control for this) and for view models I have base class to hold text, image, column and row. Number of views available will be different (depends on user level and certain settings), that's why it's I need control here.
Question: how shall my control get data from view models?
Right now I have interface INavigator, defined in (lol) control itself. And view models implement it. I could go opposite, let my control to know about view models. Both looks wrong.
There is a single Navigator control what has, lets say, Items bound to a list of view models. It can cast each view model to INavigator or ViewModelBase (common for all pages) to obtain specific view model image, text, column and row. So either view model knows about control (to implement INavigator) or control knows about ViewModelBase.. And this is a problem, both solution bind tight control and view models, which is bad in mvvm.
Schematically
The way you've drawn your diagram answers your own question as to how you should structure the code for this.
What you need is one VM (let's call it MainVM) which contains an ObservableCollection<VMBase> of the other VMs (using your base type so that they can all happily live in the same collection).
Your View needs an ItemsControl (bound to your ObservableCollection<VMBase>) where you specify a DataTemplate for the Button using the properties exposed by the VMBase type only. Set the Command property in the Button to call SwitchCommand, CommandParameter is set to the item itself (i.e. {Binding .}).
Your View also needs a ContentControl bound to a SelectedVM property on MainVM which you can populate.
Implement SwitchCommand to set the SelectedVM property based on the value from the CommandParameter.
public void ExecuteSwitchCommand(object parameter)
{
var vmBase = parameter as VMBase;
if (vmBase != null)
SelectedVM = vmBase;
}
All properties mentioned here should be INotifyPropertyChanged enabled so that the View registers when they change and updates the UI.
To get the different UIs for the ContentControl, add type-specific DataTemplates for each of your specific VM types to the Resources file of your View (or if you're smart and are building a custom plug-in framework, merge the Resource Dictionaries).
A lot of people forget with MVVM that the whole point is that there is a purposeful separation of View from ViewModel, thus meaning you can potentially have many Views for a single ViewModel, which is what this demonstrates.
I find it's easiest to think of MVVM as a top-down approach... View knows about it's ViewModel, ViewModel knows about its Model, but Model does not know about its ViewModel and ViewModel does not know about its View.
I also find a View-first approach to development the easiest to work with, as UI development in XAML is static (has to be).
I think a lot of people get to wrapped up in 'making every component (M, V, VM) standalone and replaceable', myself included, but I've slowly come to the conclusion that is just counter-productive.
Technically, sure you could get very complicated and using IoC containers, create some ViewLocator object which binds a View-type to a ViewModel-type, but... what exactly does that gain you besides more confusion? It makes it honestly harder (because I've done this at one point) to develop because now you've lost design-time support first and foremost, among other things; and you're still either binding to a specific view model interface in your view or creating the binding at run-time. Why complicate it?
This article is a good read, and the first Note: explicitly talks about View vs. ViewModel. Hopefully, it will help you draw your own conclusions.
To directly answer your question, I think having your ViewModels implement an INavigator interface of some sort is probably ideal. Remember your VM is 'glue' between your view and model/business logic, its job is to transform business data into data that is consumable by your views, so it exists somewhere between both your UI and business layers.
This is why there are things like Messengers and View Services, which is where your navigator service on the ViewModels can fit in nicely.
I think the design has led to a no way out situation.
I believe that creating a custom button control where the dependency properties tie the image, the row and column actually provide a way for the page, which it resides on ,to get that information to them; whether they are dynamically created or not.
Continuing on with that thought. There is no MVVM logic applied to a custom control, the control contains what it needs to do its job and that is through the dependency properties as mentioned. Any functionality of the button should be done by commanding; all this makes the button data driven and robust enough to use in a MVVM methodology or not.
Question: how shall my control get data from view models?
There should only one viewmodel which is the page the control resides on. The control is simply bound to information which ultimately resides on that VM. How it gets there, that is up to the programmer. If the button is going to contain state data, that is bound from its dependency property in a two way fashion back to the item it is bound to.
By keeping VMs out of the buttons and only having one VM that is the best way to segregate and maintain the data. Unless I am really missing something here....
Same as others here I find it a bit hard to actually understand what you are asking, so this is quite general. The answer to the question header is simply: the Control gets the data from the ViewModel through bindings, always. You set the DataContext of your Control to the corresponding ViewModel, and from there you keep the ViewModel and the Control synchronized:
If you add an ItemsControl containing buttons to the View, you add an ObservableCollection<ButtonViewModel> to the ViewModel and bind the ItemsSource of the ItemsControl to this.
If you allow the user to dynamically add content to the View, the actual code that does it resides in the ViewModel, e.g. when the user clicks on a button "Add Button", you use the Command property to call a ViewModel method that adds a ButtonViewModel to the collection and the View will automatically reflect your changes.
There do exist complicated cases that are impossible to code exclusively in the ViewModel, I have found Behaviors to be the missing link there, but I'll get into that when you show me the specific case.
If you'd like to get a working example, please provide as much code as you can, with your exact expectations of what it should do.
This question is quite tricky to word, so please do ask if my explanation is lacking.
I have an application that has several datagrids which contain editable objects, for example containers, shipments and packages.
Now each of these objects (shipment, container, package etc.) often require knowledge of the selected item in another datagrid (for example, package needs to know what container is selected in the containerviewmodel) usually i would fire an event when selectedItem changes and listen for those events on the viewmodels that require this information, however recently changes have been made which require models to know about selections.
So my question is, would it be "bad practice" or bad code wise to have a single class which contains all the currently selected items from all viewmodels, and simply listen to events in that single class, which is then used by viewmodels and models to find out about selected items? (Using IoC.get<> in order to get the instance of the "global" class)
In my opinion, I would say that it was 'bad practice', as you say, to have model data type classes needing to know anything about selected items. I have always believed that the data type classes should just be 'holders of data' and have very little or no functionality... that is the job of the view models.
What I might do in your situation is have a parent view model that holds all of the collections and properties to bind to the selected items from each collection. That way, you could deal with everything in one location. The child views could bind directly to the parent view model as well if that would help:
<DataGrid ItemsSource="{Binding DataContext.Shipments, RelativeSource={
RelativeSource AncestorType={x:Type Views:ParentView}}}" />
Say I have a composite control in which I have a ListBox control which lists, for example, employees, and another control which contains multiple TextBox controls that will contains the employee's details.
When an employee is selected from the ListBox control, I need to pass an Employee object to the "detail" control, then assign the Text property of every TextBox control to their relative property of the Employee object.
I have two solutions to pass the Employee object to the "detail" control but I am not sure of which is the best.
Solution 1 : Expose a Employee property in the "detail" control so when an employee is selected, I could do detailControl.Employee = selectedEmployee.
Solution 2 : Expose an event in the main control and trigger it when an employee is selected. The "detail" control would subscribe to that event and receive the Employee object through the event's eventargs.
I know that both method will work just fine. I am just unsure of what is best to use.
Some say that you should pass data between controls with custom events, that it is a best loosely-coupled approach.
Some say that implementing custom events takes more time because you have to create them, add the properties to it, etc. so going with exposed properties is faster and simpler.
What do you suggest ?
Well, in my ideal world you would have a controller of some kind.
The main control would raise an event to which the controller would listen. The controller would then decode who should be notified, i.e the detail control.
This decouples the UI components entirely, providing the usual benefits, including facilitated unit testing.