Click here to Skip to main content
15,867,308 members
Articles / Mobile Apps / Windows Phone 7

Attached VM Behaviours

Rate me:
Please Sign up or sign in to vote.
4.91/5 (47 votes)
23 Mar 2015CPOL21 min read 128.1K   85   67
Showcase of how to use a sort of Attached Behaviours for ViewModels, and how to build BIG XAML apps

Introduction

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

  • Ray Booysen
  • Keith Woods

 

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.

 

 

 

Demo Video

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

 

ScreenShot

Here is a screen shot as well

Image 1

 

CLICK THE IMAGE TO SEE A BIGGER VERSION

 

Prerequisites

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.

 

 

Where Is The Code?

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 ;-)

 

How To Run The Code

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 

  • WpfBehaviours.Shell

 

 

A Note About Navigation (The Mule)

 

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

 

 

The Good Stuff

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

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

Image 2

 

CLICK THE IMAGE TO SEE A BIGGER VERSION

 

 

 

 

A Deeper Dive Into The Guts Of It

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).

 

Thinking In Composable Views

 

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.

Image 3

 

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.

 

Disposable ViewModels / Child Container Lifecycle Management

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 IDisposables, which includes the child IOC container.

 

How this is achieved is done using 2 things.

 

Disposable ViewModel

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)

C#
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).

 

C#
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();
    }
}

 

Adding The Child IOC Container To ViewModel As IDisposable

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.

C#
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();

    //navigation windows
    childContainer.RegisterTypeForNavigationWithChildContainer<SpotTileViewModel>(
        new HierarchicalLifetimeManager());

    //viwemodel controllers
    childContainer.RegisterType<IViewModelController, SpotTileViewModelController>(
        "SpotTileViewModelController", new HierarchicalLifetimeManager());

    //viewmodel controller factories
    childContainer.RegisterType<Func<SpotTileViewModel, IViewModelController>>(
        new HierarchicalLifetimeManager(),
        new InjectionFactory(c =>
            new Func<SpotTileViewModel, IViewModelController>(
                viewModel =>
                    c.Resolve<IViewModelController>("SpotTileViewModelController",
                        new DependencyOverride<SpotTileViewModel>(viewModel)))));

    //behaviours
    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());
            
            
    //services
    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:

C#
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.

 

 

ViewModel Navigation / Child Containers

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.

XML
<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>

 

 

Cleaning Up The IOC Services For The Navigation Request

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().

 

C#
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();
        }
    }
}

 

 

 

 

 

The ViewModel Controller And The Case Of The "ViewModel Behaviours"

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.

 

ViewModel/Controller Relationship

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 IDisposables, 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 IDisposables.

 

Here is the relevant code that makes this happen

 

VM code

C#
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

C#
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()
    {
        //start behaviors
        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.

C#
private IUnityContainer ConfigureSpotTileContainer()
{
    IUnityContainer childContainer = container.CreateChildContainer();

    //navigation windows
    childContainer.RegisterTypeForNavigationWithChildContainer<SpotTileViewModel>(
        new HierarchicalLifetimeManager());

    //viwemodel controllers
    childContainer.RegisterType<IViewModelController, SpotTileViewModelController>(
        "SpotTileViewModelController", new HierarchicalLifetimeManager());

    //viewmodel controller factories
    childContainer.RegisterType<Func<SpotTileViewModel, IViewModelController>>(
        new HierarchicalLifetimeManager(),
        new InjectionFactory(c =>
            new Func<SpotTileViewModel, IViewModelController>(
                viewModel =>
                    c.Resolve<IViewModelController>("SpotTileViewModelController",
                        new DependencyOverride<SpotTileViewModel>(viewModel)))));

    //behaviours
    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());
            
            
    //services
    childContainer.RegisterType<IFakeSpotRateProvider, 
	FakeSpotRateProvider>(new HierarchicalLifetimeManager());

    return childContainer;
}

 

 

ViewModel Behaviours

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:

C#
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:

 

  1. Listening to a certain property changing (such as an ID, which causes the whole entity to be fetched and populated from the DB)
  2. Listening to a group of related properties that MUST ALL be set before some action occurs
  3. 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:

 

Image 4

 

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

 

Reactive Commands

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

C#
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

C#
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)

 

C#
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));
            }));
    }
}

 

Further Case For RX

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:

C#
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.

 

Typical VM Behaviour Code

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

C#
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()
    {
        //listen for changes in the number of legs
        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

C#
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()
    {
        //listen for changes in the number of legs
        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()
    {
        //this.disposables.Dispose();
        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);

            //100/60 * 30 = 50% done
        var secPerSegment = Globals.TotalTimeoutInSeconds/Globals.ProgressSegments;
        spotTileViewModel.Progress = 
		(100/Globals.TotalTimeoutInSeconds) * (secPerSegment * counter+1);
    }
}

 

 

 

 

That's It

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.

 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionLoved it: and made a template for it Pin
Espen Røvik Larsen1-Jun-16 11:59
Espen Røvik Larsen1-Jun-16 11:59 
AnswerRe: Loved it: and made a template for it Pin
Sacha Barber2-Jun-16 11:02
Sacha Barber2-Jun-16 11:02 
GeneralRe: Loved it: and made a template for it Pin
Espen Røvik Larsen3-Jun-16 22:40
Espen Røvik Larsen3-Jun-16 22:40 
GeneralRe: Loved it: and made a template for it Pin
Sacha Barber3-Jun-16 23:49
Sacha Barber3-Jun-16 23:49 
QuestionVote of 5 Pin
Kenneth Haugland3-Feb-16 3:51
mvaKenneth Haugland3-Feb-16 3:51 
I found this article very interesting indeed, as well as some of the informed comments on it from others.

Well, some of the comments. "The king of WPF". I guess there have to be jesters as well then. Smile | :)

So I have some questions:
Given that all ViewModels implements Dispose, I wouldn't really need any Weak Subscriptions on events or properties would I?

The talks about the MVVM pattern, where you didn't like the way that everyone did it in their own way. I just started with it, and I think the MVVM pattern is way too weakly defined. I might have some distorted view of it all, but I see the View and the ViewModel basically as the equivalent of the old WinForm designer and code behind. I'm writing a user control article now, where my model is only a MEF importer.

The way you have done the IoC (or Dependency Injections, I assume that these two could be considered equal?) it made me feel as you wrote a child application within the application so that the big ViewModel is still there helping with the communication, but you don't really see it? Initially, because I'm really not very failiar with the concepts you use, found it hard to understand what was going on in the code, and its flow.

I did some searching on the web and I found one of the smart guys?:
AdaptiveConsulting/ReactiveTrader - C# - GitHub[^]
It had a trading app very close to your own, and a youtube link that had some very interestign Rx talks.

Oh, almost forgot, you get a 5 from me.
AnswerRe: Vote of 5 Pin
Sacha Barber3-Feb-16 21:48
Sacha Barber3-Feb-16 21:48 
SuggestionOverly complex Pin
Distantcam19-May-15 23:15
Distantcam19-May-15 23:15 
GeneralRe: Overly complex Pin
Sacha Barber20-May-15 0:06
Sacha Barber20-May-15 0:06 
Generalvery nice Pin
BillW3310-Apr-15 6:06
professionalBillW3310-Apr-15 6:06 
QuestionInteresting and more Pin
Florian Rappl1-Apr-15 5:46
professionalFlorian Rappl1-Apr-15 5:46 
AnswerRe: Interesting and more Pin
Sacha Barber7-Apr-15 12:47
Sacha Barber7-Apr-15 12:47 
GeneralRe: Interesting and more Pin
Florian Rappl9-Apr-15 6:34
professionalFlorian Rappl9-Apr-15 6:34 
QuestionIf we ever achieve the ability to input information directly into our brains (aka The Matrix) Pin
gardnerp17-Mar-15 5:29
gardnerp17-Mar-15 5:29 
AnswerRe: If we ever achieve the ability to input information directly into our brains (aka The Matrix) Pin
Sacha Barber17-Mar-15 9:01
Sacha Barber17-Mar-15 9:01 
QuestionFantastic stuff Pin
SteveTheThread15-Mar-15 22:46
SteveTheThread15-Mar-15 22:46 
AnswerRe: Fantastic stuff Pin
Sacha Barber16-Mar-15 0:28
Sacha Barber16-Mar-15 0:28 
GeneralOne of your best Pin
Daniel Vaughan12-Mar-15 6:29
Daniel Vaughan12-Mar-15 6:29 
GeneralRe: One of your best Pin
Sacha Barber12-Mar-15 7:19
Sacha Barber12-Mar-15 7:19 
GeneralRe: One of your best Pin
Pete O'Hanlon12-Mar-15 8:42
subeditorPete O'Hanlon12-Mar-15 8:42 
GeneralRe: One of your best Pin
William E. Kempf12-Mar-15 9:27
William E. Kempf12-Mar-15 9:27 
GeneralRe: One of your best Pin
Sacha Barber12-Mar-15 11:17
Sacha Barber12-Mar-15 11:17 
GeneralRe: One of your best Pin
William E. Kempf13-Mar-15 2:41
William E. Kempf13-Mar-15 2:41 
GeneralRe: One of your best Pin
Sacha Barber13-Mar-15 2:59
Sacha Barber13-Mar-15 2:59 
Question1500 VMs? Pin
SledgeHammer0111-Mar-15 9:12
SledgeHammer0111-Mar-15 9:12 
AnswerRe: 1500 VMs? Pin
Sacha Barber11-Mar-15 9:21
Sacha Barber11-Mar-15 9:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.