views:

1947

answers:

5

One of the main examples being used to explain the power of Reactive Extensions (Rx) is combining existing mouse events into a new 'event' representing deltas during mouse drag:

var mouseMoves = from mm in mainCanvas.GetMouseMove()
                 let location = mm.EventArgs.GetPosition(mainCanvas)
                 select new { location.X, location.Y};

var mouseDiffs = mouseMoves
    .Skip(1)
    .Zip(mouseMoves, (l, r) => new {X1 = l.X, Y1 = l.Y, X2 = r.X, Y2 = r.Y});

var mouseDrag = from _  in mainCanvas.GetMouseLeftButtonDown()
                from md in mouseDiffs.Until(
                    mainCanvas.GetMouseLeftButtonUp())
                select md;

Source: Matthew Podwysocki's Introduction to the Reactive Framework series.

In MVVM I generally strive to keep my .xaml.cs file as empty as possible and one way of hooking up events from the view with commands in the viewmodel purely in markup is using a behavior:

<Button Content="Click Me">
    <Behaviors:Events.Commands>
        <Behaviors:EventCommandCollection>
            <Behaviors:EventCommand CommandName="MouseEnterCommand" EventName="MouseEnter" />
            <Behaviors:EventCommand CommandName="MouseLeaveCommand" EventName="MouseLeave" />
            <Behaviors:EventCommand CommandName="ClickCommand" EventName="Click" />
        </Behaviors:EventCommandCollection>
    </Behaviors:Events.Commands>
</Button>

Source: Brian Genisio.

The Reactive Framework seems to be more geared towards the traditional MVC pattern where a controller knows the view and can reference its events directly.

But, I want to both have my cake and eat it!

How would you combine these two patterns?

+1  A: 

This should be perfectly doable via the ReactiveFramework, as well.

The only change required would be to create a behavior for this, then have the behavior hook up to the Command. It would look something like:

<Button Content="Click Me">
    <Behaviors:Events.Commands>
        <Behaviors:EventCommandCollection>
            <Behaviors:ReactiveEventCommand CommandName="MouseEnterCommand" EventName="MouseEnter" />
            <Behaviors:ReactiveEventCommand CommandName="MouseLeaveCommand" EventName="MouseLeave" />
            <Behaviors:ReactiveEventCommand CommandName="ClickCommand" EventName="Click" />
        </Behaviors:EventCommandCollection>
    </Behaviors:Events.Commands>
</Button>

Just realize that EventCommand is working in a very similar way to how the ReactiveFramework would work, in this scenario. You won't really see a difference, although the implementation of EventCommand would be simplified.

EventCommand already is providing a push model for you - when the event happens, it fires your command. That's the main usage scenario for Rx, but it makes the implementation simple.

Reed Copsey
I'm not just looking for a push model - I know that's what command provides. I'm looking for a way of combining existing events into new events within my ViewModel instead of in the code-behind
jesperll
A: 

I think the idea was to create an event "chord", in this case a drag operation probably, which results in a command being called? This would be done pretty much the same way you'd do it in the codebehind, but with the code in a behavior. For example, create a DragBehavior that uses Rx to combine the MouseDown/MouseMove/MouseUp events with a command called to handle the new "event".

wekempf
That was my initial idea and it might be worth the trouble to wrap it in a new behavior if the new events you create are reusable 'enough'. But I am really looking for a more flexible way of one-off event mixing.
jesperll
+3  A: 

The solution to my problem turned out to be to create a class that implements both ICommand and IObservable<T>

ICommand is used to bind the UI (using behaviors) and IObservable can then be used within the view model to construct composite event streams.

using System;
using System.Windows.Input;

namespace Jesperll
{
    class ObservableCommand<T> : Observable<T>, ICommand where T : EventArgs
    {
        bool ICommand.CanExecute(object parameter)
        {
            return true;
        }

        event EventHandler ICommand.CanExecuteChanged
        {
            add { }
            remove { }
        }

        void ICommand.Execute(object parameter)
        {
            try
            {
                OnNext((T)parameter);
            }
            catch (InvalidCastException e)
            {
                OnError(e);
            }
        }
    }
}

Where Observable<T> is shown in Implementing IObservable from scratch

jesperll
+2  A: 

When I started to think of how to "marry" MVVM and RX, the first thing I thought of was an ObservableCommand:

public class ObservableCommand : ICommand, IObservable<object>
{
    private readonly Subject<object> _subj = new Subject<object>();

    public void Execute(object parameter)
    {
        _subj.OnNext(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public IDisposable Subscribe(IObserver<object> observer)
    {
        return _subj.Subscribe(observer);
    }
}

But then I thought that the "standard" MVVM way of binding controls to ICommand's properties is not very RX'ish, it breaks the event flow into fairly static couplings. RX is more about events, and listening to an Executed routed event seems appropriate. Here is what I came up with:

1) You have a CommandRelay behavior which you install at the root of each user control which should respond to commands:

public class CommandRelay : Behavior<FrameworkElement>
{
    private ICommandSink _commandSink;

    protected override void OnAttached()
    {
        base.OnAttached();
        CommandManager.AddExecutedHandler(AssociatedObject, DoExecute);
        CommandManager.AddCanExecuteHandler(AssociatedObject, GetCanExecute);
        AssociatedObject.DataContextChanged 
          += AssociatedObject_DataContextChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        CommandManager.RemoveExecutedHandler(AssociatedObject, DoExecute);
        CommandManager.RemoveCanExecuteHandler(AssociatedObject, GetCanExecute);
        AssociatedObject.DataContextChanged 
          -= AssociatedObject_DataContextChanged;
    }

    private static void GetCanExecute(object sender, 
        CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void DoExecute(object sender, ExecutedRoutedEventArgs e)
    {
        if (_commandSink != null)
            _commandSink.Execute(e);
    }

    void AssociatedObject_DataContextChanged(
       object sender, DependencyPropertyChangedEventArgs e)

    {
        _commandSink = e.NewValue as ICommandSink;
    }
}

public interface ICommandSink
{
    void Execute(ExecutedRoutedEventArgs args);
}

2) ViewModel serving the user control is inherited from the ReactiveViewModel:

    public class ReactiveViewModel : INotifyPropertyChanged, ICommandSink
    {
        internal readonly Subject<ExecutedRoutedEventArgs> Commands;

        public ReactiveViewModel()
        {
            Commands = new Subject<ExecutedRoutedEventArgs>();
        }

...
        public void Execute(ExecutedRoutedEventArgs args)
        {
            args.Handled = true;  // to leave chance to handler 
                                  // to pass the event up
            Commands.OnNext(args);
        }
    }

3) You do not bind controls to ICommand properties, but use RoutedCommand's instead:

public static class MyCommands
{
    private static readonly RoutedUICommand _testCommand 
       = new RoutedUICommand();
    public static RoutedUICommand TestCommand 
      { get { return _testCommand; } }
}

And in XAML:

<Button x:Name="btn" Content="Test" Command="ViewModel:MyCommands.TestCommand"/>

As a result, on your ViewModel you can listen to the commands in a very RX way:

    public MyVM() : ReactiveViewModel 
    {
        Commands
            .Where(p => p.Command == MyCommands.TestCommand)
            .Subscribe(DoTestCommand);
        Commands
            .Where(p => p.Command == MyCommands.ChangeCommand)
            .Subscribe(DoChangeCommand);
        Commands.Subscribe(a => Console.WriteLine("command logged"));
    }

Now, you have the power of routed commands (you are free to choose to handle the command on any or even multiple ViewModels in the hierarchy), plus you have a "single flow" for all the commands which is nicier to RX than separate IObservable's.

Sergey Aldoukhov
A: 

I've written a framework that represents my explorations in this question called ReactiveXaml

It implements both an Observable ICommand, as well as ViewModel objects who signal changes via an IObservable, as well as the ability to "assign" an IObservable to a property, who will then fire INotifyPropertyChange whenever its IObservable changes. It also encapsulates a lot of common patterns, like having an ICommand who runs a Task in the background, then marshalls the result back to the UI.

I have absolutely zero documentation up right now, but I'll be working on adding that information over the coming days, as well as a sample application I've coded up

Paul Betts
Your project looks interesting, looking forward to the docs and the example app!
Markus Johnsson
http://blog.paulbetts.org/index.php/2010/06/22/reactivexaml-series-reactivecommand/ is a post on one of the main classes, a Reactive ICommand
Paul Betts