MVVM: how to open a lookup window

This forum is meant for questions and discussions about the X# language and tools
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

MVVM: how to open a lookup window

Post by Frank Maraite »

Hi Wolfgang,

Nick has a central processor, which knows all about views and view models.
And there is a messenger, which recieves and sends out messages to the world.
Now
1. VM sends a message saying "I want to select a customer, and here's my VM Id XXX".
Here the message is sent out. The VM does not know anything about who is recieving the message. It's up to the central processor in step 2.
2. Central processor gets the message, opens a View/ViewModel of your lookup classes.
In this new view the user does what is asked for. This View/Viewmodel also knows nothing about the original View/ViewModel.
3. User selects a customer, lookup VM sends a message saying "customer selected, VM Id XXX asked for it".
After a while the orignal VM gets what asked for and reacts on it.
4. Original VM is looking out for messages with it's Id, gets the message and extracts the data.
At the end it's simple, but effective. It's the central controler opening the lookup view. It can be also a web request, a telefone call or whatever is able to send the answer back. The original View/ViewModel should not take care about. It only knows about the existence of a messenger.

The biggest hurdle is to think in abstractions like this. The modules should know as less as possible from each other. Only the communication via messenger is common to to them.

Frank
(@Nick: I hope I explained ok)
User avatar
wriedmann
Posts: 3759
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

MVVM: how to open a lookup window

Post by wriedmann »

Hi Frank,

yes, I had understand that the Messenger is the central communication tool between the single modules (View/ViewModel couples). In fact, such a construction has several advantages:
- module per module is testable (I know your position about this)
- module per module can be developed independently, and enhanced when needed
- module reuse is optimized (the most important for me as I need customer selection routines in many places of my program, for example)

I plan to develop single modules that register themselves and their functionality when loaded. I have something like this already working (modular programming is very, very important to me), and will upload this application when ready (and when I have the time to build a clear sample - not before February of the next year).

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

MVVM: how to open a lookup window

Post by NickFriend »

Hi Wolfgang,

Frank's explanation was excellent, so I won't add anything to that. Regarding how the actual View is opened, this is all done in the central processor.

I have a small class which I call Workspace. A workspace object keeps a copy of the ViewModel object and the associated View object that goes with it (I also have an enum of different types of ViewModels, eg. Customer lookup, customer list, so I can find them by type as well). I also have a WorkspaceFactory class, which is the only place in the program where the connection is made between ViewModels and Views.

So when a new view/viewmodel is to be opened, the central processor gets the message, it calls the WorkspaceFactory (with the enum type of the View/ViewModel type to be opened), the WorkspaceFactory instantiates a ViewModel object, the correct View object, and sets the ViewModel as the DataContext of the View. It creates a WorkSpace object with this info which is returned to the central processor.

The central processor then decides how to show this... in my app, it's basically a single page app (SPA) with a main ContentControl on the shell window. So all the Views are simply UserControls. Set the new View as the Content of the ContentControl, and it appears in the shell window. The central processor also keeps a list of current open Workspaces, so at any moment the user can swap to a different open view and that gets inserted into the ContentControl and appears instead.

When the user clicks to close a View, the central processor removes it from the ContentControl, and destroys the Workspace object (and the ViewModel and View that it contains), and removes it from the list of available open workspaces.

How your Views are displayed (as a ContentControl, popup window, separate area on the shell window, etc) is a simple detail that you can change at any moment, or vary from View to View without affecting any other part of the program.

This sort of setup works very well, and makes for very modular programming. The only other point to make for clarity is that I don't treat the shell window like the other View and ViewModel pairs - I actually keep both View and ViewModel classes for the shell in the main startup project together with app.xaml and the central processor and WorkspaceFactory, so that they can be made to work really closely together. Theoretically this violates MVVM (shock horror) but the reality is that the shell ViewModel is much easier to program if it has some knowledge of its View, so I just relax and get on with it.

Nick
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

MVVM: how to open a lookup window

Post by NickFriend »

Just a final note. This question of how to open a View and associate it with the ViewModel is much debated.

This is a reasonable summary of options : http://paulstovell.com/blog/mvvm-instan ... approaches

Here we're talking about option 3.... to my mind it's way superior to any other method. The only other one to possibly consider is option 2. The others are all disastrous for app design.

Nick
User avatar
wriedmann
Posts: 3759
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

MVVM: how to open a lookup window

Post by wriedmann »

Hi Nick,

thank you again! What you wrote makes much sense to me.

To couple Views and ViewModels I have used some code from Josh Smith, and he used approach 7 as the main TabControl is databound to a collection of ViewModels, so he needs a DataTemplate to show the View.

Since I don't liked to have this hardcoded in XAML, I have found a possibility to build this DataTemplate dynamically in code:

Code: Select all

	oShellWindowViewModel:RegisterDataTemplate( TypeOf( TestModuleViewModel ),TypeOf( TestModuleView ) )
where

Code: Select all

public method RegisterDataTemplate( viewModelType as Type, viewType as Type ) as void     

	_oDataTemplateManager:RegisterDataTemplate( viewModelType, viewType )
	
	return
But I have to confess that I'm not happy at all with this function - but I liked the idea to have databinding also for the TabControls pages.

The original article is this one:

https://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

MVVM: how to open a lookup window

Post by NickFriend »

Hi Wolfgang,

Try and get away from that DataTemplate approach, it turns into a nightmare.

If you just use the option 3 type approach, the Views and ViewModels become totally independent since the DataContext is set externally in the WorkspaceFactory. In fact the only major disadvantage is how to preview the View in the xaml designer (the designer doesn't know which ViewModel to use, or in fact anything about the ViewModel), for which there are some slightly glitchy workarounds.

But since you build your views in code, you avoid this disadvantage. So I'd say go for option 3 without a doubt, it fits in perfectly with your code-only approach, and with the message-centred app design we've been discussing.

Nick
User avatar
wriedmann
Posts: 3759
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

MVVM: how to open a lookup window

Post by wriedmann »

Hi Nick,

thank you again for all the time you have dedicated to me! I'll go on this route (as I wrote I'm not very happy with the DataTemplate approach even if I was able to build them dynamically in code).

I hope others find this discussion also worthful.

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
Phil Hepburn
Posts: 743
Joined: Sun Sep 11, 2016 2:16 pm

MVVM: how to open a lookup window

Post by Phil Hepburn »

Hi Nick and the gang,

Yes, I think you are right to be relaxed on this issue ;-0)

These MVVM and other 'rules' are just after all "guidelines". Any idea or approach taken to extremes always turns out BAD, or no good ! Even data 'normalisation'.

For most readers, we are used to other 'EXECUTIVE' systems when using Windows. The controller approach Frank so well explained, is after all similar to the 'Clipboard' with cut and paste, and even more like 'Drag and Drop'. Both of these systems have 'Source' and 'Target' objects which know nothing but themselves. S & T objects have to subscribe to the 'system', they register with it, or 'buy into' it. Words like forming a 'contract' are used as well. All these are phrases which say that they (the S & T) are aware of the executive system, and apply the system rules. Messaging either simple or more complex does go back and forth.  C&P and D&D belong to the Windows system, which is a level above each and every application in Windows.

Now then, so our inversion control (IC) system is an "Executive" one which is one level at least above the V/VM objects. And, in the Clipboard / 'Drag n Drop' we have data being parcelled up (made) and then used, on either side of the 'Source and Target'.  So effectively, we have messages flowing and also data. Neither executive system would be any use without the data.

I read with care, and great interest, what you have all written and the 'data' side of things is hardly mentioned at all - or isn't !?

Surely, if we are to apply M-VM-V then the 'M' needs to be discussed and taken account of. Remember 'Model is data.

The Controller 'executive' should be seen as a wire framework for the 'solution flow'. This framework is taken out of the detailed parts of the solution modules. In the past we mixed them up horribly, so that changes were a nightmare to apply.

If all we have done is remove the 'solution flow' details (wiring) to an executive level, then we still need to provide Model data to most VMs and associate the VM with a View. As in fact we did with our spaghetti solutions. I suspect that most of us guys will MOST often have only one VM to go with one View (a dedicated pair) - but it doesn't need to be this way.  

So how do you handle the data flow issue ? We can't be going back to a bunch of Globals for God sake !? ;-0) And we must not use multiple VMs with a V to handle data issues - those should be done by selecting and passing the right Model.

In 'DnD' we had some very nice ways in .NET to handle the data movement.

Oh! and by the way Wolfgang, Nick's approach to 'ContentControl' as a receptor, and 'UserControl' as the input to it, works very well indeed. I have copied Nick's structure a couple of times and this is a good way to space screen space, or screen real estate as the US guys say. Some of my Cologne solutions are built this way.

I am still getting my head around the 'Control Executive' approach. I know it is very good, but like a lot of this stuff it can look VERY complex, and complicated, and confusing, when shown by others (some?).

Any thoughts, comments, ideas, on what I have rambled on about ?

Best Regards,
Phil.
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

MVVM: how to open a lookup window

Post by NickFriend »

Hi Phil,

I think my approach to data handling is a bit different from yours.

I don't share data objects between ViewModels because a) I've not had the need to do it so far, b) when I'm going to say edit some info from a list I always go back to the database first, and c) I don't want to create any unnecessary links between otherwise independent objects (ViewModels in this case).

The logic of b) is very simple. Our app is normally working over a network or the Internet, so someone else could have edited data at any time. So if the user has a customer list on screen and wants to edit one, the first thing we do is go and get fresh data for the chosen customer from the db, just in case it's been changed since the list was loaded. All our data is always disconnected from the db by the time it gets to a ViewModel, so there's no automatic propagation from changes in the db through databinding.

The logic of c) is self-explanatory, and goes back to some of the previous discussion about messaging.

The way we handle data access is by having datasource objects that always have three basic methods - GetList to get a list of data (with a search condition and maybe in an abbreviated format for efficiency), GetRecord (which gets fully detailed info on a single bit of data normally identified by a primary key), and UpdateData to send modified info back to the server. Then I can tack on additional specialised methods as needed.

There's a datasource class for each type of data (eg. Customer, Order, etc). Everything is done as interfaces, and the ViewModels are programmed against the interfaces. Then when starting up a new ViewModel/View in the WorkspaceFactory the correct datasource objects are instantiated and injected into the ViewModel constructor. So each ViewModel has the datasource objects available to retrieve or modify the data it's handling, but it doesn't actually know where the data is coming from or any of the internal implementation of the data handling - it just calls the GetList or GetRecord etc methods.

I think my ViewModel classes are too big and complicated (any single responsibility principle freaks would have a heart attack!) but until I get around to refactoring it into something better, it works well enough, and each VM is totally self-contained and needs/expects nothing from anywhere else in the system.

Nick
User avatar
Phil Hepburn
Posts: 743
Joined: Sun Sep 11, 2016 2:16 pm

MVVM: how to open a lookup window

Post by Phil Hepburn »

Hi Nick,

That makes good sense in your case. So the database itself is like a "data executive" sitting above the VMs and Vs. Which is just fine! So you are still doing 'M-VM-V' then.

My point really, was that we should still be injecting data into our VM's and keeping them clear of data related processes normally associated with back-end data - not immediate manipulation, which of cause the VM will handle in some way or another.

The other thing I like about your approach is that it will cut down on asking for too much, or unnecessary amounts of SQL data - you know, how some guys just like to get 'All Customers' when in fact one or two would suffice ;-0)  Lets call it "Data on Demand".

When I have done all my preparatory LINQ work for Cologne (hopefully) I will turn my attention to getting myself a simple to "follow and use" 'Controller Executive' solution.

I will be back to you for more advice in the New Year on this ;-0)

Speak soon,
Phil.
Post Reply