0

对于以下问题,我需要您的建议:

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

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

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();
            }
        }
    }

甘特视图(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());
    }

调度器视图(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);
                }
            }
        }));
    }
4

2 回答 2

0

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

如果没有办法减少事件的数量,那么您可能想要实施节流阀,即仅在一段时间内没有收到任何事件时才处理事件。

您可以自己实现这一点 - 例如通过使用计时器 - 或者您可以使用响应式扩展的 (RX) Throttle 功能。

节流“忽略来自可观察序列的值,这些值在指定源和到期时间的到期时间之前跟随另一个值”

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

于 2012-09-14T11:47:05.243 回答
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,非常感谢您的时间和考虑!

于 2012-09-14T16:38:58.510 回答