WPF:处理大量路由事件

时间:2012-09-13 16:16:21

标签: wpf

我需要针对以下问题进行重新校准:  

假设您有一个MyView类型(UserControl),它定义了一个路由事件IsSelectedChanged。每次更改myView.IsSelected属性值时都会引发它。

 

此外,你有一个MyContainer(Canvas),它包含一个非常(非常!)大量的MyView类型的子节点。 MyContainer已路由事件MyViewsSelectionChanged,每当MyViewsSelection更改时都会引发该事件。 MyViewsSelection是一组MyView对象,其IsSelected属性设置为 true 。 MyContainer将为每个孩子处理MyView.IsSelectedChanged并将其MyViewSelection状态提供给MyContainerParent(Panel)

MyContainerParent将处理myContainer.MyViewsSelectionChanged事件

我担心的问题是我的应用程序对于大量MyView对象的选择性能不佳,导致某种事件的“野火”。

任何预防这一问题的重新定位都将非常感谢!

由于

一些代码:

BatchView.IsSelectedChanged(MyView):

public static readonly RoutedEvent IsSelectedChangedEvent = EventManager.RegisterRoutedEvent(
        "IsSelectedChanged", 
        RoutingStrategy.Direct, 
        typeof(RoutedEventHandler), 
        typeof(BatchView)
    );
/// <summary>
/// Occurs when IsSelected property value is changed.
/// </summary>
public event RoutedEventHandler IsSelectedChanged {
        add { AddHandler(IsSelectedChangedEvent, value); }
        remove { RemoveHandler(IsSelectedChangedEvent, value); }
    }
void RaiseIsSelectionChangedEvent() {
        RoutedEventArgs e = new RoutedEventArgs(IsSelectedChangedEvent, this.BatchViewModel);
        RaiseEvent(e);
        Logger.Debug("IsSelectionChanged: {0}; IsSelected = {1}", this.BatchViewModel.Description, this.IsSelected);
    }
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached(
        "IsSelected",
        typeof(bool),
        typeof(BatchView),
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(delegate(DependencyObject sender, DependencyPropertyChangedEventArgs args) {
        BatchView view = sender as BatchView;
        bool isSelected = Convert.ToBoolean(args.NewValue);
        if ( view != null ) {
            view._border.BorderBrush = isSelected ? Brushes.Magenta : Brushes.Black;
            view.IsPrimarySelected = view.IsFocused && isSelected;
        }
    })));
/// <summary>
/// Get/set whether this batch view is selected
/// </summary>
public bool IsSelected {
        get { return (bool)GetValue(IsSelectedProperty); }
        set {
            if ( IsSelected != value ) {
                SetValue(IsSelectedProperty, value);
                RaiseIsSelectionChangedEvent();
            }
        }
    }

GanttView(MyContainer):

static GanttView() {
        EventManager.RegisterClassHandler(typeof(BatchView), BatchView.IsSelectedChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {
            var batchView = sender as BatchView;
            var ganttView = batchView.FindVisualParent<GanttView>();
            if ( ganttView != null ) {
                ganttView.RaiseBatchViewsSelectionChangedEvent();
            }
            args.Handled = true;
        }));
    }

public static readonly RoutedEvent BatchViewsSelectionChangedEvent = EventManager.RegisterRoutedEvent(
        "BatchViewsSelectionChanged",
        RoutingStrategy.Direct,
        typeof(RoutedEventHandler),
        typeof(GanttView)
    );
public event RoutedEventHandler BatchViewsSelectionChanged {
        add { AddHandler(BatchViewsSelectionChangedEvent, value); }
        remove { RemoveHandler(BatchViewsSelectionChangedEvent, value); }
    }
void RaiseBatchViewsSelectionChangedEvent() {
        RoutedEventArgs e = new RoutedEventArgs(BatchViewsSelectionChangedEvent, this);         
        RaiseEvent(e);
        Logger.Debug("BatchViewsSelectionChanged: {0};", this.SelectedBatchViews.Count());
    }

SchedulerView(MyContainerParent):

static SchedulerView() {
        EventManager.RegisterClassHandler(typeof(GanttView), GanttView.BatchViewsSelectionChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {              
            var schedulerView = ((GanttView)sender).FindVisualParent<SchedulerView>();
            if ( schedulerView != null ) {
                if ( schedulerView.BatchesSelectionChanged != null ) {
                    BatchesSelectionChangedEventArgs e = new BatchesSelectionChangedEventArgs();
                    e.SelectedBatchesCount = schedulerView.GanttView.SelectedBatchViews.Count();
                    e.TotalBatchesDuration = schedulerView.GanttView.SelectedBatchViews.Sum<BatchView>(bv => bv.BatchViewModel.Model.Duration);
                    e.TotalBatchesQuantity = schedulerView.GanttView.SelectedBatchViews.Sum<BatchView>(bv => bv.BatchViewModel.Model.Quantity);
                    schedulerView.BatchesSelectionChanged(schedulerView, e);
                }
            }
        }));
    }

2 个答案:

答案 0 :(得分:0)

如果您担心必须处理的事件数量。你应该重新评估你的方法。有没有办法确定用户何时完成选择项目?

如果无法减少事件数量,那么您可能希望实施限制,即如果在一定时间内未收到任何事件,则只处理事件。

您可以自己实施 - 例如通过使用计时器 - 或者您可以使用无功扩展'(RX)节流功能。

  

节流“忽略来自可观察序列的值   在指定源的适当时间之前跟随另一个值   和dueTime“

您可以在http://msdn.microsoft.com/en-us/data/gg577609.aspx找到RX,在http://msdn.microsoft.com/en-us/library/hh229298%28v=vs.103%29找到Trottle的文档。显然你也可以通过NuGet安装RX。

答案 1 :(得分:0)

我想与我的问题分享解决方案。解决方案基于我的经理和Obalix提供的重新授权。所以,我将使用一个计时器,以延迟一点GanttView.BacthViewsSelectionChangedEvent。

static GanttView() {            
    EventManager.RegisterClassHandler(typeof(BatchView),   BatchView.IsSelectedChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {
            var batchView = sender as BatchView;
            var ganttView = batchView.FindVisualParent<GanttView>();
            if ( ganttView != null && !ganttView._batchViewIsSelectedChangedEventQueued ) {
                ganttView._batchViewIsSelectedChangedEventQueued = true;
                System.Timers.Timer eventTrigger = new System.Timers.Timer(100) { AutoReset = false };
                eventTrigger.Start();
                eventTrigger.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object timer, System.Timers.ElapsedEventArgs e) {
                    ganttView._batchViewIsSelectedChangedEventQueued = false;
                    ganttView.Dispatcher.Invoke(new Action(delegate() { ganttView.RaiseBatchViewsSelectionChangedEvent(); }), DispatcherPriority.Normal, null);
                });             
            }
            args.Handled = true;
        }));
}

使用Dispatcher属性调用ganttView.RaiseBatchViewsSelectionChangedEvent()很重要,否则你会得到一个异常(“调用线程无法访问此对象,因为不同的线程拥有它。”),请参阅这篇文章http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher

Dan和Obalix,非常感谢你的时间和考虑!