Frage

Eines der wichtigsten Beispiele verwendet werden, um die Leistung von Reactive Extensions (Rx) zu erklären, ist die Kombination von bestehenden Mausereignissen in ein neues ‚Ereignis‘ darstellt Delta während Maus ziehen:

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;

Quelle: Matthew Podwysocki der Einführung in der Reactive Rahmen Serie .

In MVVM Ich strebe im Allgemeinen meiner .xaml.cs Datei so leer wie möglich und eine Art und Weise zu halten Ereignisse Einhaken aus der Sicht mit Befehlen in dem Viewmodel rein in Markup wird mit einem Verhalten:

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

Quelle: Brian Genisio

Das Reactive Rahmen scheint ausgerichtet zu sein auf die traditionellen MVC-Muster, wo ein Controller die Ansicht kennt und ihre Veranstaltungen direkt verweisen können.

Aber, ich mag beide haben meinen Kuchen essen und ihn!

Wie würden Sie diese beiden Muster kombinieren?

War es hilfreich?

Lösung

Ich habe einen Rahmen geschrieben, dass meine Erkundungen in dieser Frage ReactiveUI

genannt repräsentiert

Es setzt sowohl eine beobachtbare ICommand, sowie Ansichtsmodell Objekte, die Änderungen Signal über einen IObservable, sowie die Fähigkeit, „assign“ ein IObservable auf eine Eigenschaft, die dann INotifyPropertyChange feuern, wenn sein IObservable ändert. Sie kapselt auch viele gemeinsame Muster, wie eine ICommand mit, die eine Aufgabe im Hintergrund ausgeführt wird, dann Marshalls das Ergebnis zurück an der Benutzeroberfläche.

Ich habe absolut null Dokumentation jetzt, aber ich werde diese Informationen in den kommenden Tagen arbeiten auf Hinzufügen, sowie eine Beispielanwendung Ich habe codiert up

UPDATE: Ich habe jetzt ziemlich viel Dokumentation auf Besuche http: // www .reactiveui.net

Andere Tipps

Die Lösung für mein Problem erwies sich als eine Klasse zu erstellen, die Geräte sowohl ICommand und IObservable

ICommand wird verwendet, um die UI (mit Verhalten) zu binden und IObservable kann dann innerhalb des View-Modells verwendet werden, um Verbundereignisströme zu konstruieren.

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

Wenn beobachtbare gezeigt wird in Ausführungs IObservable von Grund auf neu

Als ich anfing zu denken, wie man „heiraten“ MVVM und RX, das erste, was ich dachte, war ein 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);
    }
}

Aber dann dachte ich, dass die „Standard“ MVVM Weise Kontrollen ICommand die Eigenschaften der Bindung ist nicht sehr RX'ish, es bricht den Ereignisablauf in ziemlich statisch Kupplungen. RX ist mehr über Veranstaltungen, und das Hören zu einem Ausgeführt gerouteten Ereignis erscheint angemessen. Hier ist, was ich kam mit:

1) Sie haben ein CommandRelay Verhalten, das Sie an der Wurzel jeder Benutzersteuerung installieren, die auf Befehle reagieren soll:

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) Ansichtsmodell dem Benutzer die Kontrolle dient, wird von der ReactiveViewModel geerbt:

    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) Sie müssen nicht binden Steuerelemente ICommand Eigenschaften, sondern verwenden RoutedCommand die statt:

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

Und in XAML:

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

Als Ergebnis auf Ihrem Viewmodel Sie auf die Befehle in einer sehr RX Art und Weise hören können:

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

Jetzt haben Sie die Macht der gerouteten Befehle (Sie sind frei, den Befehl auf eine oder sogar mehrere Viewmodels in der Hierarchie wählen zu handhaben), und Sie haben einen „Einstrom“ für alle Befehle, die nicier zu RX als getrennte IObservable Jahren.

Dies sollte auch über die ReactiveFramework, perfekt machbar sein.

Die einzige Änderung wäre erforderlich, um ein Verhalten für diese zu schaffen, haben dann das Verhalten Haken auf den Befehl auf. Es wäre in etwa so aussehen:

<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 erkennen, dass EventCommand ist in einer sehr ähnlichen Art und Weise arbeiten, wie die ReactiveFramework funktionieren würde, in diesem Szenario. Sie werden nicht wirklich einen Unterschied sehen, obwohl die Umsetzung des EventCommand vereinfacht werden würde.

EventCommand bereits ein Push-Modell für Sie bereitstellt - wenn das Ereignis eintritt, es feuert Ihren Befehl. Das ist das Haupteinsatzszenario für Rx, aber es macht die Implementierung einfach.

Ich denke, die Idee war, ein Ereignis „Akkord“ zu schaffen, in diesem Fall eine Drag-Operation wahrscheinlich, die Ergebnisse in einem Befehl aufgerufen werden? Dies würde so ziemlich die gleiche Art und Weise durchgeführt werden Sie es in dem Code-Behind tun würden, aber mit dem Code in einem Verhalten. Um zum Beispiel eine DragBehavior erstellen, die Rx der MouseDown- / Mouse / MouseUp Ereignisse mit einem Befehl zu kombinieren, nutzt so genannten neues „Ereignis“ zu behandeln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top