Whilst working a WPF window, I ended up dumping loads of small jobs onto another developer, as he was the main one working on the XAML file for the window, and if we both tried to edit the file, we ended up with conflicts.
After dumping the umpteenth extra job on him, I was trying to work out if there is a way I could ease his burden a little. There had been quite a few new issues where the extent of my involvement had been to modify the database and update the model. After that, I had to pass it over to him, to avoid us stomping on each other when working on the same files.
So, I got to wondering if there was any way we could split the new quote window up, so multiple people could work on it at the same time. I immediately rejected using user controls for parts of the window, as this has been a road to complexity that we’ve trodden before, and I for one really don’t want to go down the route of handling multiple view models again. It gets too messy and complex. You know me, I’m a simple sort!
So, I looked into including XAML fragments, but gave up on that as a lost cause. I’m not the only one to want it, but it looks like no-one has successfully done it yet, at least, not without some scary code.
The next idea was to store some of the XAML as control templates in a resource dictionary. You don’t want to go there, believe me!
However, the brain cell woke up around this point, and I contemplated using user controls, but sharing one view model between them. Unsure of how neat this would be, I tried it out, and was pleasantly surprised by something actually very obvious. My initial thought was to declare the view model as the DataContext for each control, but that led to problems with multiple instances of the view model, so was quickly abandoned.
However, a light bulb went on at this point, and I realised I was making it too complex.
When you add a control to your XAML, it automatically inherits the DataContext of its parent. This is true of user controls as well as built-in controls. What this means is that you can have a window whose DataContext is set as usual, then have user controls for bits of the window, don’t have a separate view model for them, and don’t set a DataContext on them at all. They will automatically inherits the DataContext of whatever is above them, enabling you to work as usual. This allows you to use the same view model for all user controls on the window.
This means that we could hive out each of the expander’s sections into a user control, enabling me to work on one, and Shaul to work on another. See the attached sample project, where I did just this. As you can see, if you click the button to show the rather simple quote window, the window itself and both user controls are using the same view model (check the hashcode shown in three places), and if you open another window, it uses its own view model.
Quite amazing, but quite obvious when you think about it. It has a huge potential for simplifying many of our XAML files, which are often far too big to be manageable anyway.
We would have to be careful about naming and placing all of these user controls, to make sure we don’t end up wasting time trying to find them all. I suggested that a VrtQuoteDetailsWindow.xaml would have a VrtQuoteDetailsWindowKeyInfoUserControl.xaml, a VrtQuoteDetailsWindowSomethingElseUserControl.xaml and so on, and that these should be stored in the same folder as the VrtQuoteDetailsWindow.xaml file. Yes, this moves away from the convention of splitting windows and user controls, but my personal opinion is that this is somewhat arbitrary anyway, and no great loss. I much prefer to keep related .xaml files together, irrespective of whether they are windows or user controls.
In order to keep the user controls together with the parent window, they should be nested. Whilst this can be done manually, it’s slow and painful, and very easy to get wrong, which mucks up your project file and causes Visual Studio to have a hissy fit. Thankfully, someone has been kind enough to write a very neat extension, which enables you to nest files quickly and easily. Once you have it installed, you right-click on the file(s) that is/are to be a child(ren), and choose File Nesting -> Nest Item from the context menu (or just click Ctrl-Alt-N if you like keyboard shortcuts). This will pop up a small window, which will show you all the other files in the same folder…
Pick one and click the OK button. This will then nest the file(s) under the parent, leaving a neat structure. You can select multiple files, and nest them all under one parent in one go, which is nice.
As you can see, this worked very nicely for the window…
We were then able to work on separate parts of the same window, with a minimal amount of conflicts.
This then left the question of how to handle the view model, as we would still only have one of those. However, I have previously solved this problem by splitting the view model into several files. If you look at the VRTSystemsListViewModel, you’ll see it’s actually split across four separate files, which makes it much easier to handle…
This is pretty easy to do. I was working with a class named VRTSystemsListViewModel. In such a case, you would add a new class to the same folder, and name it (say) VRTSystemsListViewModel_Commands. Then change the file name to VRTSystemsListViewModel.Commands.cs, and the class name to VRTSystemsListViewModel (making it partial of course). Then, you need to nest the child view model files under the parent (see above for those with an extremely short memory).
Note that the systems list view model was split into separate files for commands, INPC properties, messages, etc. On reflection, this wasn’t the best way to do it, partly because it leaves related code spread over multiple files, and partly because it increases the chances of conflicts. If they had been split according to functionality, then we would have had the same benefits as explained earlier. I later did this for the system details window view model…
As you can see, the view model has been split according to features, making it less likely that two people would be working on the same files at the same time.
There was an unexpected side benefit to this idea. If you’ve spent any amount of time twiddling your thumbs while Visual Studio loads or saves a XAML file, you’ll know how frustrating this is.
It’s known issue with Visual Studio, which Microsoft have never acknowledged, and will presumably never fix. However, by splitting the XAML as described above, we dramatically reduced the amount of time Visual Studio spent thinking about loading and saving the XAML files. That made us happy