Hi all,
I don't know if this is the right place, but I have a big understanding problem, and maybe someone (Nick?) can help me. I've tried to post this question also on the public NG.
On a View in a typical business application there are some fields that
are connected to other data, like a customer ID in a order window.
How should this be handled in a MVVM application? The lookup button can
be connected to a command, but how can I proceed?
My lookup window of course is again a View with a ViewModel, and I have
no problem building this.
Should the order view open the customer view and then, after a
successful selection, paste the selected customer ID into the field?
Would be simply to accomplish as I have my own control class where I
could store the the columns and the select statement from the ViewModel
using databinding.
Or should the ViewModel start the ViewModel trough a command?
I'm completely out of ideas what is the "correct" or, better, the best
solution for the future development.... It is a so basic and often used
thing....
I have to add that I'm using no specific framework as I prefer to understand what my code does, and I need software that is maintenable for the next 20 years.
Thank you for any hint! I have already searched on the web, and could find no valid answer.
Wolfgang
MVVM: how to open a lookup window
MVVM: how to open a lookup window
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
-
- Posts: 248
- Joined: Fri Oct 14, 2016 7:09 am
MVVM: how to open a lookup window
Hi Wolfgang,
How to communicate between different ViewModels is where you hit a wall with MVVM (or at least I did), and there's a whole secondary topic here of how/when to instantiate Views and ViewModels, and how/when to join them together, but that really is a big topic and takes us away from the more straightforward solution I think.
I know there's a tendency in MVVM to break everything down into tiny ViewModels, but to be honest I find that that approach can get a bit out of hand with loads of interdependent classes. I tend to be a bit more practical. How about the following....
1. Textbox where the user can type eg. part of the customer name they're searching for.
2. Listbox or grid (on the same main View) whose Visibility attribute is bound to a property (ShowSearchGrid) in the ViewModel to show the search results, and whose ItemsSource is bound to an ObservableCollection, also in the main ViewModel.
3. Button to start the lookup.
4. Button fires a command in the main ViewModel (you can link the CanExecute of the command to the content of the textbox so it's only activated when something has been typed in).
5. Command calls a private method in the ViewModel, which asynchronously does the lookup to get the list of matching data. When the lookup is completed, set the ShowSearchGrid property to true and set the search results to the ObservableCollection (with appropriate notifications).
So when the search results are ready to be used, the grid automatically appears filled with matching results. All you have to do now is bind say the SelectedItem property (or whatever it is depending on the precise control you use), to a SelectedRecord property in the ViewModel and you can get the selected result back. When the SelectedItem property gets set, make ShowSearchGrid false, and the grid disappears again.
You'll need to use a boolean to visibility converter as well.
Nick
How to communicate between different ViewModels is where you hit a wall with MVVM (or at least I did), and there's a whole secondary topic here of how/when to instantiate Views and ViewModels, and how/when to join them together, but that really is a big topic and takes us away from the more straightforward solution I think.
I know there's a tendency in MVVM to break everything down into tiny ViewModels, but to be honest I find that that approach can get a bit out of hand with loads of interdependent classes. I tend to be a bit more practical. How about the following....
1. Textbox where the user can type eg. part of the customer name they're searching for.
2. Listbox or grid (on the same main View) whose Visibility attribute is bound to a property (ShowSearchGrid) in the ViewModel to show the search results, and whose ItemsSource is bound to an ObservableCollection, also in the main ViewModel.
3. Button to start the lookup.
4. Button fires a command in the main ViewModel (you can link the CanExecute of the command to the content of the textbox so it's only activated when something has been typed in).
5. Command calls a private method in the ViewModel, which asynchronously does the lookup to get the list of matching data. When the lookup is completed, set the ShowSearchGrid property to true and set the search results to the ObservableCollection (with appropriate notifications).
So when the search results are ready to be used, the grid automatically appears filled with matching results. All you have to do now is bind say the SelectedItem property (or whatever it is depending on the precise control you use), to a SelectedRecord property in the ViewModel and you can get the selected result back. When the SelectedItem property gets set, make ShowSearchGrid false, and the grid disappears again.
You'll need to use a boolean to visibility converter as well.
Nick
MVVM: how to open a lookup window
Hi Nick,
thank you very much for your answer! It took a while to fully understand what you are meaning. Unfortunately it is no option for me to insert a DataGrid in every window. Most business windows have several key fields, and I have simply not the place to put DataGrids on these windows in addition to the other controls.
My idea currently would be to have an own control class for lookups, and this control class should retrieve the needed data (lookup table, needed columms etc. via databinding from the ViewModel using an own object), and when the user klicks on the lookup button, the view starts another view with the parameters from the lookup definition object. I would like to write a generic lookup popup window (I already have something like this in my VO applications), and reuse this on every place where I need a lookup.
What do you think about this? Is this rubbish or should it work?
Thank you again!
Wolfgang
thank you very much for your answer! It took a while to fully understand what you are meaning. Unfortunately it is no option for me to insert a DataGrid in every window. Most business windows have several key fields, and I have simply not the place to put DataGrids on these windows in addition to the other controls.
My idea currently would be to have an own control class for lookups, and this control class should retrieve the needed data (lookup table, needed columms etc. via databinding from the ViewModel using an own object), and when the user klicks on the lookup button, the view starts another view with the parameters from the lookup definition object. I would like to write a generic lookup popup window (I already have something like this in my VO applications), and reuse this on every place where I need a lookup.
What do you think about this? Is this rubbish or should it work?
Thank you again!
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
-
- Posts: 248
- Joined: Fri Oct 14, 2016 7:09 am
MVVM: how to open a lookup window
It wouldn't have to be a datagrid, it could be a combobox in which case it wouldn't take any more space than the existing edit control, but I get your point.
In which case if I understand correctly, the discussion is really about how to manage communication between two VMs. First, a quick point - you mention calling one View from another. This is the wrong way to think about it. The Views have no real program logic associated with them, so what we're really talking about is calling one VM from another, and then how those VMs pass info back and forth.
For this the key is messaging, so that you can pass info but without any direct coupling between the VMs. So it would be something like...
1. Press button for lookup - button is bound to a command in the VM.
2. Command calls a private method in the VM.
3. Method sends a message which basically says "I want to look up x type of data", and here's my unique ID (say a Guid generated specifically for this message).
4. Your messaging system catches this message and starts up the appropriate VM and View (your popup window).
5. When user selects data in the popup, send a message back out saying "user selected XXX data", and attach the Guid to identify who it's for.
6. Your original VM catches the message, checks that the identifying Guid is the same, and then gets hold of the attached data.
100% of the communication between VMs in our app is done like this and it works perfectly, and it leaves the VMs totally decoupled (one VM never instantiates another directly).
Of course the issue is the messaging system. I use MVVM Light. I'd suggest you look at the source code version of MVVM Light, because you could simply extract the messaging class out of it, or use the (very highly documented) source code as a guide to create your own version.
This sort of technique allows your app to grow to any size absolutely effortlessly, simply by adding VMs and their associated Views, and slotting them into the messaging system.
Sorry if this is a bit vague.
Nick
In which case if I understand correctly, the discussion is really about how to manage communication between two VMs. First, a quick point - you mention calling one View from another. This is the wrong way to think about it. The Views have no real program logic associated with them, so what we're really talking about is calling one VM from another, and then how those VMs pass info back and forth.
For this the key is messaging, so that you can pass info but without any direct coupling between the VMs. So it would be something like...
1. Press button for lookup - button is bound to a command in the VM.
2. Command calls a private method in the VM.
3. Method sends a message which basically says "I want to look up x type of data", and here's my unique ID (say a Guid generated specifically for this message).
4. Your messaging system catches this message and starts up the appropriate VM and View (your popup window).
5. When user selects data in the popup, send a message back out saying "user selected XXX data", and attach the Guid to identify who it's for.
6. Your original VM catches the message, checks that the identifying Guid is the same, and then gets hold of the attached data.
100% of the communication between VMs in our app is done like this and it works perfectly, and it leaves the VMs totally decoupled (one VM never instantiates another directly).
Of course the issue is the messaging system. I use MVVM Light. I'd suggest you look at the source code version of MVVM Light, because you could simply extract the messaging class out of it, or use the (very highly documented) source code as a guide to create your own version.
This sort of technique allows your app to grow to any size absolutely effortlessly, simply by adding VMs and their associated Views, and slotting them into the messaging system.
Sorry if this is a bit vague.
Nick
- Phil Hepburn
- Posts: 743
- Joined: Sun Sep 11, 2016 2:16 pm
MVVM: how to open a lookup window
Good Evening / Night Wolfgang,
I can tell you what I do for WPF apps, and what I showed in my sessions in the German Conference in Cologne and Stuttgart?
It is too late to do this tonight, so I will revisit it in the morning.
The simple answer is 'Data Binding' (DB), this is what DB is all about. However, the coding has to be done in a suitable manner. Do it right and it is the Model which is shared by one or many ViewModels (as input parameters) and hence one or many Views can display exactly the same data / items. Changes in one View is reflected in the others, immediately, even if they are open on screen simultaneously. I demonstrated this on numerous occasions to the Conference.
So if a 'shared' Model is updated in one View, its new value will be available in the others that are also sharing this same Model.
To do this the properties of the various ViewModels have the parameter inputted assigned to them in their Constructors. Yes, keep exactly the same Model 'object' don't make copies or the likes. So one Model is passed into many VMs when they are instantiated.
On one 'screen / WPF form / View', react to user input in the usual way (click event or otherwise) and use a Model class 'Method' to search / find / update the data in question. This will be then available elsewhere.
In fact I also showed in the sessions a good way to check one View and its data binding capabilities - and that is to have multiple copies of the View open on the screen - changes in one should be reflected in the copies, if not then you have NOT done your Data Binding correctly.
If you make your Models too local (a very common mistake), or somehow restrict such processes to code in the VM (also very common problem), then it just won't work. I know, I have spent many frustrated hours 'until I saw the light!'
Try to work (data wise) as far from the View as possible, and as close to the back-end DB as possible when you instantiate your data Model. I have many working examples of this which I did make available years ago.
Good Luck,
And hope to revisit this in the morning.
Cheers,
Phil.
P.S. do NOT give up ;-0)
I can tell you what I do for WPF apps, and what I showed in my sessions in the German Conference in Cologne and Stuttgart?
It is too late to do this tonight, so I will revisit it in the morning.
The simple answer is 'Data Binding' (DB), this is what DB is all about. However, the coding has to be done in a suitable manner. Do it right and it is the Model which is shared by one or many ViewModels (as input parameters) and hence one or many Views can display exactly the same data / items. Changes in one View is reflected in the others, immediately, even if they are open on screen simultaneously. I demonstrated this on numerous occasions to the Conference.
So if a 'shared' Model is updated in one View, its new value will be available in the others that are also sharing this same Model.
To do this the properties of the various ViewModels have the parameter inputted assigned to them in their Constructors. Yes, keep exactly the same Model 'object' don't make copies or the likes. So one Model is passed into many VMs when they are instantiated.
On one 'screen / WPF form / View', react to user input in the usual way (click event or otherwise) and use a Model class 'Method' to search / find / update the data in question. This will be then available elsewhere.
In fact I also showed in the sessions a good way to check one View and its data binding capabilities - and that is to have multiple copies of the View open on the screen - changes in one should be reflected in the copies, if not then you have NOT done your Data Binding correctly.
If you make your Models too local (a very common mistake), or somehow restrict such processes to code in the VM (also very common problem), then it just won't work. I know, I have spent many frustrated hours 'until I saw the light!'
Try to work (data wise) as far from the View as possible, and as close to the back-end DB as possible when you instantiate your data Model. I have many working examples of this which I did make available years ago.
Good Luck,
And hope to revisit this in the morning.
Cheers,
Phil.
P.S. do NOT give up ;-0)
MVVM: how to open a lookup window
Hi Phil,
thank you for your answer. I'm trying to do the things right, and at the same moment I try to understand how things are working together. Therefore I take longer than other people to make something working - and need more patience from others and mostly from myself. But I'm very short on time - I have to deliver working things to customers....
Wolfgang
thank you for your answer. I'm trying to do the things right, and at the same moment I try to understand how things are working together. Therefore I take longer than other people to make something working - and need more patience from others and mostly from myself. But I'm very short on time - I have to deliver working things to customers....
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
MVVM: how to open a lookup window
Hi Nick,
thank you very much! I already have such an object that I'm using to display messageboxes from the ViewModel.
I'll be away today and since I will be stay in the car for long time, I will think aboout how to make it work.
Thank you again for you continued help!
Wolfgang
thank you very much! I already have such an object that I'm using to display messageboxes from the ViewModel.
I'll be away today and since I will be stay in the car for long time, I will think aboout how to make it work.
Thank you again for you continued help!
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
- Phil Hepburn
- Posts: 743
- Joined: Sun Sep 11, 2016 2:16 pm
MVVM: how to open a lookup window
Hi Wolfgang and all,
It does take a lot of time and effort to get our heads into MVVM, I know, I have spent ages (a very long time) myself ;-0(( But its quicker once we have some good and clear examples to guide us in our design and coding. A lot of my invested time was having to make my own.
The BIG problem to me was the name 'MVVM' is wrong, yes, incorrect. It may be sexy to say "em vee vee-em" - BUT - it should be MVMV said "em vee-em vee" as the 'Model' comes first, then the 'ViewModel' and lastly the 'View' in the way the data 'flows' or is presented in our .NET code and design.
I have done all my conference session samples without any third party framework, Data Binding works (and works well) with standard C# / X# code.
While you are driving on your long journey today I will try and make a simple example for us all to study. I will make a sample where the Customers list is shown in multiple Views and where any changes in Customer property details is reflected (shown) immediately in the others. Each of the Views should work equally well - they will be duplicates.
As I said last evening, the trick ("must do") is to instantiate the Model outside of the VM, then pass it into the Constructor as an input parameter. The same Model object has then to be passed into any VM and hence V that are instantiated.
AND - the big thing to remember is that we only 'Data Bind' with public properties in the VM. And that for all properties (including those in the data class) we bind, need to handle (notify) property changes (events). Collection properties are best to be of type ObservableCollection as these always work well in every way.
Public properties, on the VM itself, can be defined and used as 'helper' properties, and then ONLY effect the performance of the single instance of the VM. One thing I do myself to be like this, is to have a property (PP) called 'SelectedItem' which holds the item chosen from a drop down box (combo), so that each of the separate Views on shown can have different selections showing of list items, even though the underlying list data is common to all the VMs/Vs.
I will do my best to include all these things in my simple sample. Wow! I thought I had gotten away from MVMV (MVVM) for a while - oh! well !! ;-0((
Cheers,
Phil.
It does take a lot of time and effort to get our heads into MVVM, I know, I have spent ages (a very long time) myself ;-0(( But its quicker once we have some good and clear examples to guide us in our design and coding. A lot of my invested time was having to make my own.
The BIG problem to me was the name 'MVVM' is wrong, yes, incorrect. It may be sexy to say "em vee vee-em" - BUT - it should be MVMV said "em vee-em vee" as the 'Model' comes first, then the 'ViewModel' and lastly the 'View' in the way the data 'flows' or is presented in our .NET code and design.
I have done all my conference session samples without any third party framework, Data Binding works (and works well) with standard C# / X# code.
While you are driving on your long journey today I will try and make a simple example for us all to study. I will make a sample where the Customers list is shown in multiple Views and where any changes in Customer property details is reflected (shown) immediately in the others. Each of the Views should work equally well - they will be duplicates.
As I said last evening, the trick ("must do") is to instantiate the Model outside of the VM, then pass it into the Constructor as an input parameter. The same Model object has then to be passed into any VM and hence V that are instantiated.
AND - the big thing to remember is that we only 'Data Bind' with public properties in the VM. And that for all properties (including those in the data class) we bind, need to handle (notify) property changes (events). Collection properties are best to be of type ObservableCollection as these always work well in every way.
Public properties, on the VM itself, can be defined and used as 'helper' properties, and then ONLY effect the performance of the single instance of the VM. One thing I do myself to be like this, is to have a property (PP) called 'SelectedItem' which holds the item chosen from a drop down box (combo), so that each of the separate Views on shown can have different selections showing of list items, even though the underlying list data is common to all the VMs/Vs.
I will do my best to include all these things in my simple sample. Wow! I thought I had gotten away from MVMV (MVVM) for a while - oh! well !! ;-0((
Cheers,
Phil.
- Phil Hepburn
- Posts: 743
- Joined: Sun Sep 11, 2016 2:16 pm
MVVM: how to open a lookup window
Hi Wolfgang,
The answer is to 'follow the data' and don't start thinking from the View / Window end of things. get the data provision correct.
I now have a working example which provides editable data in collections (or type Customer, Product, Order and Supplier) to a 'ViewModel' which is assigned to the 'DataContext' property of a View. It works nicely when I edit/change the customer name Jimmy Williams to Nick Riedmann ! See the next image :-
Once I move off the row being changed the text changes show immediately in the other two Views. Its even more immediate when using ListBoxes.
It took very little effort and not much code or time, to do this to the application I had in front of me, for testing out the EF6 stuff I have been posting about.
Below I show that the LINQ queries supply the 'GetAll...' methods with results collections, so I can use them as input parameters to the ViewModel - as sort of 'Models'.
Check this out :-
The full View structure looks like the following :-
So we need to supply four different 'Models' to the ViewModel constructor. Because the sample is simple I use as Models the collection object for Cust, Prod, Ord and Supp. Usually the Model will be a class based on each data collection with possible methods to give extra functionality like saving changes, or finding an object with a certain identity.
Image '_04' shows the XAML script (which you will like, as you are a confessed XAML freak !) and only two small changes to each DataGrid are required - ItemsSource and AutoGen....
I took the extra short amount of time to try and structure my SE pane entries / content see below :-
The secret of this success is the scoping of the data Models (here returned collections) so that one instance can be use to instantiate a number of VMs.
ALSO - do not underestimate the 'Business Classes' Customer, Product, etc. as these have code to notify the property changes as we discussed previously. I have not room to show this here, but it is crucial for success.
Try to keep from the VM too much processing and any business data manipulation. Keep as much as possible to the Model classes. This way we can share Models with many different types of VM. ViewModels should really only present to the View the bunch of Public Properties for binding. Its not to be used as 'code behind.
So its all about scope and notifying, as well as keeping VMs as simple as possible.
Lastly, here is the simple VM in the four grid View approach :-
Note that you will see these property names in the XAML script for binding - each DataGrid is bound to a collection property.
Hope this helps you and possibly enlightens a few other guys who have not as yet put in the endless hours of work to make this stuff sense and get real functional results ;-0)
Cheers,
Phil.
The answer is to 'follow the data' and don't start thinking from the View / Window end of things. get the data provision correct.
I now have a working example which provides editable data in collections (or type Customer, Product, Order and Supplier) to a 'ViewModel' which is assigned to the 'DataContext' property of a View. It works nicely when I edit/change the customer name Jimmy Williams to Nick Riedmann ! See the next image :-
Once I move off the row being changed the text changes show immediately in the other two Views. Its even more immediate when using ListBoxes.
It took very little effort and not much code or time, to do this to the application I had in front of me, for testing out the EF6 stuff I have been posting about.
Below I show that the LINQ queries supply the 'GetAll...' methods with results collections, so I can use them as input parameters to the ViewModel - as sort of 'Models'.
Check this out :-
The full View structure looks like the following :-
So we need to supply four different 'Models' to the ViewModel constructor. Because the sample is simple I use as Models the collection object for Cust, Prod, Ord and Supp. Usually the Model will be a class based on each data collection with possible methods to give extra functionality like saving changes, or finding an object with a certain identity.
Image '_04' shows the XAML script (which you will like, as you are a confessed XAML freak !) and only two small changes to each DataGrid are required - ItemsSource and AutoGen....
I took the extra short amount of time to try and structure my SE pane entries / content see below :-
The secret of this success is the scoping of the data Models (here returned collections) so that one instance can be use to instantiate a number of VMs.
ALSO - do not underestimate the 'Business Classes' Customer, Product, etc. as these have code to notify the property changes as we discussed previously. I have not room to show this here, but it is crucial for success.
Try to keep from the VM too much processing and any business data manipulation. Keep as much as possible to the Model classes. This way we can share Models with many different types of VM. ViewModels should really only present to the View the bunch of Public Properties for binding. Its not to be used as 'code behind.
So its all about scope and notifying, as well as keeping VMs as simple as possible.
Lastly, here is the simple VM in the four grid View approach :-
Note that you will see these property names in the XAML script for binding - each DataGrid is bound to a collection property.
Hope this helps you and possibly enlightens a few other guys who have not as yet put in the endless hours of work to make this stuff sense and get real functional results ;-0)
Cheers,
Phil.
MVVM: how to open a lookup window
Hi Nick,
after thinking a while what you wrote, I think I understand finally a part.
Basically, I should use a central Messenger object to transport the messages. Such a thing as I use in my VO ShellWindow to broadcast notifications (but enhanced).
What is unclear to me is the following:
the ViewModel of the order window for example sends out a notification like "I need a customer ID" through the communicator. But what is the right object to react on this? The ViewModel of my Shell or main Window? And then: if is is the ViewModel: since the lookup window needs an owner: how does it finds the correct window?
Thank you very much for your patience!
Wolfgang
after thinking a while what you wrote, I think I understand finally a part.
Basically, I should use a central Messenger object to transport the messages. Such a thing as I use in my VO ShellWindow to broadcast notifications (but enhanced).
What is unclear to me is the following:
the ViewModel of the order window for example sends out a notification like "I need a customer ID" through the communicator. But what is the right object to react on this? The ViewModel of my Shell or main Window? And then: if is is the ViewModel: since the lookup window needs an owner: how does it finds the correct window?
Thank you very much for your patience!
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it