It has been a while since I have written an article, I have had this one on my back burner list for a while, so I thought it high time that I get it out there. In some ways this article is a strange one, as 1/2 of it is kind of throw away, and merely serves as a vehicle to demonstrate the parts that I actually do think are very useful and can be applied to a whole range of different application styles/platforms.
So what is this article about exactly?
Quite simply it is about how to structure large codebases. I am not claiming that there are not other ways, rather I am claiming this is certainly one way that I personally have found to be incredibly useful. To give you an idea of my current code base I am using MVVM/WPF and have around 1500 ViewModels, my UI codebase alone is around 150,000 lines of code.
This is not the 1st WPF/MVVM application I have written, but when I look at my current code base compared to older codebases that I worked on, I do get a warm feeling in the knowledge that my new codebase is way more maintainable and testable.
How has this come about
Well as I say in my (and others) older code bases people were still finding their way with MVVM (which was a new pattern at the time, or at the very least a rebranded one), and composition of ViewModels etc etc Essentially it was unfamiliar ground. Ok there were certain tools that helped with this, one such tool was the now famous DelegateCommand
(or Josh Smiths RelayCommand
) which allowed you to run ICommand
implementation code directly in your ViewModel. So that helped, and soon everyone was happily creating loads of DelegateCommand
(s) in their ViewModels. Some clever folk actually went the extra mile and implemented the Command Pattern properly, but most didn't, and just had loads of DelegateCommand
(s) in their ViewModel(s). Nothing wrong with that, until.....
You realise you ViewModel is now getting very large due to the sheer number of properties it has to expose, and also the number of DelegateCommand
(s) it must accomodate. Each of these DelegateCommand
(s) may call out to additional services such as WCF Proxies, Http services etc etc, and the DelegateCommand
logic may also deal with retries and showing errors to the user, so its not difficult to imagine that one DelegateCommand.Execute()
method could end up with between 30-100 lines, multiply that by the number of DelegateCommand
(s) in your ViewModel and you can quite easily see how out of hand this can get.
Before long I realised this was not a good idea, and I started create more modular ViewModels. That helped for a while., but then that started to fall apart on occasion, where I realised there were scenarios where the logical split between ViewModels still ended up in situations where that there was still a big ViewModel, and I could not see a way out of that. I caved in, the occassion big ViewModel sigh, ok.
Around the same time I took a new job working at a tier 1 bank and met some very smart developers called
These guys were making heavy use of the Reactive Extensions (RX), which was fairly new to me at the time, and also talked quite often about VM Behaviours. At the time I did not fully understand what Ray/Keith were talking about, but after I left that job, I had a good old think about the problem, and was like aha I get it now.
What Ray/Keith were talking about was creating small bits of isolated functionality that did one thing and one thing only (Yes our old friend Seperation Of Concern SOC), and kind of associating that bit of functionality with a ViewModel instance. The way I like to think of this is "Attached Behaviours For ViewModels". This was achieved using a number of techniques such as
- Child IOC containers
- RX
- Specific IOC services to manage the ViewModel
The rest of this article will talk about my journey into this brighter world (that's a tongue in cheek joke by the way). This technique may not be for everyone, some may even suggest that it breaks the "Law Of Demeter" which I personally think should be renamed "The Occassionally Useful Law Of Demeter".
Anyway lets get on with the article shall we.
I have posted a small YouTube video of the demo app in action. You can see that video by clicking on the link below:
https://www.youtube.com/watch?v=d6rOiiXRZyY
Here is a screen shot as well
CLICK THE IMAGE TO SEE A BIGGER VERSION
The demo code is made using VS2013, and .NET 4.5, as such you will need both of those to run the demo app correctly.
The code for this article is available on my GitHub account :
https://github.com/sachabarber/WpfVMBehaviours
Don't forget if you like the rest of this article, and the ideas herein you can always Star the GitHub repository ;-)
Once/if you download the code, you will need to run it. This is pretty easy just ensure that the following project is set to be the startup project in the Visual Studio IDE
I am entitled this section "The Mule" as it is really not that important, it is a vehicle to allow the demonstration of the rest of the article. That is not to say Mules are useless, they are infact very useful and there may be some amongst you who after reading this, may actually like how I packed "The Mules (AKA PRISM 4.1)" saddle bags, and want to take him for your own ride.
For this demo app I am using PRISM 4.1 for the navigation functionality ONLY, as I think PRISM 4.1 does offer an excellent navigation framework for WPF/Silverlight/Windows Phones apps. I am of course aware there is a newer version available, but I had some code lying about that I could make use of that targeted PRISM 4.1 and the newer version of PRISM is different enough that I would have to redo this work. As I say in this article its all about other stuff the Navigation is a nice to have, that I just happened to be able to provide easily by using PRISM 4.1.
The truth is you could apply the techniques I am talking about in this to any Windows platform that you can work with that supports IOC containers and RX. You could even apply this to a Windows Forms application where you might use the Model-View-Presenter pattern.
I guess what I am really saying is that there is quite a bit of custom PRISM 4.1 region stuff that has been written to make PRISM 4.1 be able to navigate using a child IOC container in the attached demo article, but you should NOT pay too much attention to that, as that is NOT really what the article is about
If you are interested in how I achieved that you can read a completely separate article I wrote about this some time ago:
http://www.codeproject.com/Articles/640573/ViewModel-st-Child-Container-PRISM-Navigation
From here on in is what I consider to be the good/useful stuff in this article that could be applied to other project types, such as :
- WPF Applications
- Windows Store Applications
- Silverlight Applications
- Windows Phone Applications
- Universal Applications
The general idea behind this architecture is that you may know in advance that you are going to have some pretty knarly UI requirements. I for example work creating trading apps, and regularly have requirements that involve UIs that have cross dependencies between many interconnected ViewModels. Where there may be relationships to parent ViewModels, or possibly even grand parent ViewModels, or between siblings. It can be be a nightmare, imagine all those INotifyPropertyChanged
event(s) that you will need to not only monitor, but also unhook at the correct time. Argghhh
Now trying to shoe horn this into a single ViewModel is just not going to work. Thing is we live in modern times, and can make use of modern tooling, which for me includes things like RX/IOC Containers. These are certainly 2 heavy hitters in the implemention of the demo app/framework I am presenting here.
If this all sounds quite vague don't worry you will see lots of code, we are just setting the scene for it.
So what is the basic idea, well before we get to that, it helps to kind of think about our UI a bit more. Like I say I work creating trading apps, where I have typically have closeable Tabs/Tiles of the same type of ViewModel I must show over and over again (different instances of course). So it kind of makes sense for the creation of that ViewModel and its dependencies to be isolated from the creation of another instance of the same type of ViewModel.
Now, you may have come across lots of Inversion Of Control (IOC) examples about how to inject services into ViewModel instances. Thing is you never really see that many that deal with actual IOC component lifecyles and how that works within your app.
- Imagine you has a shared service, but you wanted it to only be a singleton to a certain type of ViewModel. Mmmm.
- And also how would the IOC container know when to release things? Mmmm
- Or you want each tile to get a singleton instance scoped to it alone. Mmmm
I have pondered this a lot, and IOC resolved dependencies and having a highly composable UI are things that interest/concern me. And that is what this article is really about.
So some bullet points
- We will assume that for each new part (view) that is being navigated to, that we will use a Child IOC Container
- We will couple (Strongly) the newly created IOC container the the lifecycle of the ViewModel such that when the ViewModel is closed (no longer needed) the child container and all the held dependencies it created for the current ViewModel will be Disposed
- We will create a set of ViewModel behaviours (some of you may have seen MVVM + Controller, so you can think of ViewModel Behaviours as Micro Controllers for the ViewModel instance). The beauty of these "Micro controllers" is that you can VERY easily find where some code is that does a certain action, and you will know it will not impact anything else as is is also following the Separation Of Concerns ideals.
- The ViewModel behaviours will be applied with nothing more than a single IOC registration
- The ViewModel behaviours will be able to monitor the ViewModel using RX (this is awesome the more I use it the more I love it)
Some of you may baulk at the "Airy Fairyness" of this, and yeah fair enough, but I honestly am telling you by following this pattern I feel I have finally found my XAML nirvana/sweet spot.
Anyway here is a diagram which I hope illustrates what I am talking about here:
Here is a screen shot as well
CLICK THE IMAGE TO SEE A BIGGER VERSION
This section will dig a lot deeper into the specifics of how to achieve all the good stuff mentioned above. As I have stated already I am using PRISM 4.1, but as I have also stated, the techniques that have been/will be demonstrated in this article would work equally well anywhere where you have a stateful UI, and you use navigation, so things like Windows Phone apps, Silverlight, even Windows forms could be a good candidate for this (where you might use the Model-View-Presenter pattern instead of MVVM).
As I say this pattern (if you will) makes a lot of sense in UIs that can show repeatable bit of information, such as tabs, tiles with workspaces etc etc. Where each Tab or tile may get its own child IOC container.
Here is a screen shot of the demo app, which makes use of tiles, where each tile gets its own child IOC container, and each tile gets it own set of VM behaviours, which are associated with the VM and child container. The child IOC container is added the tile VM such that when the close button on the DataTemplate
for the VM is clicked it will call an ICommand in the VM, which will dispose of the VMs disposables, which include the child IOC container.
Nice (in my opinion) all nice and self contained.
CLICK THE IMAGE TO SEE A BIGGER VERSION
Ok so enough chat about how it all works, you guys want to see some code don't you. From here on in it's all about the code.
As I have stated on numerous occassions throughout the article, we couple a child IOC container to the ViewModel as an IDisposable
, such that when the ViewModel is asked to close (typically by way of a close ICommand
implementation), it can call Dispose()
on all of the ViewModel held IDisposable
s, which includes the child IOC container.
How this is achieved is done using 2 things.
Firstly we need a special base class for our ViewModels (only the top level ones that need the child container should need to inherit from this new base class. I personally find it quite useful to be able to add IDisposable
instances to the VM though)
public abstract class DisposableViewModel : INPCBase, IDisposable
{
private CompositeDisposable disposables = new CompositeDisposable();
public void AddDisposable(IDisposable disposable)
{
disposables.Add(disposable);
}
public virtual void Dispose()
{
disposables.Dispose();
}
}
Where it can be seen that there is a void AddDisposable(IDisposable disposable)
method which accepts IDisposable
instances which are added to a CompositeDisposable
(Rx class, which is really just like a List<IDisposable>
). Other ViewViewModel(s) in this demo app would inherit from as follows (in this code there is another base class TileViewModelBase
which actually inherits from DisposableViewModel
, but hopefully you get the idea).
public class SpotTileViewModel : TileViewModelBase, INavigationAware
{
private readonly IViewModelController controller;
public SpotTileViewModel(
Func<SpotTileViewModel, IViewModelController> controllerFactory,
IRegionManager regionManager,
IMessageBoxService messageBoxService)
: base(regionManager, messageBoxService)
{
controller = controllerFactory(this);
this.AddDisposable(controller);
controller.Start();
}
}
The other part of the puzzle is that when we respond with a navigation request (as I say I am using PRISM 4.1 but this could be done using whatever navigation process you have in place), is that we would wire up a child IOC container for the current navigation request and then add that child container to the DisposableViewModel
(the one that is being navigated to).
As I am using PRISM 4.1 I am using the Microsoft Unity IOC container, but you would use another one of your choice here, as long as it supports the creation of child containers.
private void NavigateToNewSpotTile(ShowNewSpotTileMessage showNewSpotTileMessage)
{
if (regionNavigationCapacityChecker.IsNavigationAllowedForRegion(RegionNames.MainRegion))
{
UriQuery parameters = new UriQuery();
parameters.Add("UniqueId", Guid.NewGuid().ToString());
IUnityContainer childContainer = ConfigureSpotTileContainer();
var uri = new Uri(typeof(SpotTileViewModel).FullName + parameters, UriKind.RelativeOrAbsolute);
regionManager.RequestNavigateUsingSpecificContainer(RegionNames.MainRegion, uri,
regionNavigationCallbackHelper.HandleNavigationCallback, childContainer);
}
}
private IUnityContainer ConfigureSpotTileContainer()
{
IUnityContainer childContainer = container.CreateChildContainer();
childContainer.RegisterTypeForNavigationWithChildContainer<SpotTileViewModel>(
new HierarchicalLifetimeManager());
childContainer.RegisterType<IViewModelController, SpotTileViewModelController>(
"SpotTileViewModelController", new HierarchicalLifetimeManager());
childContainer.RegisterType<Func<SpotTileViewModel, IViewModelController>>(
new HierarchicalLifetimeManager(),
new InjectionFactory(c =>
new Func<SpotTileViewModel, IViewModelController>(
viewModel =>
c.Resolve<IViewModelController>("SpotTileViewModelController",
new DependencyOverride<SpotTileViewModel>(viewModel)))));
childContainer.RegisterType<ISpotTileViewModelBehaviour,
LoadFakeSpotCCYPairsBehaviour>("LoadFakeSpotCCYPairsBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
MonitorFakePairBehaviour>("MonitorFakePairBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
OkCommandBehaviour>("OkCommandBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
TimeoutBehaviour>("TimeoutBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<IFakeSpotRateProvider,
FakeSpotRateProvider>(new HierarchicalLifetimeManager());
return childContainer;
}
The important thing to note in the above code can be condensed to these few points:
- We create a child container
- We register the services in the child container using the Unity HierarchicalLifetimeManager, which means that they are singleton instances for the scope of the child container
So where does the child container get added to the DisposableViewModel?
Well for me that is done in the following extension method:
public static void RegisterTypeForNavigationWithChildContainer<T>(this IUnityContainer container, LifetimeManager lifetimeManager)
{
container.RegisterType(typeof(Object), typeof(T), typeof(T).FullName,
lifetimeManager,
new InjectionMethod("AddDisposable", new object[] { container }));
}
But as I say you may have to find your own way of doing that, depending on your requirements, the only important part is that you add the child container to the DisposableViewModel
that is being navigated to.
So we now know some of what is going on, we know we will create a new child container when a ViewModel is requested to be navigated to, and that we add the child container to this ViewModel.
So how do we deal with showing the ViewModel. The answer to that for me is to use a DataTemplate
that represents the ViewModel. Here is the DataTemplate
for the demo apps SpotTileViewModel
.
<DataTemplate DataType="{x:Type viewModels:SpotTileViewModel}">
<Border BorderBrush="{DynamicResource AccentBrush}" BorderThickness="2,2,2,2"
Background="White" IsHitTestVisible="True">
<Grid Background="White"
attachedProps:GridUtils.ColumnDefinitions="*"
attachedProps:GridUtils.RowDefinitions="Auto,Auto,Auto,*,Auto">
<Grid HorizontalAlignment="Stretch"
Background="{DynamicResource AccentBrush}">
<Label Foreground="White" Content="Spot Tile"
VerticalAlignment="Center"
VerticalContentAlignment="Center" Margin="2,0,0,0" />
<Button Style="{DynamicResource toolbarButtonStyle}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
VerticalContentAlignment="Center" Margin="0,0,0,0"
Command="{Binding CloseViewCommand}"
ToolTip="Close">
<Viewbox Width="40" Height="40">
<Grid>
<Grid Width="128" Height="128" >
<Rectangle Fill="{Binding
RelativeSource={RelativeSource
AncestorType={x:Type Button} }, Path=Background}"
Margin="20"/>
</Grid>
<Path Data="F1M54.0573,47.8776L38.1771,31.9974 54.0547,16.1198C55.7604,14.4141 55.7604,11.6511 54.0573,9.94531 52.3516,8.23962 49.5859,8.23962 47.8802,9.94531L32.0026,25.8229 16.1224,9.94531C14.4167,8.23962 11.6511,8.23962 9.94794,9.94531 8.24219,11.6511 8.24219,14.4141 9.94794,16.1198L25.8255,32 9.94794,47.8776C8.24219,49.5834 8.24219,52.3477 9.94794,54.0534 11.6511,55.7572 14.4167,55.7585 16.1224,54.0534L32.0026,38.1745 47.8802,54.0534C49.5859,55.7585 52.3516,55.7572 54.0573,54.0534 55.7604,52.3477 55.763,49.5834 54.0573,47.8776z"
Stretch="Uniform"
Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button} },
Path=Foreground}" Width="26" Height="26" />
</Grid>
</Viewbox>
</Button>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<ComboBox HorizontalAlignment="Center" Width="80" Margin="2"
ItemsSource="{Binding FakeSpotPairs}"
SelectedItem="{Binding FakeSpotPair}"
IsEnabled="{Binding IsEnabled}"/>
<DatePicker SelectedDate="{Binding SelectedDate}"
Width="100" Margin="2"
IsEnabled="{Binding IsEnabled}"/>
</StackPanel>
<Grid Background="White" Grid.Row="2"
attachedProps:GridUtils.ColumnDefinitions="*,*"
attachedProps:GridUtils.RowDefinitions="Auto">
<ContentControl Content="{Binding RateViewModel}"
Margin="2,0,0,0"
IsEnabled="{Binding IsEnabled}"/>
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Vertical"
Visibility="{Binding StartedTiming,
Converter={x:Static
converters:BoolToVisibilityConverter.Instance},
ConverterParameter='True'}">
<ProgressBar Value="{Binding Progress}"
Style="{StaticResource SegmentedProgressBarStyle}"
Margin="5" Width="40" Height="40"></ProgressBar>
<Label Content="{Binding TimeOutRemaining}"
IsEnabled="{Binding IsEnabled}"
VerticalAlignment="Bottom"
HorizontalAlignment="Center" Margin="2"/>
</StackPanel>
</Grid>
<Button Content="Ok" Margin="2" Grid.Row="4"
Command="{Binding OkCommand}"/>
</Grid>
</Border>
</DataTemplate>
So how do we Dispose()
of the child container and all its held resources. Well ideally the DataTemplate/ViewModel pair will have a close button (or some other way of removing) to remove the ViewModel from the workspace). In the demo app when the Close button is clicked it will run the following code, which disposes of ALL the held IDisposable
instances held by the ViewModel. The child IOC container is one of these.
Simply put, when the ViewModel is removed, the child container and all the services it holds are all asked to Dispose()
.
public abstract class TileViewModelBase : DisposableViewModel
{
public TileViewModelBase(
IRegionManager regionManager,
IMessageBoxService messageBoxService)
{
this.regionManager = regionManager;
this.messageBoxService = messageBoxService;
CloseViewCommand = new SimpleCommand<object, object>(ExecuteCloseViewCommand);
}
public ICommand CloseViewCommand { get; set; }
private void ExecuteCloseViewCommand(Object arg)
{
var result = messageBoxService.ShowYesNo(
"You are about to close this Option, you will loose any edits you have. Are you sure?",
"Confirm close",
CustomDialogIcons.Warning);
if (result == CustomDialogResults.Yes)
{
IRegion region = regionManager.Regions["MainRegion"];
region.Remove(this);
this.Dispose();
}
}
}
Another thing that makes this all hum is to have most of the harsh ICommand logic (and other logic) moved out of the ViewModel, such that the ViewModel can concentrate on doing what it needs to do, which is to be A MODEL FOR THE VIEW. So how do we go about moving stuff out of the ViewModel?
The trick to that lies in 2 things for the demo app.
For each of these top level VMs that are navigated to (using whatever technique you prefer), there is a 1:1 mapping between the VM and a single Controller. The Controller takes the VM as a dependancy, as well as a collection of VM Behaviours (which we will see in a minute).
- The ViewModel role is to take a factory for the controller as a dependency, and then uses the factory passing itself to the controller factory which returns a controller for the VM. The VM then calls start (I am a big fan of deterministic starting of things). The VM adds the controller as a
IDisposable
to the VMs list of IDisposable
s, such that the VM being disposed will also clean up the controller and its dependencies
- The controller takes the VM and a collection of specific VM behaviours as constructor parameters. When the start method is called on the controller it will in turn call Start() on each of the specific VM behaviours. The controller will also add each of the specific VM behaviours to it own list of
IDisposable
s.
Here is the relevant code that makes this happen
VM code
public class SpotTileViewModel : TileViewModelBase, INavigationAware
{
private readonly IViewModelController controller;
public SpotTileViewModel(
Func<SpotTileViewModel, IViewModelController> controllerFactory,
....)
: base(.....)
{
controller = controllerFactory(this);
this.AddDisposable(controller);
controller.Start();
}
}
The take away point here is that the VM gets a factory to create a controller, and it then uses that
Controller Code
public class SpotTileViewModelController : IViewModelController
{
private CompositeDisposable disposables = new CompositeDisposable();
private readonly SpotTileViewModel spotTileViewModel;
private readonly ISpotTileViewModelBehaviour[] behaviors;
public SpotTileViewModelController(
SpotTileViewModel spotTileViewModel,
ISpotTileViewModelBehaviour[] behaviors)
{
this.spotTileViewModel = spotTileViewModel;
this.behaviors = behaviors;
}
public void Start()
{
foreach (var behavior in behaviors)
{
disposables.Add(behavior);
behavior.Start(spotTileViewModel);
}
}
public void Dispose()
{
disposables.Dispose();
}
}
The take away point this time is that the controller expects a particular type of VM and also a collection of a specific type of VM behaviours. It then calls the Start()
method on each of these specific VM behaviours passing in the actual VM
IOC Wireup Code
Obviously there is some voodoo going on to wir this all up nicely. That is the job of the IOC container. Which is done as follows. As I have previously stated I am using the Microsoft Unity application block, so you may need to change this for your needs/IOC container of choice.
private IUnityContainer ConfigureSpotTileContainer()
{
IUnityContainer childContainer = container.CreateChildContainer();
childContainer.RegisterTypeForNavigationWithChildContainer<SpotTileViewModel>(
new HierarchicalLifetimeManager());
childContainer.RegisterType<IViewModelController, SpotTileViewModelController>(
"SpotTileViewModelController", new HierarchicalLifetimeManager());
childContainer.RegisterType<Func<SpotTileViewModel, IViewModelController>>(
new HierarchicalLifetimeManager(),
new InjectionFactory(c =>
new Func<SpotTileViewModel, IViewModelController>(
viewModel =>
c.Resolve<IViewModelController>("SpotTileViewModelController",
new DependencyOverride<SpotTileViewModel>(viewModel)))));
childContainer.RegisterType<ISpotTileViewModelBehaviour,
LoadFakeSpotCCYPairsBehaviour>("LoadFakeSpotCCYPairsBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
MonitorFakePairBehaviour>("MonitorFakePairBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
OkCommandBehaviour>("OkCommandBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<ISpotTileViewModelBehaviour,
TimeoutBehaviour>("TimeoutBehaviour",
new HierarchicalLifetimeManager());
childContainer.RegisterType<IFakeSpotRateProvider,
FakeSpotRateProvider>(new HierarchicalLifetimeManager());
return childContainer;
}
Ah finally the meat of the article. So what are these VM behaviours? I like to think of them as either attached VM behaviours or micro controllers, where each behaviour does a specific job for the ViewModel in question.
So how do we create a VM behaviour? Well it starts with an interface that is specific to your VM needs. Here is the one that is used for the demo app:
public interface ISpotTileViewModelBehaviour : IDisposable
{
void Start(SpotTileViewModel spotTileViewModel);
}
Remember these are used and started by the ONE controller for the VM in question. The idea is that each behaviour does ONE thing, and ONE thing only. This might include:
- Listening to a certain property changing (such as an ID, which causes the whole entity to be fetched and populated from the DB)
- Listening to a group of related properties that MUST ALL be set before some action occurs
- Listening to a command being executed (we will use RX for this, but more on this later)
By following this design it is VERY easy to isolate things, and find things, and also to be pretty sure the code you are modifiying will not impact other parts of the system. Seperation Of Concerns and all that.
For example consider this screen shot for the demo app:
If you wanted to find out what was wrong with the OkCommand implementation, where would you look, oh in the "OkCommandBehavior" you say.
If you wanted to find out what was wrong with the timeout implementation, where would you look, oh in the "TimeOutBehavior" you say.
etc etc....You get the idea
NOTE : Some readers may just dismiss this as being a "Proper"/"Poor" (depending on how you think/argue) implementation of the "Command Pattern" but it is much more flexible than that. You do not necessarily need to perform actions based on user input, as stated above it could be based on some other VM property changing. You have the VM in scope so it or ANY of its child properties/VMs are also available to change/listen to
Another thing that is VERY useful in this pattern is the use of a ReactiveCommand
that will use RX internally.The Reactive command still implements ICommand
, and will OnNext()
and internal RX Subject when the command is executed. By using an ReactiveCommand
that means anywhere you can see the ViewModel you can use the full power of the RX operators, and there are loads (as many as LINQ and then some), and also be able to subsribe to the command when it executes.
Here is the ReactiveCommand
implementation
public class ReactiveCommand<T1, T2> : ICommand, IReactiveCommand
{
private Func<T1, bool> canExecuteMethod;
private Subject<object> commandExecutedSubject = new Subject<object>();
public ReactiveCommand()
{
this.canExecuteMethod = (x) => { return true; };
}
public ReactiveCommand(Func<T1, bool> canExecuteMethod)
{
this.canExecuteMethod = canExecuteMethod;
}
public bool CanExecute(T1 parameter)
{
if (canExecuteMethod == null) return true;
return canExecuteMethod(parameter);
}
public void Execute(T2 parameter)
{
commandExecutedSubject.OnNext(parameter);
}
public bool CanExecute(object parameter)
{
return CanExecute((T1)parameter);
}
public void Execute(object parameter)
{
Execute((T2)parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
if (canExecuteMethod != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (canExecuteMethod != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
public IObservable<object> CommandExecutedStream
{
get { return this.commandExecutedSubject.AsObservable(); }
}
}
It can be seen that the ReactiveCommand
also take a CanExecute
delegate, which means you can use it directly in your viewmodel. The other important part of the execute internally calls OnNext()
on a Subject<object>
where the object is the ICommand
parameter, which you could supply from code or XAML.
Here is an example usage from a ViewModel
OkCommand = new ReactiveCommand<object, object>(x => IsValid() && IsEnabled);
And here is example of subscribing to the command ONCE it executes. I have includes a complete VM behavior here to give you an idea of the sort of thing you can do with a ReactiveCommand
and the VM behaviours idea (each doing one thing only)
public class OkCommandBehaviour : ISpotTileViewModelBehaviour
{
private readonly IEventMessager eventMessager;
private SpotTileViewModel spotTileViewModel;
private CompositeDisposable disposables = new CompositeDisposable();
public OkCommandBehaviour(IEventMessager eventMessager)
{
this.eventMessager = eventMessager;
}
public void Dispose()
{
disposables.Dispose();
}
public void Start(SpotTileViewModel spotTileViewModel)
{
this.spotTileViewModel = spotTileViewModel;
SetupTopLevelSubscription();
}
private void SetupTopLevelSubscription()
{
disposables.Add(spotTileViewModel.OkCommand.CommandExecutedStream.Subscribe(
x =>
{
spotTileViewModel.IsEnabled = false;
eventMessager.Publish(new SpotTrade(
spotTileViewModel.SelectedDate,
spotTileViewModel.FakeSpotPair,
spotTileViewModel.RateViewModel.WholeRate,
DateTime.Now));
}));
}
}
Another case for RX is that of monitoring properties via the INotifyPropertyChanged
interface that XAML ViewModel(s) typically implement. Or even listening to ObservableCollection
(s) changing where items are added/removed. I typically have a bunch of extension methods floating about to aid in this matter. Here is the one from the demo project:
public class ItemPropertyChangedEvent<TSender>
{
public TSender Sender { get; set; }
public PropertyInfo Property { get; set; }
public bool HasOld { get; set; }
public object OldValue { get; set; }
public object NewValue { get; set; }
public override string ToString()
{
return string.Format("Sender: {0}, Property: {1}, HasOld: {2}, OldValue: {3}, NewValue: {4}", this.Sender, this.Property, this.HasOld, this.OldValue, this.NewValue);
}
}
public class ItemPropertyChangedEvent<TSender, TProperty>
{
public TSender Sender { get; set; }
public PropertyInfo Property { get; set; }
public bool HasOld { get; set; }
public TProperty OldValue { get; set; }
public TProperty NewValue { get; set; }
}
public class ItemChanged<T>
{
public T Item { get; set; }
public bool Added { get; set; }
public NotifyCollectionChangedEventArgs EventArgs { get; set; }
}
public class WeakSubscription<T> : IDisposable, IObserver<T>
{
private readonly WeakReference reference;
private readonly IDisposable subscription;
private bool disposed;
public WeakSubscription(IObservable<T> observable, IObserver<T> observer)
{
this.reference = new WeakReference(observer);
this.subscription = observable.Subscribe(this);
}
void IObserver<T>.OnCompleted()
{
var observer = (IObserver<T>)this.reference.Target;
if (observer != null)
observer.OnCompleted();
else
this.Dispose();
}
void IObserver<T>.OnError(Exception error)
{
var observer = (IObserver<T>)this.reference.Target;
if (observer != null)
observer.OnError(error);
else
this.Dispose();
}
void IObserver<T>.OnNext(T value)
{
var observer = (IObserver<T>)this.reference.Target;
if (observer != null)
observer.OnNext(value);
else
this.Dispose();
}
public void Dispose()
{
if (!this.disposed)
{
this.disposed = true;
this.subscription.Dispose();
}
}
}
public static class ObservableExtensions
{
public static IObservable<Unit> AsUnit<TValue>(this IObservable<TValue> source)
{
return source.Select(x => new Unit());
}
public static IObservable<TItem> ObserveWeakly<TItem>(this IObservable<TItem> source)
{
return Observable.Create<TItem>(obs =>
{
var weakSubscription = new WeakSubscription<TItem>(source, obs);
return () =>
{
weakSubscription.Dispose();
};
});
}
public static IObservable<Unit> ObserveCollectonChanged<T>(this T source)
where T : INotifyCollectionChanged
{
var observable = Observable
.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
h => source.CollectionChanged += h,
h => source.CollectionChanged -= h)
.AsUnit();
return observable;
}
public static IObservable<Unit> ObserveCollectonChanged<T>(this T source, NotifyCollectionChangedAction collectionChangeAction)
where T : INotifyCollectionChanged
{
var observable = Observable
.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
h => source.CollectionChanged += h,
h => source.CollectionChanged -= h)
.Where(x => x.Action == collectionChangeAction)
.AsUnit();
return observable;
}
public static IObservable<ItemChanged<T>> ItemChanged<T>(this ObservableCollection<T> collection, bool fireForExisting = false)
{
var observable = Observable.Create<ItemChanged<T>>(obs =>
{
NotifyCollectionChangedEventHandler handler = null;
handler = (s, a) =>
{
if (a.NewItems != null)
{
foreach (var item in a.NewItems.OfType<T>())
{
obs.OnNext(new ItemChanged<T>()
{
Item = item,
Added = true,
EventArgs = a
});
}
}
if (a.OldItems != null)
{
foreach (var item in a.OldItems.OfType<T>())
{
obs.OnNext(new ItemChanged<T>()
{
Item = item,
Added = false,
EventArgs = a
});
}
}
};
collection.CollectionChanged += handler;
return () =>
{
collection.CollectionChanged -= handler;
};
});
if (fireForExisting)
observable = observable.StartWith(Scheduler.CurrentThread, collection.Select(i => new ItemChanged<T>()
{
Item = i,
Added = true,
EventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, i)
}).ToArray());
return observable;
}
public static IObservable<TObserved> ObserveInner<TItem, TObserved>(this ObservableCollection<TItem> collection, Func<TItem, IObservable<TObserved>> observe)
{
return Observable.Create<TObserved>(obs =>
{
Dictionary<TItem, IDisposable> subscriptions = new Dictionary<TItem, IDisposable>();
var mainSubscription =
collection.ItemChanged(true)
.Subscribe(change =>
{
IDisposable subscription = null;
subscriptions.TryGetValue(change.Item, out subscription);
if (change.Added)
{
if (subscription == null)
{
subscription = observe(change.Item).Subscribe(obs);
subscriptions.Add(change.Item, subscription);
}
}
else
{
if (subscription != null)
{
subscriptions.Remove(change.Item);
subscription.Dispose();
}
}
});
return () =>
{
mainSubscription.Dispose();
foreach (var subscription in subscriptions)
subscription.Value.Dispose();
};
});
}
public static IObservable<TValue> ObserveProperty<T, TValue>(this T source,
Expression<Func<T, TValue>> propertyExpression) where T : INotifyPropertyChanged
{
return source.ObserveProperty(propertyExpression, false);
}
public static IObservable<TValue> ObserveProperty<T, TValue>(this T source,
Expression<Func<T, TValue>> propertyExpression,
bool observeInitialValue) where T : INotifyPropertyChanged
{
var getter = propertyExpression.Compile();
var observable = Observable
.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Where(x => x.PropertyName == propertyExpression.GetPropertyName())
.Select(_ => getter(source));
if (observeInitialValue)
return observable.Merge(Observable.Return(getter(source)));
return observable;
}
public static IObservable<string> ObservePropertyChanged<T>(this T source)
where T : INotifyPropertyChanged
{
var observable = Observable
.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Select(x => x.PropertyName);
return observable;
}
public static IObservable<ItemPropertyChangedEvent<TItem, TProperty>> ObservePropertyChanged<TItem, TProperty>(this TItem target, Expression<Func<TItem, TProperty>> propertyName, bool fireCurrentValue = false) where TItem : INotifyPropertyChanged
{
var property = ExpressionExtensions.GetPropertyName(propertyName);
return ObservePropertyChanged(target, property, fireCurrentValue)
.Select(i => new ItemPropertyChangedEvent<TItem, TProperty>()
{
HasOld = i.HasOld,
NewValue = (TProperty)i.NewValue,
OldValue = i.OldValue == null ? default(TProperty) : (TProperty)i.OldValue,
Property = i.Property,
Sender = i.Sender
});
}
public static IObservable<ItemPropertyChangedEvent<TItem>> ObservePropertyChanged<TItem>(this TItem target, string propertyName = null, bool fireCurrentValue = false) where TItem : INotifyPropertyChanged
{
if (propertyName == null &&& fireCurrentValue)
throw new InvalidOperationException("You need to specify a propertyName if you want to fire the current value of your property");
return Observable.Create<ItemPropertyChangedEvent<TItem>>(obs =>
{
Dictionary<PropertyInfo, object> oldValues = new Dictionary<PropertyInfo, object>();
Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
PropertyChangedEventHandler handler = null;
handler = (s, a) =>
{
if (propertyName == null || propertyName == a.PropertyName)
{
PropertyInfo prop = null;
if (!properties.TryGetValue(a.PropertyName, out prop))
{
prop = typeof(TItem).GetProperty(a.PropertyName);
properties.Add(a.PropertyName, prop);
}
var change = new ItemPropertyChangedEvent<TItem>()
{
Sender = target,
Property = prop,
NewValue = prop.GetValue(target, null)
};
object oldValue = null;
if (oldValues.TryGetValue(prop, out oldValue))
{
change.HasOld = true;
change.OldValue = oldValue;
oldValues[prop] = change.NewValue;
}
else
{
oldValues.Add(prop, change.NewValue);
}
obs.OnNext(change);
}
};
target.PropertyChanged += handler;
if (propertyName != null && fireCurrentValue)
handler(target, new PropertyChangedEventArgs(propertyName));
return () =>
{
target.PropertyChanged -= handler;
};
});
}
}
In here you will find a great many useful methods for working with RX, it has been my savior on many occassions.
I thought a nice way to end things would be to list a few behaviours from the demo app so you could get a flavour of them. You have already seen the OkBehaviour
above, so lets look at a few different ones.
MonitorFakePairBehaviour
This one does the job of listening to the chose currency pair, and then listening to changes from a fake rate pair symbol ticker
public class MonitorFakePairBehaviour : ISpotTileViewModelBehaviour
{
private readonly IFakeSpotRateProvider fakeSpotRateProvider;
private SpotTileViewModel spotTileViewModel;
private CompositeDisposable disposables = new CompositeDisposable();
private CompositeDisposable fakePairDisposables = new CompositeDisposable();
public MonitorFakePairBehaviour(IFakeSpotRateProvider fakeSpotRateProvider)
{
this.fakeSpotRateProvider = fakeSpotRateProvider;
disposables.Add(this.fakeSpotRateProvider);
}
public void Dispose()
{
disposables.Dispose();
fakePairDisposables.Dispose();
}
public void Start(SpotTileViewModel spotTileViewModel)
{
this.spotTileViewModel = spotTileViewModel;
SetupTopLevelSubscription();
}
private void SetupTopLevelSubscription()
{
DisposableHelper.CreateNewCompositeDisposable(ref fakePairDisposables);
fakePairDisposables.Add(spotTileViewModel.ObservePropertyChanged(x => x.FakeSpotPair)
.Where(x => !string.IsNullOrEmpty(x.NewValue))
.Subscribe(x =>
{
this.SetupFakePairSubscription();
}));
if (!string.IsNullOrEmpty(spotTileViewModel.FakeSpotPair))
{
SetupFakePairSubscription();
}
}
private void SetupFakePairSubscription()
{
if (spotTileViewModel == null)
return;
fakePairDisposables.Add(fakeSpotRateProvider.MonitorFakePair(spotTileViewModel.FakeSpotPair)
.Subscribe(x =>
{
if (spotTileViewModel.IsEnabled)
{
spotTileViewModel.RateViewModel.AcceptNewPrice(x);
}
}));
}
}
TimeoutBehaviour
This one does the job of starting a timer when the ViewModel has picked a Currency pair. The timeout will end up disabling the ViewModel when the timeout period elapses
public class TimeoutBehaviour : ISpotTileViewModelBehaviour
{
private SpotTileViewModel spotTileViewModel;
private CompositeDisposable disposables = new CompositeDisposable();
private CompositeDisposable fakePairDisposables = new CompositeDisposable();
public TimeoutBehaviour()
{
}
public void Dispose()
{
disposables.Dispose();
fakePairDisposables.Dispose();
}
public void Start(SpotTileViewModel spotTileViewModel)
{
this.spotTileViewModel = spotTileViewModel;
SetupTopLevelSubscription();
}
private void SetupTopLevelSubscription()
{
DisposableHelper.CreateNewCompositeDisposable(ref fakePairDisposables);
fakePairDisposables.Add(spotTileViewModel
.ObservePropertyChanged(x => x.FakeSpotPair)
.Where(x => !string.IsNullOrEmpty(x.NewValue))
.Subscribe(x =>
{
this.SetupFakePairSubscription();
}));
if (!string.IsNullOrEmpty(spotTileViewModel.FakeSpotPair))
{
SetupFakePairSubscription();
}
}
private void SetupFakePairSubscription()
{
if (spotTileViewModel == null)
return;
SetupTimeOutSubscription();
}
private void SetupTimeOutSubscription()
{
spotTileViewModel.StartedTiming = true;
int counter = 0;
UpdateProgress(counter);
var tileDisabledObservable = spotTileViewModel
.ObservePropertyChanged(x => x.IsEnabled)
.Where(x => !x.NewValue);
this.disposables.Add(Observable.Interval(
TimeSpan.FromSeconds(Globals.ProgressTimeOut))
.TakeUntil(tileDisabledObservable)
.Subscribe(x =>
{
counter++;
UpdateProgress(counter);
if (counter == Globals.ProgressSegments - 1)
{
spotTileViewModel.IsEnabled = false;
this.disposables.Dispose();
}
}));
}
private void UpdateProgress(int counter)
{
var timeRemaining = (Globals.TotalTimeoutInSeconds -
(Globals.ProgressTimeOut * (counter + 1)));
spotTileViewModel.TimeOutRemaining = string.Format("{0}s of {1}s",
counter == 0 ? Globals.TotalTimeoutInSeconds : timeRemaining,
Globals.TotalTimeoutInSeconds);
var secPerSegment = Globals.TotalTimeoutInSeconds/Globals.ProgressSegments;
spotTileViewModel.Progress =
(100/Globals.TotalTimeoutInSeconds) * (secPerSegment * counter+1);
}
}
Anyway that is all I wanted to say this time. I do have another VERY LARGE article I am working on right now on CQRS, that I hope to have out soon. That will take a me a while to finish so until then, could I just ask if you liked this article could you spare 2 minutes to leave a comment/question, or a vote, they are always welcome.