5

2つのDecimalUpDownコントロール、num_oneとnum_twoがあり、それぞれプロパティFirstとSecondにバインドされています。Firstが変更されると、サーバーに接続してSecondの値を計算し、その逆も同様です。サーバー呼び出しを非同期で起動するとUIが解放されますが、すばやく起動すると(たとえば、スクロールホイール)、最後の要求が常に最後に返されるとは限らないため、値が同期しなくなる可能性があります。

Reactiveを使用して、ユーザーがしばらくの間変更を停止した後にのみサーバー呼び出しを起動するように呼び出しを抑制しようとしています。問題は、更新中に変更を加えると、プロパティの変更が相互にトリガーを開始し、スロットルのTimeSpanに応じて前後にスタックすることです。

    public MainWindow()
    {
        InitializeComponent();

        DataContext = this;

        Observable.FromEventPattern<RoutedPropertyChangedEventHandler<object>, RoutedPropertyChangedEventArgs<object>>(h => num_one.ValueChanged += h, h => num_one.ValueChanged -= h)
            .Throttle(TimeSpan.FromMilliseconds(100), Scheduler.ThreadPool)
           .Subscribe(x =>
           {
               Thread.Sleep(300); // simulate work
               Second = (decimal)x.EventArgs.NewValue / 3.0m;
           });

        Observable.FromEventPattern<RoutedPropertyChangedEventHandler<object>, RoutedPropertyChangedEventArgs<object>>(h => num_two.ValueChanged += h, h => num_two.ValueChanged -= h)
            .Throttle(TimeSpan.FromMilliseconds(100), Scheduler.ThreadPool)
           .Subscribe(x =>
           {
               Thread.Sleep(300); // simulate work
               First = (decimal)x.EventArgs.NewValue * 3.0m;
           });
    }

    private decimal first;
    public decimal First
    {
        get { return first; }
        set
        {
            first = value;
            NotifyPropertyChanged("First");
        }
    }

    private decimal second;
    public decimal Second
    {
        get { return second; }
        set
        {
            second = value;
            NotifyPropertyChanged("Second");
        }
    }
4

2 に答える 2

7

Throttleタイムアウトを使用せずに、必要なことを正確に行うのに役立つ組み込みの Rx オペレーターがあります。これがSwitchオペレーターです。

Switchオペレーターは機能しないため、ほとんどのIObservable<T>場合、IntelliSense で表示されることはありません。

代わりにIObservable<IObservable<T>>、オブザーバブルのストリームで動作し、生成された最新のオブザーバブルに継続的に切り替えることでソースをフラット化IObservable<T>します (以前のオブザーバブルの値は無視します)。内側のオブザーバブルではなく、外側のオブザーバブルが完了したときにのみ完了します。

これはまさにあなたが望むものです - 新しい値の変更が発生した場合、以前の結果を無視して最新の結果のみを返します。

方法は次のとおりです。

最初に、厄介なイベント処理コードをいくつかのオブザーバブルに削除しました。

var ones =
    Observable
        .FromEventPattern<
            RoutedPropertyChangedEventHandler<object>,
            RoutedPropertyChangedEventArgs<object>>(
            h => num_one.ValueChanged += h,
            h => num_one.ValueChanged -= h)
        .Select(ep => (decimal)ep.EventArgs.NewValue);

var twos =
    Observable
        .FromEventPattern<
            RoutedPropertyChangedEventHandler<object>,
            RoutedPropertyChangedEventArgs<object>>(
            h => num_two.ValueChanged += h,
            h => num_two.ValueChanged -= h)
        .Select(ep => (decimal)ep.EventArgs.NewValue);

あなたのコードは少し混乱しているようです。DecimalUpDownコントロールの値は、結果を返すサーバー関数への入力であると想定しています。サーバーを呼び出す関数は次のとおりです。

Func<decimal, IObservable<decimal>> one2two = x =>
    Observable.Start(() =>
    {
        Thread.Sleep(300); // simulate work
        return x / 3.0m;
    });

Func<decimal, IObservable<decimal>> two2one = x =>
    Observable.Start(() =>
    {
        Thread.Sleep(300); // simulate work
        return x * 3.0m;
    });

明らかに、実際のサーバー コード呼び出しをこれら 2 つの関数に入れます。

これで、最終的なオブザーバブルとサブスクリプションを結び付けるのはほとんど簡単です。

ones
    .DistinctUntilChanged()
    .Select(x => one2two(x))
    .Switch()
    .Subscribe(x =>
    {
        Second = x;
    });

twos
    .DistinctUntilChanged()
    .Select(x => two2one(x))
    .Switch()
    .Subscribe(x =>
    {
        First = x;
    });

DistinctUntilChanged値が実際に変更された場合にのみ呼び出しを行うようにします。

Switch次に、2 つのサーバー関数を呼び出して実行し、プロパティに割り当てられた最新の結果のみを取得するのは簡単です。

サブスクリプションを UI スレッドに渡すために、あちこちにスケジューラを挿入する必要があるかもしれませんがObserveOn、それ以外の場合、このソリューションはうまく機能するはずです。

于 2012-06-29T00:06:48.250 に答える
1

プロパティが変更されていない場合は、プロパティ変更通知を発行しないでください。プロパティにifステートメントを追加する必要があります。

public decimal First
{
    get { return first; }
    set
    {
        if(first == value)
            return;

        first = value;
        NotifyPropertyChanged("First");
    }
}
于 2012-06-28T18:48:20.780 に答える