Как избежать тысяч бесполезных ListView. События SelectedIndexChanged?

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

16
задан James Dunn 22 October 2013 в 15:20
поделиться

9 ответов

Хорошее решение от Яна. Я взял его и превратил в многоразовый класс, убедившись, что таймер правильно утилизирован. Я также сократил интервал, чтобы получить более отзывчивое приложение. Этот элемент управления также имеет двойной буфер для уменьшения мерцания.

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

Сообщите мне, можно ли это улучшить.

12
ответ дан 30 November 2019 в 22:18
поделиться

Я просто пытался заняться этой самой проблемой вчера. Я не знаю точно, под чем Вы подразумеваете, "живут" таймеры, но я пытался реализовать свою очень собственную версию ожидания, пока все изменения не сделаны. К сожалению, единственный способ, которым я мог думать, чтобы сделать, это было в отдельном потоке, и оказывается, что при создании отдельного потока элементы UI недоступны в том потоке..NET выдает исключение, указывая, что к элементам UI можно только получить доступ в потоке, где элементы были созданы! Так, я нашел способ оптимизировать мой ответ на SelectedIndexChanged и сделать его достаточно быстро туда, где это терпимо - не масштабируемое решение все же. Позволяет надежде, у кого-то есть умная мысль заняться этой проблемой в единственном потоке.

0
ответ дан 30 November 2019 в 22:18
поделиться

Я или попытался бы связать обратную передачу с кнопкой, чтобы позволить пользователю отправлять их изменения и отсоединять обработчик событий.

0
ответ дан 30 November 2019 в 22:18
поделиться

Это - жить решение для таймера, которое я использую на данный момент (живите, просто означает, "ожидают немного"). Этот код мог бы пострадать от состояния состязания и возможно исключения нулевой ссылки.

Timer changeDelayTimer = null;

private void lvResults_SelectedIndexChanged(object sender, EventArgs e)
{
        if (this.changeDelayTimer == null)
        {
            this.changeDelayTimer = new Timer();
            this.changeDelayTimer.Tick += ChangeDelayTimerTick;
            this.changeDelayTimer.Interval = 200; //200ms is what Explorer uses
        }
        this.changeDelayTimer.Enabled = false;
        this.changeDelayTimer.Enabled = true;
}

private void ChangeDelayTimerTick(object sender, EventArgs e)
{
    this.changeDelayTimer.Enabled = false;
    this.changeDelayTimer.Dispose();
    this.changeDelayTimer = null;

    //Add original SelectedIndexChanged event handler code here
    //todo
}
3
ответ дан 30 November 2019 в 22:18
поделиться

Возможно, это поможет вам выполнить то, что вам нужно, без использования таймеров:

http://www.dotjem.com/archive/2009/06/19/20.aspx

Мне не нравится пользователь таймеров и т. Д. Как я также заявляю в сообщении ...

Надеюсь, это поможет ...

Ох, я забыл сказать, что это .NET 3.5, и я использую некоторые функции в linq для выполнения «Оценки изменений выбора» если вы можете назвать это так oO ..

В любом случае, если вы используете более старую версию, эта оценка должна выполняться с небольшим количеством кода ...>. <...

0
ответ дан 30 November 2019 в 22:18
поделиться

Таймер - лучшее общее решение.

Проблема с предложением Йенса состоит в том, что если в списке много выбранных элементов (тысячи или более), начинается получение списка выбранных элементов. это займет много времени.

Вместо того, чтобы создавать объект таймера каждый раз, когда происходит событие SelectedIndexChanged, проще просто поместить постоянное значение в форму с помощью дизайнера и заставить его проверять логическую переменную в классе, чтобы узнать, не или нет, он должен вызывать функцию обновления.

Например:

bool timer_event_should_call_update_controls = false;

private void lvwMyListView_SelectedIndexChanged(object sender, EventArgs e) {

  timer_event_should_call_update_controls = true;
}

private void UpdateControlsTimer_Tick(object sender, EventArgs e) {

  if (timer_event_should_call_update_controls) {
    timer_event_should_call_update_controls = false;

    update_controls();
  }
}

Это отлично работает, если вы используете информацию просто для целей отображения, например, при обновлении строки состояния, чтобы сказать «Выбрано X из Y».

1
ответ дан 30 November 2019 в 22:18
поделиться

Я рекомендую виртуализировать представление списка, если в нем несколько сотен или тысяч элементов.

0
ответ дан 30 November 2019 в 22:18
поделиться

Maylon >>>

Целью никогда не было работать со списком, превышающим несколько сотен пунктов, но ... Я протестировал общий пользовательский интерфейс с 10.000 элементами и выборками по 1000-5000 элементов за один раз (и изменения 1000-3000 элементов как в выбранных, так и в отмененных) ...

Общая продолжительность вычисления никогда не превышала 0,1 секунды , некоторые из самых высоких измерений составляли 0,04 секунды, я обнаружил, что это вполне приемлемо для такого количества элементов.

А для 10 000 элементов простая инициализация списка занимает более 10 секунд, так что в этот момент я мог подумать, что пришли другие вещи. Виртуализация, как указывает Джо Чанг.

Тем не менее, должно быть ясно, что код не является оптимальным решением в том, как он вычисляет разницу в выборе, при необходимости это можно значительно улучшить и в различными способами, я сосредоточился на понимании концепции с помощью кода, а не производительности.

Однако,Если у вас ухудшилась производительность, меня очень интересуют некоторые из следующего:

  • Сколько элементов в списке?
  • Сколько выбранных / невыбранных элементов за раз?
  • Сколько времени примерно занимает событие поднять?
  • Аппаратная платформа?
  • Подробнее о случае использования?
  • Другая важная информация, которую вы можете придумать?

Иначе нелегко помочь улучшить решение.

0
ответ дан 30 November 2019 в 22:18
поделиться

Оставьте ListView и все старые элементы управления.

Сделайте DataGridView своим другом, и все будет хорошо :)

0
ответ дан 30 November 2019 в 22:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: