无功扩展(Rx)和#x2B;MVVM=?

无功扩展(Rx)和#x2B;MVVM=?,mvvm,system.reactive,reactive-programming,Mvvm,System.reactive,Reactive Programming,用于解释无功扩展(Rx)功能的一个主要示例是将现有鼠标事件组合成一个新的“事件”,表示鼠标拖动期间的增量: var mouseMoves = from mm in mainCanvas.GetMouseMove() let location = mm.EventArgs.GetPosition(mainCanvas) select new { location.X, location.Y}; var mouseDiffs =

用于解释无功扩展(Rx)功能的一个主要示例是将现有鼠标事件组合成一个新的“事件”,表示鼠标拖动期间的增量:

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;
来源:

在MVVM中,我通常会尽量保持.xaml.cs文件为空,而将视图中的事件与viewmodel中的命令连接起来的一种方法是使用以下行为:

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

来源:

反应式框架似乎更倾向于传统的MVC模式,其中控制器知道视图并可以直接引用其事件

但是,我想既吃蛋糕又吃蛋糕


如何将这两种模式结合起来?

这也应该完全可以通过ReactiveFramework实现

唯一需要的更改是为此创建一个行为,然后将该行为连接到命令。它看起来像:

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

请注意,在本场景中,EventCommand的工作方式与ReactiveFramework的工作方式非常相似。虽然EventCommand的实现会被简化,但您不会看到真正的区别


EventCommand已经在为您提供推送模型-当事件发生时,它会激发您的命令。这是Rx的主要使用场景,但它使实现变得简单。

我认为这个想法是创建一个事件“chord”,在这种情况下可能是一个拖动操作,这会导致调用一个命令?这与您在codebehind中执行的方式几乎相同,但代码是在行为中执行的。例如,创建一个DragBehavior,它使用Rx将MouseDown/MouseMove/MouseUp事件与一个用于处理新“事件”的命令相结合。

我的问题的解决方案是创建一个同时实现ICommand和IObservable的类

ICommand用于绑定UI(使用行为),然后可以在视图模型中使用IObservable来构建复合事件流

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);
            }
        }
    }
}
使用系统;
使用System.Windows.Input;
名称空间杰斯佩尔
{
类ObservableCommand:Observable,ICommand,其中T:EventArgs
{
bool ICommand.CanExecute(对象参数)
{
返回true;
}
事件事件处理程序ICommand.CanExecuteChanged
{
添加{}
删除{}
}
void ICommand.Execute(对象参数)
{
尝试
{
OnNext((T)参数);
}
捕获(无效卡斯特例外e)
{
OnError(e);
}
}
}
}

当我开始思考如何将MVVM和RX“结合”起来时,我想到的第一件事是一个可观察命令:

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);
    }
}
在XAML中:

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

现在,您拥有路由命令的功能(您可以自由选择在层次结构中的任何甚至多个ViewModels上处理命令),此外,对于所有命令,您都有一个“单一流”,它比单独的IObservable命令更适合RX。

我已经编写了一个框架,它代表了我在这个问题中的探索,名为

它实现了一个可观察的ICommand,以及通过IObservable发出更改信号的ViewModel对象,还实现了将IObservable“分配”给属性的功能,该属性在其IObservable更改时将触发INotifyPropertyChange。它还封装了许多常见模式,比如让一个ICommand在后台运行任务,然后将结果封送回UI

我现在绝对没有文档,但我将在接下来的几天里努力添加这些信息,以及我编写的一个示例应用程序


更新:我现在有很多文档,请查看

Anthony:这有关系吗?我不仅仅是在寻找推送模型-我知道command提供了这些。我正在寻找一种方法,将现有事件组合到ViewModel中的新事件中,而不是在代码中。这是我最初的想法,如果您创建的新事件“足够”可重用,那么将其包装到新行为中可能是值得的。但我真的在寻找一种更灵活的一次性事件混合方式。你的项目看起来很有趣,期待文档和示例应用程序!是一个关于一个主要类的帖子,一个反应性的ICommanda作为一个经验丰富的WPF开发人员,我可以说反应性UI背后的想法非常好,推荐!一些不请自来的建议与ReactiveUI很好地结合在一起:Nito计算属性[。它几乎没有开销,可以随时插入现有项目中,我将其用于所有简单的计算属性通知,并求助于Rx/ReactiveUI以获得更高级的观察结果。
    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);
        }
    }
public static class MyCommands
{
    private static readonly RoutedUICommand _testCommand 
       = new RoutedUICommand();
    public static RoutedUICommand TestCommand 
      { get { return _testCommand; } }
}
<Button x:Name="btn" Content="Test" Command="ViewModel:MyCommands.TestCommand"/>
    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"));
    }