سؤال

أواجه مشاكل في معرفة كيفية القيام بذلك. لدي حالتان (المصدر والهدف) تنفذ INotifyPropertyChanged وأنا أتتبع PropertyChanged حدث لكليهما. ما أريد القيام به هو تشغيل إجراء في أي وقت source.PropertyChanged يتم رفعه حتى target.PropertyChanged تربى. يمكنني أن أفعل ذلك على ما يرام مثل هذا:

INotifyPropertyChanged source;
INotifyPropertyChanged target;

var sourcePropertyChanged = Observable
    .FromEvent<PropertyChangedEventArgs>(source, "PropertyChanged")
    .Where(x => x.EventArgs.PropertyName == sourcePropertyName);

var targetPropertyChanged = Observable
    .FromEvent<PropertyChangedEventArgs>(target, "PropertyChanged")
    .Where(x => x.EventArgs.PropertyName == targetPropertyName);

sourcePropertyChanged
    .TakeUntil(targetPropertyChanged)
    .ObserveOnDispatcher()
    .Subscribe(_ => /*Raises target.PropertyChanged for targetPropertyName*/);

المشكلة التي أواجهها هي أنني أريد تجاهل PropertyChanged الإخطارات الناجمة عن الإجراءات وتوقف فقط عن أخذ القيم عندما تكون PropertyChanged يتم رفع الحدث بواسطة مصدر خارجي. هل هناك طريقة جيدة للحصول على ذلك؟

هل كانت مفيدة؟

المحلول

لا يوجد مبني في طريقة لفعل ما تتحدث عنه. هذا بسيط SkipWhen التنفيذ الذي يتخطى قيمة المصدر التالية في كل مرة يتم فيها استلام قيمة على تسلسل "الآخر":

public static IObservable<TSource> SkipWhen(this IObservable<TSource> source, 
    IObservable<TOther> other)
{
    return Observable.Defer<TSource>(() =>
    {
        object lockObject = new object();
        Stack<TOther> skipStack = new Stack<TOther>();

        other.Subscribe(x => { lock(lockObject) { skipStack.Push(x); });

        return source.Where(_ =>
        {
            lock(lockObject);
            {
                if (skipStack.Count > 0)
                {
                    skipStack.Pop();
                    return false;
                }
                else
                {
                    return true;
                }
            }
        });
    });
}

سيتم بعد ذلك تحديث رمزك مثل SO (انظر ملاحظتي أدناه):

INotifyPropertyChanged source;
INotifyPropertyChanged target;

// See the link at the bottom of my answer
var sourcePropertyChanged = source.GetPropertyChangeValues(x => x.SourceProperty);

// Unit is Rx's "void"
var targetChangedLocally = new Subject<Unit>();

var targetPropertyChanged = target.GetPropertyChangeValues(x => x.TargetProperty)
    .SkipWhen(targetChangedLocally);

sourcePropertyChanged
    .TakeUntil(targetPropertyChanged)
    .ObserveOnDispatcher()
    .Subscribe(_ =>
    {
        targetChangedLocally.OnNext();
        /*Raises target.PropertyChanged for targetPropertyName*/
    });

NB: لقد قمت مؤخرًا بالتدوين عنها غلاف قابل للمكتوب بقوة حول أحداث inotifypropertychanged; ؛ لا تتردد في سرقة هذا الرمز.

نصائح أخرى

لا توجد طريقة مدمجة ولكن ربما يمكنك تصفية الأحداث باستخدام Where طريقة التمديد للملاحظة. الشرط للتصفية على هو مرسل الحدث. أفترض أن مرسل أ target.PropertyChanged الحدث مختلف عن مرسل أ PropertyChanged الحدث الذي أثاره مصدر آخر.

لست متأكدًا تمامًا مما إذا كان هذا نهجًا يمكنك استخدامه.

استخدام الأقفال في RX بهذه الطريقة أمر جيد. القفل قصير الأجل ولا يتصل برمز المستخدم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top