我想为WPF创建一种基于Rx的ICommand。我想做的是能够通过组合任意数量的IObservable< bool>来控制CanExecute。流。
我想要工作的方式是我想使用所有谓词的最新组合逻辑AND值,并使用它来控制bool ICommand.CanExecute(对象参数)方法实现。我不想等待所有谓词产生,它应该使用任何一个源谓词流OnNexts(产生一个值)。
我试图找出如何连接它以使任何谓词都应该导致ICommand.CanExecute产生新值时,我有点困惑。
忘记实际的ICommand实现一分钟(因为我的问题更多是关于Rx方面的事情),任何人都可以建议我如何连接一堆谓词(IObservable< bool>),每当底层的东西产生创建他们的流更改,但也将协同工作以创建我可以订阅的整体结束bool值。结束值将是当前谓词流值的逻辑AND。
我希望我不需要订阅所有的谓词流,并希望在RX中有一个很酷的操作符,我可能会忽略它。
我知道我可以合并流,但这不是我所追求的行为,因为这只是已经合并的输入流的最新值,我也知道我可以CombineLatest,再次这不是很正确,因为它只会在所有组合流产生值时产生。
我想要的是要组合的流,因此任何更改都会通知订阅者,但我也想知道组合谓词IObservable< bool>的逻辑AND。流是现在,这样我就可以从这个整体组合值中驱动ICommand.CanExecute。
我希望这是有道理的。
这是一些骨架代码(我留下了一些注释掉的代码,显示了我的Rx Command背后的想法,因为它可能有助于说明我想要工作的内容)
public class ViewModel : INPCBase
{
private string title;
private bool hasStuff;
public ViewModel()
{
//Initialise some command with 1st predicate, and
// initial CanExecute value
//SomeCommand = new ReactiveCommand(
// this.ObserveProperty(x => x.Title)
// .Select(x => !string.IsNullOrEmpty(x)), false);
//SomeCommand.AddPredicate(this.ObserveProperty(x => x.HasStuff));
//SomeCommand.CommandExecutedStream.Subscribe(x =>
// {
// MessageBox.Show("Command Running");
// });
IObservable<bool> obsPred = this.ObserveProperty(x => x.Title)
.Select(x => !string.IsNullOrEmpty(x))
.StartWith(!string.IsNullOrEmpty(this.Title));
IObservable<bool> obsPred2 = this.ObserveProperty(x =>
x.HasStuff).StartWith(this.HasStuff);
obsPred.Merge(obsPred2).Subscribe(x =>
{
//How do I get this to fire whenever obsPred OR
//obsPred2 fire OnNext, but also get a combined value (bool)
//of the AND of obsPred & obsPred2 (bearing in mind I may
//want more than 2 predicates, it should cope with any number of
//IObservable<bool> predicates
});
}
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);
}
}
}
答案 0 :(得分:1)
您正在寻找CombineLatest
运营商
ISubject<bool> obsPred = new BehaviorSubject<bool>(false);
ISubject<bool> obsPred2 = new BehaviorSubject<bool>(false);
Observable.CombineLatest(obsPred, obsPred2, (a, b)=>a&&b)
.DistinctUntilChanged()
.Dump();
obsPred.OnNext(true);
obsPred2.OnNext(true);
obsPred2.OnNext(true);
obsPred.OnNext(true);
obsPred.OnNext(false);
这将输出
False
True
False
使用DistinctUntilChanged()
将停止返回重复的连续值。
显然,为你的财产可观察性换掉了BehaviorSubject
。
答案 1 :(得分:1)
好的,这就是我成功完成这项工作的方式
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;
}
}
这是一个如何连线的例子
以下是ViewModel
的示例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>