Wpf 如何组合多个IObservable<;布尔>;形成复合bool订阅值
我想为WPF创建一种基于Rx的ICommand。我想做的是通过组合任意数量的IObservable流来控制CanExecute 我希望的工作方式是,我希望使用所有谓词的最新组合逻辑and值,并使用该值来控制bool ICommand.CanExecute(对象参数)方法实现。我不想等待所有谓词都产生,它应该使用任何一个源谓词流OnNexts(产生一个值) 我一直在努力解决如何将它连接起来,以便任何谓词都能使ICommand.CanExecute产生一个新值 有一分钟忘记了实际的ICommand实现(因为我的问题更多的是关于Rx方面的事情),有人能建议我如何连接一组谓词(IObservable),它们在创建流的底层事物发生变化时生成,但也将协同工作,创造一个我也可以订阅的全面的最终bool价值。结束值将是当前谓词流值的逻辑AND 我希望我不需要订阅所有的谓词流,我希望RX中有一个我可能忽略的很酷的运营商 我知道我可以合并流,但这并不是我所追求的行为,因为这只是已合并的输入流的最新值,我还知道我可以合并最新值,同样,这也不太正确,因为它只会在所有合并流产生值时产生 我想要的是合并流,这样任何更改都会通知订阅者,但我还想知道合并谓词IObservable streams的逻辑AND现在是什么,这样我就可以从这个整体合并值驱动ICommand.CanExecute 我希望这是有道理的 这里是一些基本代码(我留下了一些注释代码,这些代码显示了我的Rx命令想法背后的思想,因为它可能有助于说明我想要实现的功能)Wpf 如何组合多个IObservable<;布尔>;形成复合bool订阅值,wpf,system.reactive,Wpf,System.reactive,我想为WPF创建一种基于Rx的ICommand。我想做的是通过组合任意数量的IObservable流来控制CanExecute 我希望的工作方式是,我希望使用所有谓词的最新组合逻辑and值,并使用该值来控制bool ICommand.CanExecute(对象参数)方法实现。我不想等待所有谓词都产生,它应该使用任何一个源谓词流OnNexts(产生一个值) 我一直在努力解决如何将它连接起来,以便任何谓词都能使ICommand.CanExecute产生一个新值 有一分钟忘记了实际的ICommand实
公共类视图模型:INPCBase
{
私有字符串标题;
私人布尔·哈斯塔夫;
公共视图模型()
{
//用第一个谓词初始化某个命令,然后
//初始执行值
//SomeCommand=新的反应命令(
//this.ObserveProperty(x=>x.Title)
//.Select(x=>!string.IsNullOrEmpty(x)),false);
//AddPredicate(this.ObserveProperty(x=>x.hastuff));
//SomeCommand.CommandExecutedStream.Subscribe(x=>
// {
//MessageBox.Show(“命令运行”);
// });
IObservable obsPred=this.ObserveProperty(x=>x.Title)
.Select(x=>!string.IsNullOrEmpty(x))
.StartWith(!string.IsNullOrEmpty(this.Title));
IObservable observePred2=此。ObserveProperty(x=>
x、 hastuff).StartWith(this.hastuff);
obpred.Merge(obpred2.Subscribe)(x=>
{
//我怎样才能在OBPRED或
//obsPred2触发OnNext,但也会获得一个组合值(bool)
//以及OBPRED和OBPRED2(请记住,我可能
//如果需要2个以上的谓词,它应该能够处理任意数量的
//IObservable谓词
});
}
公共字符串标题
{
得到
{
返回此.title;
}
设置
{
RaiseAndSetIfChanged(参考this.title,value,()=>title);
}
}
公共布尔哈斯塔夫酒店
{
得到
{
退回这个.hastuff;
}
设置
{
RaiseAndSetIfChanged(参考this.hastuff,value,()=>hastuff);
}
}
}
您正在寻找CombineTest
操作符
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);
使用DistinctUntilChanged()
将停止返回重复的连续值
显然,将
BehaviorSubject
s替换为属性可观察对象。好的,我就是这样完成这项工作的
public interface IReactiveCommand : ICommand
{
IObservable<object> CommandExecutedStream { get; }
IObservable<Exception> CommandExeceptionsStream { get; }
void AddPredicate(IObservable<bool> predicate);
}
公共接口IReactiveCommand:ICommand
{
IObservable CommandExecutedStream{get;}
IObservable CommandExeceptionStream{get;}
void AddPredicate(IObservable谓词);
}
然后是实际的命令执行
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();
}
}
公共类反应命令:IReactiveCommand,IDisposable
{
私有主题commandExecutedSubject=新主题();
私有主题命令ExeceptionSubjectStream=新主题();
私有列表谓词=新列表();
私人IObservable canExecuteObs;
private bool canExecuteLatest=真;
私有CompositeDisposable一次性用品=新CompositeDisposable();
公共反应命令(IObservable initPredicate,bool initialCondition)
{
if(initPredicate!=null)
{
canExecuteObs=initPredicate;
设置订阅();
}
RaiseCanExecute(初始条件);
}
私有无效RaiseCanExecute(布尔值)
{
canExecuteLatest=值;
this.raiseCanExecuteChanged(EventArgs.Empty);
}
公共反应命令()
{
RaiseCanExecute(真);
}
私有void SetupSubscriptions()
{
可处置的=新的可组合的();
一次性。添加(this.canExecuteObs.Subscribe(
//OnNext
x=>
{
RaiseCanExecute(x);
},
//一个错误
CommandExeceptionSubjectStream.OnNext
));
}
公共void AddPredicate(IObservable谓词)
{
可处置的;可处置的;
Add(谓词);
this.canExecuteObs=this.canExecuteObs.combinelatetest(谓词.Last(),(a,b)=>a&&b.DistinctUntilChanged();
设置订阅();
}
bool ICommand.CanExecute(对象p
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>