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
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
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.
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.
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!
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.
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.
And hope to revisit this in the morning.
P.S. do NOT give up ;-0)
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....
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((
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)
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!
thank you very much for taking the time to build your samples.
MVVM or MVMV how do you call it seems to me to be the right structure for clean data applications. Where I have my doubts is how to interconnect the windows together. In VO, I have my Shell and its children, sometimes also modal selection dialogs.
In X#, my Shell will be replaced by another structure: the child windows will be replaced by tab pages:
What is already working is the load of single modules at runtime - I plan to have an empty shell and load the functionality based on settings.
I hope to finish this structure as early as possible and publish it for everyone that needs such a structure and is not happy with large and complex kits like Prism and prefers a smaller solution that is understandable.
I'm going to be a bit busy today, so this evening I'll sit down and post a response with a bit more detail.
But in short, the idea is that all the opening/closing of View/ViewModels is managed by the central message processor. This is the first thing you start up in app.xaml.cs, it starts the shell window, and is responsible for opening/closing windows (or usercontrols or whatever, each one representing a View/ViewModel pair).
Moving data (such as the selected customer id from your lookup) and other notifications, can be direct from one VM to another, but again by messaging, not by direct reference. The trick is that each VM must have a unique identifier (a Guid) so that the correct target for data can be found.
1. VM sends a message saying "I want to select a customer, and here's my VM Id XXX".
2. Central processor gets the message, opens a View/ViewModel of your lookup classes.
3. User selects a customer, lookup VM sends a message saying "customer selected, VM Id XXX asked for it".
4. Original VM is looking out for messages with it's Id, gets the message and extracts the data.
I'll explain better later on, but this sort of scheme leaves a totally decoupled system. You can update your lookup implementation without worrying about VMs that call it for example.
I see that Nick will help you with the 'inversion of control' so you can de-couple each and every View (and supporting ViewModel), from the others. This is great and the modern way to do things - Nick has been doing it successfully for years.
However, please remember that any business related data ought to be help in 'Model' classes and not in the VM properties. And remember that the Model object(s) can still be scoped such that they are available to be used as input objects when VMs are instantiated by the Controller system.
Nick got me (and others) to keep the VM code as clean and as simple as possible, just enough to service the View. And it works, works well, and I like it that way ;-0)
If there is one reason I need to criticise myself (and I am sure there are many!) is for NOT making more of the data 'Model' object as a data provider to support my VM to support my Views. I am working on improving this side of my application design and coding.