Friday 12 December 2014

Mixing MVVM with RX - Part 3

Lately I have been playing around with F#, and I must admit I like the language a lot ! I think it is very expressive and very intuitive to use.

I personally think that the best way to learn a new language is to find a project that you are familiar with (you have implemented in the language you are familiar with) and then try to convert it to the new language.

So I decided to try and convert the RX MVVM framework that I showed in the first blog on this series and convert it to F#.

The first interface IPropertySubject is declared as :

type IPropertySubject<'t> =
    inherit IObservable<'t>
    abstract member Value : 't with get, set

Then the implementation looks like:

type PropertySubject<'t>() =
    let _subject : Subject<'t> = new Subject<'t>()
    let mutable _value : 't = Unchecked.defaultof<'t>
    let setValue v =
        _value <- v
        _subject.OnNext(v)
    member x.SetValues(obs : IObservable<'t>) =
        obs.Subscribe(fun v -> setValue v)
    member x.SetValue(value : 't) = setValue value

    interface IPropertySubject<'t> with
        member x.Value with get() = _value and set v = setValue v
        member x.Subscribe observer =
            _subject.Subscribe observer


The next interface is ICommandObserver. the interface is declared as follows:

type ICommandObserver<'t> =
    inherit ICommand
    abstract member Execute : IObservable<'t>  with get
    abstract member CanExecute : IObserver<bool> with get

And the implementation :

type CommandObserver<'t>(value) as self =
    let event = Event<_, _>()
    let _executeSubject = new Subject<'t>()
    let _canExecuteSubject = new Subject<bool>()
    let mutable _canExecute = value
    let disp = _canExecuteSubject.DistinctUntilChanged().Subscribe(fun v -> _canExecute <- v
                                                                            event.Trigger(self, EventArgs.Empty))
    new() = new CommandObserver<'t>(true)
    member x.SubscribeOnValues (values : IObservable<bool>)  =
        values.Subscribe(_canExecuteSubject)
    interface ICommandObserver<'t> with  
        member x.Execute with get() = _executeSubject.AsObservable()
        member x.CanExecute with get() = _canExecuteSubject.AsObserver()
                                                                  
    interface ICommand with  
        member x.add_CanExecuteChanged(e) = event.Publish.AddHandler(e)                                                                       
        member x.remove_CanExecuteChanged(e) = event.Publish.RemoveHandler(e)
        member x.Execute parameter =
            match parameter with            | :? 't -> _executeSubject.OnNext(parameter :?> 't)
            | _ -> _executeSubject.OnNext(Unchecked.defaultof<'t>)    
        member x.CanExecute parameter =
            _canExecute

The next step is to implement ViewModelBase. The implementation in F# looks like:

type ViewModelBase() =
    let event = Event<_, _>()
    member x.OnPropertyChanged(propName : string) =
        event.Trigger(x, new PropertyChangedEventArgs(propName))
    interface INotifyPropertyChanged with       
        member x.add_PropertyChanged(e) = event.Publish.AddHandler(e)
        member x.remove_PropertyChanged(e) = event.Publish.RemoveHandler(e)

One we have ViewModelBase, then it is time to declare IPropertyProvider interface. The declaration for the interface looks like:

type IPropertyProvider<'viewmodel> =
    inherit IDisposable
    abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> -> IPropertySubject<'ttype> 
    abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> * 'ttype -> IPropertySubject<'ttype> 
    abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> * IObservable<'ttype> -> IPropertySubject<'ttype> 
    abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> -> ICommandObserver<'ttype>
    abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> * bool -> ICommandObserver<'ttype>
    abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> * IObservable<bool> -> ICommandObserver<'ttype>


And the implementation is :

type PropertyProvider<'viewmodel>(viewModelBase : ViewModelBase, schedulers : ISchedulers) =
    let _viewModelBase = viewModelBase
    let _disposables = new CompositeDisposable()
 
    let getProperty (expr : Expression<System.Func<'viewmodel,'ttype>>) =
        let propName = (expr.Body :?> MemberExpression).Member.Name
        let propSubject = new PropertySubject<'t>()
        propSubject.SubscribeOn(schedulers.Dispatcher).Subscribe(fun _ -> _viewModelBase.OnPropertyChanged(propName))
        |> _disposables.Add 
        propSubject
 
    interface IPropertyProvider<'viewmodel> with
        member x.CreateProperty<'ttype> expr =
            let propSubject = getProperty expr
            propSubject :> IPropertySubject<'ttype>
 
        member x.CreateProperty<'ttype> (expr : Expression<System.Func<'viewmodel,'ttype>>, value : IObservable<'ttype>) =
            let propSubject = getProperty expr
            propSubject.SetValues(value) |> _disposables.Add
            propSubject :> IPropertySubject<'ttype>
 
        member x.CreateProperty<'ttype> (expr : Expression<System.Func<'viewmodel,'ttype>>, value : 'ttype) =
            let propSubject = getProperty expr
            propSubject.SetValue(value)
            propSubject :> IPropertySubject<'ttype>
                        
        member x.CreateCommand<'ttype> expr =
            new CommandObserver<'ttype>(true) :> ICommandObserver<'ttype>
 
        member x.CreateCommand<'ttype>(expr : Expression<System.Func<'viewmodel, ICommand>>, value : bool) =
            new CommandObserver<'ttype>(value) :> ICommandObserver<'ttype>
 
        member x.CreateCommand<'ttype>(expr : Expression<System.Func<'viewmodel, ICommand>>, values : IObservable<bool>) =
            let cmdObserver = new CommandObserver<'ttype>() 
            cmdObserver.SubscribeOnValues(values) |> _disposables.Add
            cmdObserver :> ICommandObserver<'ttype>
                
    interface IDisposable with
        member x.Dispose() =
            _disposables.Dispose()


The next step is to declare a factory that will create property provider. The interface for the factory looks like :

type IPropertyProviderFactory = 
    abstract member Create<'t> : ViewModelBase -> IPropertyProvider<'t>  

The implementation of the interface looks like :


type PropertyProviderFactory(schedulers : ISchedulers) =
    interface IPropertyProviderFactory with
        member x.Create<'t> (vm : ViewModelBase) =
            new PropertyProvider<'t>(vm, schedulers) :> IPropertyProvider<'t>


Next we need to implement message bus. The interface for the message bus is as follows :


type IMessageBus = 
    abstract member Subscribe<'t> : System.Action<'t> -> IDisposable
    abstract member Publish<'t> : 't -> unit

And the implementation is :


type MessageBus() =
    let _messageBus = new Subject<Object>()
    interface IMessageBus with
        member x.Subscribe<'t> onNext =
            _messageBus.OfType<'t>().Subscribe(onNext) 
        member x.Publish<'t>(value : 't)  =
            _messageBus.OnNext(value :> Object)

And lastly we will declare and implement schedulers. The interface for the schedulers looks like :



type ISchedulers =
    abstract member Dispatcher : IScheduler with get
    abstract member NewThread : IScheduler with get
    abstract member NewTask : IScheduler with get
    abstract member ThreadPool : IScheduler with get
    abstract member Timer : IScheduler with get


And the implementation is as follows:


type Schedulers(guiSynchronizationContext : IGuiSynchronizationContext) =
    let _dispatcher = new SynchronizationContextScheduler(guiSynchronizationContext.SynchronizationContext)
    interface ISchedulers with
        member x.Dispatcher with get() = _dispatcher :> IScheduler
        member x.NewThread with get() = Scheduler.NewThread :> IScheduler
        member x.NewTask with get() = Scheduler.TaskPool :> IScheduler
        member x.ThreadPool with get() = Scheduler.ThreadPool :> IScheduler
        member x.Timer with get() = Scheduler.Immediate :> IScheduler

As you can see from the code above, F# code is a lot more compact and has a lot less boilerplate and ceremony. 

I hope you find it interesting and helpful.




Friday 13 June 2014

Mixing MVVM with RX - Part 2

In the previous post I showed the implementation of the RX MVVM framework. On this post I will continue with implementing the demo application that will show how is the framework used. 

As I specified on the first post, the application is going to be an Address Book, which will capture most aspects of the framework but it is also simple enough so we don't get lost in the business logic.

So the application has the following features:
  • All persons will be displayed in a list on the main page
  • Details of the selected person can be viewed/edited by clicking Details button on the item on list
  • Selected person can be deleted from the list by clicking on Delete button on the item on list
  • A new person can be added

Firstly, we will create a window that will be used as a dialog window to display the details of the person. This window is a standard WPF window. But we want to make this window generic enough so it can display any viewmodel that we pass to it, so we will need to modify the constructor. The definition of the 
class (on code behind) looks like :

public partial class DialogWindow : Window
{
    public DialogWindow(ViewModelBase viewModel)
    {
        InitializeComponent();
        DataContext = viewModel;
 
        var closeable = viewModel as ICloseable;
        if (closeable != null)
        {
            closeable.Close.ObserveOnDispatcher().Subscribe(r =>
            {
                CloseResult = r;
                Close();
            });
        }
    }
 
    public bool CloseResult { getprivate set; }
}

and the XAML will look like :

<Window x:Class="AddressBook.Views.DialogWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModels="clr-namespace:AddressBook.ViewModels"
        xmlns:views="clr-namespace:AddressBook.Views"
        Title="DialogWindow" Height="300" Width="300" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Background="#FF737D81">
    
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewModels:PersonViewModel}">
            <views:PersonDetailsView />
        </DataTemplate>
    </Window.Resources>
    
    <Grid>
        <ContentControl Content="{Binding}"></ContentControl>
    </Grid>
</Window>

From the implementation we can see that constructor takes two parameters, the first one is an instance of a  ViewModelBase and the second on is an instance of ISchedulers. Then we set the window's DataContext to the viewmodel, and in XAML we define a DataTemplate that will be used for rendering. In our case we only have the PersonViewModel to be displayed. Then we check if the provided viewmodel implement ICloseable interface, which is declared as :


public interface ICloseable
{
IObservable<bool> Close { get; } 
}

and if the viewmodel implements the interface, then the windows subscribes to it's Close observable property and whenever a value is pushed to the Close observable the windows will close. This is to enable the viewmodel to close the window since the window does not know anything about our viewmodels or views (it uses the defined data template to render the viewmodel and nothing else).  So, in our case when user clicks on Cancel or Save buttons on the details view, then the dialog needs to be closed. Again we are using schedulers to schedule the closing of the window to happen on the dispatcher scheduler, thus allowing us to push a value to Close observable from any thread.

Now we will start implementing the viewmodels for our application, I will start with the person viewmodel interface. The interface is defined as:

public interface IPersonViewModel
{
    string Name { get; }
    string Surname { get; }
    int Age { get; }
    string Address1 { get; }
    string Address2 { get; }
    string Address3 { get; }
    string City { get; }
    ICommand EditCommand { get; }
    ICommand SaveCommand { get; }
    ICommand DeleteCommand { get; }
    ICommand CloseCommand { get; }
}

Next step is to create design data that can be used by Visual Studio WPF designer or Expression Blend. The design data is a class that implements IPersonViewModel interface and it is a simple class with some initial values set. The  implementation of the design data class looks like:

public class DPersonViewModel : IPersonViewModel
{
    public DPersonViewModel()
    {
        Name = "Joe";
        Surname = "Bloggs";
        Age = 40;
        Address1 = "Flat 1";
        Address2 = "Watergate House";
        Address3 = "Rotherhithe Street";
        City = "London";
    }
 
    public string Name { getset; }
    public string Surname { getset; }
    public int Age { getset; }
    public string Address1 { getset; }
    public string Address2 { getset; }
    public string Address3 { getset; }
    public string City { getset; }
    public ICommand EditCommand { getset; }
    public ICommand SaveCommand { getset; }
    public ICommand DeleteCommand { getset; }
    public ICommand CloseCommand { getset; }
}

Now we need to build the views. We have two different views for the PersonViewModel, one will be to display/edit details and the other will be to show the person data in the list. We will start first with the list item and it will be called PersonSummaryView. The XAML for the view will look like this:

<UserControl x:Class="AddressBook.Views.PersonSummaryView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:designData="clr-namespace:AddressBook.DesignData"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance Type=designData:DPersonViewModel, IsDesignTimeCreatable=True}"
             d:DesignHeight="55" d:DesignWidth="500" BorderBrush="DarkGray" BorderThickness="1" Background="#33FFFFFF">
    
    <UserControl.Resources>
        <ControlTemplate x:Key="LinkButton" TargetType="{x:Type Button}">
            <TextBlock HorizontalAlignment="Center" Text="{TemplateBinding Content}" Cursor="Hand" VerticalAlignment="Center" TextDecorations="Underline" Foreground="#FF0025BA"/>
        </ControlTemplate>
    </UserControl.Resources>
    <Grid Margin="5">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
            <TextBlock Text="{Binding Name}" FontSize="13" FontWeight="Bold"/>
            <TextBlock Text=" " FontSize="13" FontWeight="Bold"/>
            <TextBlock Text="{Binding Surname}" FontSize="13" FontWeight="Bold"/>
        </StackPanel>
 
        <StackPanel Orientation="vertical" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,70,0" Width="130">
            <TextBlock Text="{Binding Address1}" FontSize="8" HorizontalAlignment="Center"/>
            <TextBlock Text="{Binding Address2}" FontSize="8" HorizontalAlignment="Center"/>
            <TextBlock Text="{Binding Address3}" FontSize="8" HorizontalAlignment="Center"/>
            <TextBlock Text="{Binding City}" FontSize="8" HorizontalAlignment="Center"/>
        </StackPanel>
        
        <Button Template="{StaticResource LinkButton}" Command="{Binding EditCommand}" Content="Details" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,3,5,0" />
        <Button Template="{StaticResource LinkButton}" Command="{Binding DeleteCommand}" Content="Delete" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,5,3" />
 
    </Grid>
</UserControl>

And the control inside Visual Studio designer looks like:



and as we can see design data which are bound to the different controls and are displayed in the designer making styling the control very easy. The design data for the view is set using d:DataContext="{d:DesignInstance Type=dd:DPersonViewModel, IsDesignTimeCreatable=True}" at the beginning.
Next, we design the view to display person's details. Again we will use the same design data to style our view. After styling, the XAML for the view looks like:

<UserControl x:Class="AddressBook.Views.PersonDetailsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:designData="clr-namespace:AddressBook.DesignData"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance Type=designData:DPersonViewModel, IsDesignTimeCreatable=True }"
             Height="260" Width="330">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        
        <TextBlock Text="Name :" Grid.Column="0" Grid.Row="0" VerticalAlignment="Center" />
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" />
 
        <TextBlock Text="Surname :" Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" />
        <TextBox Text="{Binding Surname, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" />
 
        <TextBlock Text="Address1 :" Grid.Column="0" Grid.Row="2" VerticalAlignment="Center" />
        <TextBox Text="{Binding Address1, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="2" VerticalAlignment="Center" />
 
        <TextBlock Text="Address2 :" Grid.Column="0" Grid.Row="3" VerticalAlignment="Center" />
        <TextBox Text="{Binding Address2, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="3" VerticalAlignment="Center" />
 
        <TextBlock Text="Address3 :" Grid.Column="0" Grid.Row="4" VerticalAlignment="Center" />
        <TextBox Text="{Binding Address3, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" />
 
        <TextBlock Text="City :" Grid.Column="0" Grid.Row="5" VerticalAlignment="Center" />
        <TextBox Text="{Binding City, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="5" VerticalAlignment="Center" />
 
        <TextBlock Text="Age :" Grid.Column="0" Grid.Row="6" VerticalAlignment="Center" />
        <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="6" VerticalAlignment="Center" />        
        
        <Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="1" Grid.Row="7" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="80" Margin="0,0,95,0" />
        <Button Content="Cancel" Command="{Binding CloseCommand}" Grid.Column="1" Grid.Row="7" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="80" Margin="0,0,5,0" />
    </Grid>
</UserControl>

And the control on the designer will look like:



The next step is to define the data model for the person. The model is used to retrieve the data from the repository (database) and send the data to repository.  The data model will look like :

public class Person
{
    public string Name { getset; }
    public string Surname { getset; }
    public int Age { getset; }
    public string Address1 { getset; }
    public string Address2 { getset; }
    public string Address3 { getset; }
    public string City { getset; }
}

Now, before we dive in and implement the person viewmodel, lets first describe the tasks that this viewmodel will handle. First, the viewmodel will take a data model and populate the properties with the model values. Then, when user clicks on Details button on the summary view (PersonSummeryView) the viewmodel should bring up a dialog with the details so the user can view/edit the details. If the user changes details on the details dialog and clicks on Save button, then the model should be updated and the model should be saved into a repository. Otherwise if the user clicks on Cancel button then all the changes will be reverted and the dialog will be closed.

Next, when user clicks on Delete button on summary view the item should be deleted from the repository and the item should be removed from the list. Now, because the viewmodel does not have any knowledge about the container (in our case the list), it does not know how to delete itself an remove itself from the list. So instead of passing the parent viewmodel to it and tightly coupling them together, the viewmodel will publish a message to MessageBus and whoever listens for the message has the knowledge necessary to delete the viewmodel and remove it from the list.

So the implementation of the viewmodel looks like:

public class PersonViewModel : ViewModelBaseIPersonViewModelICloseable 
{
    private Person _model;
    private IPropertySubject<string> _name;
    private IPropertySubject<string> _surname;
    private IPropertySubject<int> _age;
    private IPropertySubject<string> _address1;
    private IPropertySubject<string> _address2;
    private IPropertySubject<string> _address3;
    private IPropertySubject<string> _city;
    private ICommandObserver<Unit> _editCommand;
    private ICommandObserver<Unit> _saveCommand;
    private ICommandObserver<Unit> _deleteCommand;
    private ICommandObserver<Unit> _closeCommand;
    private readonly ISubject<bool> _closeSubject = new Subject<bool>(); 
 
    public PersonViewModel(IPropertyProviderFactory propertyProviderFactory, IMessageBus messageBus, IDialogService dialogService, Person model)
    {
        _model = model;
        var pp = propertyProviderFactory.Create<IPersonViewModel>(this);
            
        // creating properties
        _name = pp.CreateProperty(i => i.Name, model.Name);
        _surname = pp.CreateProperty(i => i.Surname, model.Surname);
        _age = pp.CreateProperty(i => i.Age, model.Age);
        _address1 = pp.CreateProperty(i => i.Address1, model.Address1);
        _address2 = pp.CreateProperty(i => i.Address2, model.Address2);
        _address3 = pp.CreateProperty(i => i.Address3, model.Address3);
        _city = pp.CreateProperty(i => i.City, model.City);
 
        // creating commands
        _editCommand = pp.CreateCommand<Unit>(i => i.EditCommand);
        _saveCommand = pp.CreateCommand<Unit>(i => i.SaveCommand, !string.IsNullOrEmpty(model.Name) && !string.IsNullOrEmpty(model.Surname));
        _deleteCommand = pp.CreateCommand<Unit>(i => i.DeleteCommand);
        _closeCommand = pp.CreateCommand<Unit>(i => i.CloseCommand);
 
        // Observing commands
        ShouldDispose(_name.CombineLatest(_surname, (n,s) => !string.IsNullOrEmpty(n) && !string.IsNullOrEmpty(s)).Subscribe(_saveCommand.SetCanExecute));
        ShouldDispose(_saveCommand.OnExecute.Subscribe(_ =>
        {
            UpdateModel();
            _closeSubject.OnNext(true);
        }));
        ShouldDispose(_editCommand.OnExecute.Subscribe(_ => dialogService.ShowViewModel("Edit Person"this)));
        ShouldDispose(_deleteCommand.OnExecute.Subscribe(_ => messageBus.Publish(new DeletePersonMessage(this))));
        ShouldDispose(_closeCommand.OnExecute.Subscribe(_ =>
        {
            ResetData();
            _closeSubject.OnNext(false);
        }));
    }
 
    public string Name
    {
        get { return _name.Value;  }
        set { _name.Value = value; }
    }
 
    public string Surname
    {
        get { return _surname.Value; }
        set { _surname.Value = value; }
    }
 
    public int Age
    {
        get { return _age.Value; }
        set { _age.Value = value; }
    }
 
    public string Address1
    {
        get { return _address1.Value; }
        set { _address1.Value = value; }
    }
 
    public string Address2
    {
        get { return _address2.Value; }
        set { _address2.Value = value; }
    }
 
    public string Address3
    {
        get { return _address3.Value; }
        set { _address3.Value = value; }
    }
 
    public string City
    {
        get { return _city.Value; }
        set { _city.Value = value; }
    }
 
    public ICommand EditCommand
    {
        get { return _editCommand;  }
    }
 
    public ICommand SaveCommand
    {
        get { return _saveCommand; }
    }
 
    public ICommand DeleteCommand
    {
        get { return _deleteCommand; }
    }
 
    public ICommand CloseCommand
    {
        get { return _closeCommand; }
    }
 
    private void UpdateModel()
    {
        _model = this.ToModel();
    }
 
    private void ResetData()
    {
        Name = _model.Name;
        Surname = _model.Surname;
        Surname = _model.Surname;
        Age = _model.Age;
        Address1 = _model.Address1;
        Address2 = _model.Address2;
        Address3 = _model.Address3;
        City = _model.City;
    }
 
    IObservable<boolICloseable.Close
    {
        get { return _closeSubject.AsObservable();  }
    }
}

As we can see, everything happens in a constructor, so if we start disecting the constructor we can see that initially we create the properties and we set the initial values from the data model:

// creating properties
_name = pp.CreateProperty(i => i.Name, model.Name);
_surname = pp.CreateProperty(i => i.Surname, model.Surname);
_age = pp.CreateProperty(i => i.Age, model.Age);
_address1 = pp.CreateProperty(i => i.Address1, model.Address1);
_address2 = pp.CreateProperty(i => i.Address2, model.Address2);
_address3 = pp.CreateProperty(i => i.Address3, model.Address3);
_city = pp.CreateProperty(i => i.City, model.City);


then we create the commands :

// creating commands
_editCommand = pp.CreateCommand<Unit>(i => i.EditCommand);
_saveCommand = pp.CreateCommand<Unit>(i => i.SaveCommand, !string.IsNullOrEmpty(model.Name) && !string.IsNullOrEmpty(model.Surname));
_deleteCommand = pp.CreateCommand<Unit>(i => i.DeleteCommand);
_closeCommand = pp.CreateCommand<Unit>(i => i.CloseCommand);

As we can see, save command initially will be enabled if the Name and Surname properties on the datamodel are not null or empty.

ShouldDispose(_name.CombineLatest(_surname, (n,s) => !string.IsNullOrEmpty(n) && !string.IsNullOrEmpty(s)).Subscribe(_saveCommand.SetCanExecute));

In the line above we combine _name and _surname observables using CombineLatest() method, and then we subscribe to the result by providing save command's SetCanExecute property (which is an observer). So anytime a new value is pushed on either name or surname observable, the provided function will get the values of both observables and will return another value (in our case it will produce a boolean value depending if the values of the name and surname are not null or empty). Then the result produced by the function will be pushed into an observable, which in turn will be pushed ino SetCanExecute thus enabling/disabling the control depending on the result (Save button).

Next, on the following piece of code :

ShouldDispose(_saveCommand.OnExecute.Subscribe(_ =>
{
    UpdateModel();
    _closeSubject.OnNext(true);
}));

we subscribe to _saveCommand.OnExecute and anytime  the SaveCommand  command is executed (by clicking on Save button on details view in our case) we update the data model and push a value to Close observable thus closing the dialog.

On the next line :


ShouldDispose(_editCommand.OnExecute.Subscribe(_ => dialogService.ShowViewModel("Edit Person"this)));

we subscribe to _editCommand.OnExecute and whenever the edit command is executed (by clicking Detailsbutton on summary view) then we, instead of creating the dialog ourselves in the viewmodel, we delegate it to the dialog service (by calling ShowViewModel() method). In this way, as we said before when we write unit tests we can fake the dialog service and we are also adhering to SRP (Single Responsibility Principle) principle in our viewmodel.
then on the next line :

ShouldDispose(_deleteCommand.OnExecute.Subscribe(_ => messageBus.Publish(new DeletePersonMessage(this))));

we are subscribing to the delete command and whenever it gets executed (by clicking on Delete button on the summary view) then we will publish a new DeleteMessage on the message bus. So whoever is listening to this message can react on it.

And lastly, on the next block:

ShouldDispose(_closeCommand.OnExecute.Subscribe(_ =>
{
    ResetData();
    _closeSubject.OnNext(false);
}));

we are subscribing to Close command, so whenever the user clicks on Close button on the details view, then we are reverting property values and closing the dialog (by pushing a value to Close observable defined inICloseable interface and implemented in this viewmodel).

The next step is to define and implement dialog service. The definition of the dialog service looks like :

public interface IDialogService
{
    void ShowInfo(string title, string message);
    MessageBoxResult ShowWarning(string title, string message);
    void ShowError(string title, string message);
    bool ShowViewModel(string title, ViewModelBase viewModel);
}

And the implementation looks like :

public class DialogService : IDialogService
{
public void ShowInfo(string title, string message)
{
    MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Information);
}
 
public MessageBoxResult ShowWarning(string title, string message)
{
    return MessageBox.Show(message, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning);
}
 
public void ShowError(string title, string message)
{
    MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
 
public bool ShowViewModel(string title, ViewModelBase viewModel)
{
    var win = new DialogWindow(viewModel) {Title = title};
    win.ShowDialog();
 
    return win.CloseResult;
}
}

Now we need to define and implement the repository that will hold person objects. The definition of the repository looks like:

public interface IPersonRepository
{
    IObservable<IEnumerable<Person>> GetAllClients();
    IObservable<bool> Save(Person person);
    IObservable<bool> Delete(Person person);
}

As we can see from the definition for the interface, all the methods are declared as observables. This allows us to subscribe to the method and react when we get the data making the methods asynchronous.

For the purpose of this application we will only hold the data on the list in memory, but for a real application we would implement this interface so the data would be stored in a database.

The implementation of the repository interface looks like:

public class PersonRepository : IPersonRepository
{
    private readonly List<Person> _clients;
 
    public PersonRepository()
    {
        _clients = new List<Person>
        {
            new Person { Name = "Joe", Surname = "Blogs", Address1 = "Flat 2", Address2 = "19 Fleet Street", Address3 = "City", City = "London", Age = 21 },
            new Person { Name = "Mary", Surname = "Jones", Address1 = "Flat 21", Address2 = "Rotherhithe Street", Address3 = "Calder Court", City = "New York", Age = 28 }
        };
    }
 
    public IObservable<IEnumerable<Person>> GetAllClients()
    {
        return ToObservable(() =>
        {
            IEnumerable<Person> cl = _clients;
            Thread.Sleep(1500);
            return cl;
        });
    }
 
    public IObservable<bool> Save(Person person)
    {
        return ToObservable(() =>
        {
            _clients.Add(person);
            Thread.Sleep(100);
            return true;
        });
    }
 
    public IObservable<bool> Delete(Person person)
    {
        return ToObservable(() =>
        {
            var cl = _clients.FirstOrDefault(c => c.Name == person.Name && c.Surname == person.Surname);
            if (cl != null)
            {
                _clients.Remove(person);
                Thread.Sleep(800);
                return true;                    
            }
 
            return false;
        });
    }
 
    private IObservable<T> ToObservable<T>(Func<T> func)
    {
        return func.ToAsync()();
    }
}

As we can see from the implementation of the private method ToObservable<>(), RX has another cool feature that can convert a function to an async functions that returns an observable. To mimic a long running process we also have added a Thread.Sleep for 1 second when we call GetAllPersons() and Save() methods.

The next step is to define main viewmodel. The main viewmodel will be responsible for getting all persons from the repository and displaying them in a list view, adding a new person, and deleting the appropriate person from the list. So the definition looks like :

public interface IMainViewModel
{
    ObservableCollection<IPersonViewModel> Clients { get; }
    ICommand AddNewClientCommand { get; }
    bool IsBusy { get; }
}

And the implementation looks like :


public class MainViewModel : ViewModelBaseIMainViewModel
{
    private readonly ICommandObserver<Unit> _addNewClientCommand;
    private readonly IPropertySubject<bool> _isBusy;
 
    public MainViewModel(IPropertyProviderFactory propertyProviderFactory, IMessageBus messageBus, IDialogService dialogService, IPersonRepository personRepository, IPersonViewModelFactory personViewModelFactory)
    {
        var pp = propertyProviderFactory.Create<IMainViewModel>(this);
            
        //create properies
        _isBusy = pp.CreateProperty(i => i.IsBusy, false);
 
        // create commands
        _addNewClientCommand = pp.CreateCommand<Unit>(i => i.AddNewClientCommand);
 
        Clients = new ObservableCollection<IPersonViewModel>();
 
        IsBusy = true;
        ShouldDispose(personRepository.GetAllClients().ObserveOnDispatcher().Subscribe(c =>
        {
            foreach (var item in c.Select(personViewModelFactory.Create))
            {
                Clients.Add(item);
            }
 
            IsBusy = false;
        }));
 
        ShouldDispose(messageBus.Subscribe<DeletePersonMessage>(m =>
        {
            var msg = string.Format("Are you sure you want to delete {0} {1}?", m.Person.Name, m.Person.Surname);
            if (dialogService.ShowWarning("Delte", msg) == MessageBoxResult.OK)
            {
                IsBusy = true;
                ShouldDispose(personRepository.Delete(m.Person.ToModel())
                                                .ObserveOnDispatcher()
                                                .Subscribe(_ =>
                                                {
                                                    Clients.Remove(m.Person);
                                                    IsBusy = false;                                                      
                                                }));
            }
        }));
 
        ShouldDispose(_addNewClientCommand.OnExecute.Subscribe(_ =>
        {
            var newClient = personViewModelFactory.Create(new Person());
            if (dialogService.ShowViewModel("New Person", newClient as ViewModelBase))
            {
                IsBusy = true;
                ShouldDispose(personRepository.Save(newClient.ToModel())
                                                .ObserveOnDispatcher()
                                                .Subscribe(u =>
                                                {
                                                    Clients.Add(newClient);
                                                    IsBusy = false;
                                                }));
            }
        }));
    }
 
    public ObservableCollection<IPersonViewModel> Clients { getprivate set; }
 
    public ICommand AddNewClientCommand
    {
        get { return _addNewClientCommand; }
    }
 
    public bool IsBusy
    {
        get { return _isBusy.Value; }
        set { _isBusy.Value = value; }
    }
}


So if we dissect the constructor again, we will see that at the beginning we create properties:

//create properies
_isBusy = pp.CreateProperty(i => i.IsBusy, false);
 
// create commands
_addNewClientCommand = pp.CreateCommand<Unit>(i => i.AddNewClientCommand);
 
Clients = new ObservableCollection<IPersonViewModel>();


On next block of code we retrieve all persons from the repository :

ShouldDispose(personRepository.GetAllClients().ObserveOnDispatcher().Subscribe(c =>
{
    foreach (var item in c.Select(personViewModelFactory.Create))
    {
        Clients.Add(item);
    }
 
    IsBusy = false;
}));


Then, on the next block we subscribe to DeleteMessage on message bus, and whenever delete message is published on the message bus, we react to it by deleting the selected person from the list:


ShouldDispose(messageBus.Subscribe(m => { var msg = string.Format("Are you sure you want to delete {0} {1}?", m.Person.Name, m.Person.Surname); if (dialogService.ShowWarning("Delte", msg) == MessageBoxResult.OK) { IsBusy = true; ShouldDispose(personRepository.Delete(m.Person.ToModel()) .ObserveOnDispatcher() .Subscribe(_ => { Clients.Remove(m.Person); IsBusy = false; })); } }));
And lastly, we subscribe to _addNewClient command, so whenever the user clicks on the button, we display the dialog for the user to enter the details, and once the details have been entered and the user clicks on Save button, then the new person is stored in the repository:


ShouldDispose(_addNewClientCommand.OnExecute.Subscribe(_ =>
{
    var newClient = personViewModelFactory.Create(new Person());
    if (dialogService.ShowViewModel("New Person", newClient as ViewModelBase))
    {
        IsBusy = true;
        ShouldDispose(personRepository.Save(newClient.ToModel())
                                        .ObserveOnDispatcher()
                                        .Subscribe(u =>
                                        {
                                            Clients.Add(newClient);
                                            IsBusy = false;
                                        }));
    }
}));

The last thing left to do in our app is to register the types with the IoC container, and set the DataContext of the main window. We do this in code-behind in App.xaml. Our code-behind code looks like:

public partial class App : Application
{
    private IContainer _container;
    private readonly ContainerBuilder _containerBuilder;
 
    public App()
    {
        _containerBuilder = new ContainerBuilder();
 
        // Register MVVM dependencies
        (new RX_MVVM.Bootstrapper()).Register(_containerBuilder);
 
        // Register Viewmodels
        _containerBuilder.RegisterType<MainViewModel>().As<IMainViewModel>().SingleInstance();
        _containerBuilder.RegisterType<PersonViewModelFactory>().As<IPersonViewModelFactory>().SingleInstance();
        _containerBuilder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
        _containerBuilder.RegisterType<PersonRepository>().As<IPersonRepository>().SingleInstance();
    }
 
    protected override void OnStartup(StartupEventArgs e)
    {
        try
        {
            ShutdownMode = ShutdownMode.OnMainWindowClose;
            _container = _containerBuilder.Build();
 
            var mainWidow = new MainWindow {DataContext = _container.Resolve<IMainViewModel>()};
            MainWindow = mainWidow;
            mainWidow.Show();
            base.OnStartup(e);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
 
        }
            
    }
}

The application that was build as an example is very simple but it give you a glimpse of the features of RX. RX is a very powerful framework which can simplify a lot of things in the programming world especially asynchronous programming.