如何避免不必要的数千事件ListView.SelectedIndexChanged的?数千、不必要、事件、SelectedIndexChanged

2023-09-02 01:48:51 作者:泪丶为你落

如果用户选择在.NET 2.0 ListView中的所有项目,在ListView将触发一个的SelectedIndexChanged 事件每一个项目,而不是触发一个事件来表明的选择已经改变了。

If a user select all items in a .NET 2.0 ListView, the ListView will fire a SelectedIndexChanged event for every item, rather than firing an event to indicate that the selection has changed.

如果用户再点击来选择列表中只有一个项目,在ListView将触发一个的SelectedIndexChanged 事件的每次的项目是越来越未选中状态,然后一个的SelectedIndexChanged 事件为单新选择的项目,而不是点火的事件,以指示选择已更改

If the user then clicks to select just one item in the list, the ListView will fire a SelectedIndexChanged event for every item that is getting unselected, and then an SelectedIndexChanged event for the single newly selected item, rather than firing an event to indicate that the selection has changed.

如果你有code在的SelectedIndexChanged 事件处理程序,该程序将在您开始有几百/千列表中的项目成为pretty的响应。

If you have code in the SelectedIndexChanged event handler, the program will become pretty unresponsive when you begin to have a few hundred/thousand items in the list.

我想过的停留计时器的,等等。

但没有任何人有一个很好的解决方案,以避免不必要的数以千计的ListView的。 SelectedIndexChange 事件,当真正的一个事件会做什么?

But does anyone have a good solution to avoid thousands of needless ListView.SelectedIndexChange events, when really one event will do?

推荐答案

从伊恩好的解决方案。我花了这一点,把它做成一个可重用的类,并确保处理的计时器正常。我还减少了间隔以获得更敏感的应用程序。这种控制也doublebuffers减少闪烁。

Good solution from Ian. I took that and made it into a reusable class, making sure to dispose of the timer properly. I also reduced the interval to get a more responsive app. This control also doublebuffers to reduce flicker.

  public class DoublebufferedListView : System.Windows.Forms.ListView
  {
     private Timer m_changeDelayTimer = null;
     public DoublebufferedListView()
        : base()
     {
        // Set common properties for our listviews
        if (!SystemInformation.TerminalServerSession)
        {
           DoubleBuffered = true;
           SetStyle(ControlStyles.ResizeRedraw, true);
        }
     }

     /// <summary>
     /// Make sure to properly dispose of the timer
     /// </summary>
     /// <param name="disposing"></param>
     protected override void Dispose(bool disposing)
     {
        if (disposing && m_changeDelayTimer != null)
        {
           m_changeDelayTimer.Tick -= ChangeDelayTimerTick;
           m_changeDelayTimer.Dispose();
        }
        base.Dispose(disposing);
     }

     /// <summary>
     /// Hack to avoid lots of unnecessary change events by marshaling with a timer:
     /// http://stackoverflow.com/questions/86793/how-to-avoid-thousands-of-needless-listview-selectedindexchanged-events
     /// </summary>
     /// <param name="e"></param>
     protected override void OnSelectedIndexChanged(EventArgs e)
     {
        if (m_changeDelayTimer == null)
        {
           m_changeDelayTimer = new Timer();
           m_changeDelayTimer.Tick += ChangeDelayTimerTick;
           m_changeDelayTimer.Interval = 40;
        }
        // When a new SelectedIndexChanged event arrives, disable, then enable the
        // timer, effectively resetting it, so that after the last one in a batch
        // arrives, there is at least 40 ms before we react, plenty of time 
        // to wait any other selection events in the same batch.
        m_changeDelayTimer.Enabled = false;
        m_changeDelayTimer.Enabled = true;
     }

     private void ChangeDelayTimerTick(object sender, EventArgs e)
     {
        m_changeDelayTimer.Enabled = false;
        base.OnSelectedIndexChanged(new EventArgs());
     }
  }

不要让我知道这是否可以得到改善。

Do let me know if this can be improved.