Wpf 如何组合多个IObservable<;布尔>;形成复合bool订阅值

Wpf 如何组合多个IObservable<;布尔>;形成复合bool订阅值,wpf,system.reactive,Wpf,System.reactive,我想为WPF创建一种基于Rx的ICommand。我想做的是通过组合任意数量的IObservable流来控制CanExecute 我希望的工作方式是,我希望使用所有谓词的最新组合逻辑and值,并使用该值来控制bool ICommand.CanExecute(对象参数)方法实现。我不想等待所有谓词都产生,它应该使用任何一个源谓词流OnNexts(产生一个值) 我一直在努力解决如何将它连接起来,以便任何谓词都能使ICommand.CanExecute产生一个新值 有一分钟忘记了实际的ICommand实

我想为WPF创建一种基于Rx的ICommand。我想做的是通过组合任意数量的IObservable流来控制CanExecute

我希望的工作方式是,我希望使用所有谓词的最新组合逻辑and值,并使用该值来控制bool ICommand.CanExecute(对象参数)方法实现。我不想等待所有谓词都产生,它应该使用任何一个源谓词流OnNexts(产生一个值)

我一直在努力解决如何将它连接起来,以便任何谓词都能使ICommand.CanExecute产生一个新值

有一分钟忘记了实际的ICommand实现(因为我的问题更多的是关于Rx方面的事情),有人能建议我如何连接一组谓词(IObservable),它们在创建流的底层事物发生变化时生成,但也将协同工作,创造一个我也可以订阅的全面的最终bool价值。结束值将是当前谓词流值的逻辑AND

我希望我不需要订阅所有的谓词流,我希望RX中有一个我可能忽略的很酷的运营商

我知道我可以合并流,但这并不是我所追求的行为,因为这只是已合并的输入流的最新值,我还知道我可以合并最新值,同样,这也不太正确,因为它只会在所有合并流产生值时产生

我想要的是合并流,这样任何更改都会通知订阅者,但我还想知道合并谓词IObservable streams的逻辑AND现在是什么,这样我就可以从这个整体合并值驱动ICommand.CanExecute

我希望这是有道理的

这里是一些基本代码(我留下了一些注释代码,这些代码显示了我的Rx命令想法背后的思想,因为它可能有助于说明我想要实现的功能)

公共类视图模型: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>