缓冲区PropertyChanged事件

时间:2014-12-03 11:51:42

标签: c# wpf system.reactive

我有一个wpf应用程序,所有viewModel都继承自实现INotifyPropertyChanged的NotifyPropertyChangeClass类(见下文)。

我希望限制发送到视图的通知,以避免由于发送过多通知而导致滞后。 到目前为止,我所做的是使用反应扩展的Sample Sample延迟每个属性。

问题是Gui在节流期间后期更新了。如果在这个时期的开始时提出第一个事件会更加敏感:

More graphic explanation

NotifyPropertyChangeClass的代码:

using System;
using System.ComponentModel;
using System.Reactive.Linq;

namespace MyNameSpace
{
    public class NotifyPropertyChangeClass : INotifyPropertyChanged
    {
        public NotifyPropertyChangeClass(int throttlingPeriod)
        {
            var obs = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
               h => this.privatePropertyChanged += h, h => this.privatePropertyChanged -= h);
            var groupedByName = obs.Select(o => o.EventArgs.PropertyName).GroupBy(x => x).SelectMany(o => o.Sample(TimeSpan.FromMilliseconds(throttlingPeriod)));
            groupedByName.Subscribe(o =>
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(o));
            });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private event PropertyChangedEventHandler privatePropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = privatePropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我如何实现我的目标?

2 个答案:

答案 0 :(得分:1)

我还没试过,但你可以尝试一下:

using System;
using System.ComponentModel;
using System.Reactive.Linq;

namespace MyNameSpace
{
    public class NotifyPropertyChangeClass : INotifyPropertyChanged
    {
        private bool _isThrottling = false;

        public NotifyPropertyChangeClass(int throttlingPeriod)
        {
            var obs = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
               h => this.privatePropertyChanged += h, h => this.privatePropertyChanged -= h);
            var groupedByName = obs.Select(o => o.EventArgs.PropertyName).GroupBy(x => x).SelectMany(o => o.Sample(TimeSpan.FromMilliseconds(throttlingPeriod)));
            groupedByName.Subscribe(o =>
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(o));
                _isThrottling = false;
            });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private event PropertyChangedEventHandler privatePropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            // Will fire the first time, the event is raised
            if (!_isThrottling)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(o));
            }

            // Setting to true here will suppress raising the public event 
            // for every subsequent call, until the event is raised
            // by the observable pattern and the flag is set to false again.
            _isThrottling = true;

            // Will always be raised
            PropertyChangedEventHandler handler = privatePropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

答案 1 :(得分:0)

我还想在同时通知许多更改(它使列表视图陷入泥潭)时限制PropertyChanged通知程序

我最终使用了时间检查,因此,如果自上一个事件起经过500毫秒之前引发了一个新事件,它将在触发所有属性更新之前在500毫秒内阻止此事件以及任何其他事件。 下面的代码

private DateTime _lastPropertyChange = DateTime.Now - TimeSpan.FromMinutes(1);
private readonly TimeSpan _propertyChangeBuffer = TimeSpan.FromMilliseconds(500);
private bool _propertyChangeBlock = false;

public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (_propertyChangeBlock)
            return;

        if (_lastPropertyChange < DateTime.Now - _propertyChangeBuffer)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            _lastPropertyChange = DateTime.Now;
        }
        else
        {
            _propertyChangeBlock = true;
            ThreadPool.QueueUserWorkItem(p =>
            {
                Thread.Sleep(_propertyChangeBuffer);
                _propertyChangeBlock = false;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
            });
        }
    }