无功扩展(Rx)和#x2B;MVVM=?
用于解释无功扩展(Rx)功能的一个主要示例是将现有鼠标事件组合成一个新的“事件”,表示鼠标拖动期间的增量:无功扩展(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 =
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"));
}