好的,这就是我设法完成这项工作的方式
public interface IReactiveCommand : ICommand
{
IObservable<object> CommandExecutedStream { get; }
IObservable<Exception> CommandExeceptionsStream { get; }
void AddPredicate(IObservable<bool> predicate);
}
然后是实际的命令实现
public class ReactiveCommand : IReactiveCommand, IDisposable
{
private Subject<object> commandExecutedSubject = new Subject<object>();
private Subject<Exception> commandExeceptionsSubjectStream = new Subject<Exception>();
private List<IObservable<bool>> predicates = new List<IObservable<bool>>();
private IObservable<bool> canExecuteObs;
private bool canExecuteLatest = true;
private CompositeDisposable disposables = new CompositeDisposable();
public ReactiveCommand(IObservable<bool> initPredicate, bool initialCondition)
{
if (initPredicate != null)
{
canExecuteObs = initPredicate;
SetupSubscriptions();
}
RaiseCanExecute(initialCondition);
}
private void RaiseCanExecute(bool value)
{
canExecuteLatest = value;
this.raiseCanExecuteChanged(EventArgs.Empty);
}
public ReactiveCommand()
{
RaiseCanExecute(true);
}
private void SetupSubscriptions()
{
disposables = new CompositeDisposable();
disposables.Add(this.canExecuteObs.Subscribe(
//OnNext
x =>
{
RaiseCanExecute(x);
},
//onError
commandExeceptionsSubjectStream.OnNext
));
}
public void AddPredicate(IObservable<bool> predicate)
{
disposables.Dispose();
predicates.Add(predicate);
this.canExecuteObs = this.canExecuteObs.CombineLatest(predicates.Last(), (a, b) => a && b).DistinctUntilChanged();
SetupSubscriptions();
}
bool ICommand.CanExecute(object parameter)
{
return canExecuteLatest;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
commandExecutedSubject.OnNext(parameter);
}
public IObservable<object> CommandExecutedStream
{
get { return this.commandExecutedSubject.AsObservable(); }
}
public IObservable<Exception> CommandExeceptionsStream
{
get { return this.commandExeceptionsSubjectStream.AsObservable(); }
}
protected virtual void raiseCanExecuteChanged(EventArgs e)
{
var handler = this.CanExecuteChanged;
if (handler != null)
{
handler(this, e);
}
}
public void Dispose()
{
disposables.Dispose();
}
}
我在哪里使用以下助手
public static class ObservableExtensions
{
public static IObservable<TValue> ObserveProperty<T, TValue>(
this T source,
Expression<Func<T, TValue>> propertyExpression
)
where T : INotifyPropertyChanged
{
return source.ObserveProperty(propertyExpression, false);
}
public static IObservable<TValue> ObserveProperty<T, TValue>(
this T source,
Expression<Func<T, TValue>> propertyExpression,
bool observeInitialValue
)
where T : INotifyPropertyChanged
{
var memberExpression = (MemberExpression)propertyExpression.Body;
var getter = propertyExpression.Compile();
var observable = Observable
.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => new PropertyChangedEventHandler(h),
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Where(x => x.EventArgs.PropertyName == memberExpression.Member.Name)
.Select(_ => getter(source));
if (observeInitialValue)
return observable.Merge(Observable.Return(getter(source)));
return observable;
}
public static IObservable<string> ObservePropertyChanged<T>(this T source)
where T : INotifyPropertyChanged
{
var observable = Observable
.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => new PropertyChangedEventHandler(h),
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Select(x => x.EventArgs.PropertyName);
return observable;
}
}
这是一个如何连接它的例子
这是一个示例视图模型
public class ViewModel : INPCBase
{
private string title;
private bool hasStuff;
public ViewModel()
{
IObservable<bool> initPredicate = this.ObserveProperty(x => x.Title).Select(x => !string.IsNullOrEmpty(x)).StartWith(!string.IsNullOrEmpty(this.Title));
IObservable<bool> predicate = this.ObserveProperty(x => x.HasStuff).StartWith(this.HasStuff);
SomeCommand = new ReactiveCommand(initPredicate, false);
SomeCommand.AddPredicate(predicate);
SomeCommand.CommandExecutedStream.Subscribe(x =>
{
MessageBox.Show("Command Running");
});
}
public ReactiveCommand SomeCommand { get; set; }
public string Title
{
get
{
return this.title;
}
set
{
RaiseAndSetIfChanged(ref this.title, value, () => Title);
}
}
public bool HasStuff
{
get
{
return this.hasStuff;
}
set
{
RaiseAndSetIfChanged(ref this.hasStuff, value, () => HasStuff);
}
}
}
这是一个示例视图
<Window x:Class="RxCommand.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Horizontal" Height="60" VerticalAlignment="Top">
<CheckBox IsChecked="{Binding HasStuff, Mode=TwoWay}" Margin="10"></CheckBox>
<TextBox Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="150" Margin="10"></TextBox>
<Button Command="{Binding SomeCommand}" Width="150" Margin="10"></Button>
</StackPanel>
</Grid>
</Window>