Khi tôi bắt đầu suy nghĩ làm thế nào để "kết hôn" MVVM và RX, điều đầu tiên tôi nghĩ đến là một 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);
}
}
Nhưng sau đó tôi nghĩ rằng "tiêu chuẩn" MVVM cách điều khiển ràng buộc để tài sản của ICommand không phải là rất RX'ish, nó phá vỡ sự kiện chảy vào khớp nối khá tĩnh.RX có nhiều thông tin hơn về sự kiện và nghe sự kiện định tuyến Executed có vẻ phù hợp. Dưới đây là những gì tôi đã đưa ra:
1) Bạn có một hành vi CommandRelay mà bạn cài đặt tại thư mục gốc của mỗi điều khiển người dùng mà nên đáp ứng với lệnh:
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 phục vụ điều khiển người dùng là kế thừa từ 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) Bạn không ràng buộc điều khiển để tính ICommand, nhưng sử dụng RoutedCommand thay vì:
public static class MyCommands
{
private static readonly RoutedUICommand _testCommand
= new RoutedUICommand();
public static RoutedUICommand TestCommand
{ get { return _testCommand; } }
}
Và trong XAML:
<Button x:Name="btn" Content="Test" Command="ViewModel:MyCommands.TestCommand"/>
Kết quả là, trên ViewModel của bạn, bạn có thể nghe các lệnh một cách rất RX:
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"));
}
Bây giờ, bạn có sức mạnh của lệnh định tuyến (bạn được tự do lựa chọn để xử lý lệnh trên bất kỳ hoặc thậm chí nhiều ViewModels trong hệ thống phân cấp), cộng với bạn có một "dòng chảy duy nhất" cho tất cả các lệnh đó là tốt hơn để RX hơn IObservable riêng biệt.
Nền tảng? Silverlight? – AnthonyWJones
Anthony: Có quan trọng không? –