Почему Listbox.Drawitem вызывается много раз? - PullRequest
0 голосов
/ 29 марта 2012

В моем C # WinForm есть список с привязкой к данным, который содержит строки, которые являются ссылками на расположение файлов изображений. Я хочу отобразить изображение в виде эскиза для пользователя, чтобы щелкнуть и просмотреть. Я правильно заработал, установив DrawMode = OwnerDrawVariable и обработав события DrawItem и MeasureItem.

Однако я заметил, что мне нужно нажать выход 2 раза, чтобы выйти из приложения (похоже, что он называется selectedIndexChanged при первом нажатии, а затем выходит при втором). При дальнейшей проверке я заметил, что событие DrawItem вызывается множество раз, когда я щелкаю элемент в списке (например, 15+ раз). За один раз в списке есть только 1-2 элемента! Почему это называют так много раз?

Я протестировал это с простым списком без привязки к данным, и он делает то же самое. Мне только любопытно, так как я должен прочитать изображение с диска и получить его миниатюру, чтобы поместить в список, что, если это произойдет, то 15-20 раз может повлиять на производительность (и совершенно НЕОБХОДИМО).

private void listBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
   MessageBox.Show("listBox1_MeasureItem");
   // Cast the sender object back to ListBox type.
   ListBox theListBox = (ListBox)sender;

   // Get the file path contained in each item.
   DataRowView drv = (DataRowView)theListBox.Items[e.Index];
   string fileString = drv.Row["fullpath"].ToString();

   // Create an image object and load image file into it
   Image img = Image.FromFile(fileString);
   e.ItemHeight = Convert.ToInt32(img.Height * 0.15);
   e.ItemWidth = Convert.ToInt32(img.Width * 0.15);
}

private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
   MessageBox.Show("listBox1_DrawItem");
   // If the item is the selected item, then draw the rectangle
   // filled in blue. The item is selected when a bitwise And  
   // of the State property and the DrawItemState.Selected 
   // property is true.
   if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
   {
      e.Graphics.FillRectangle(Brushes.CornflowerBlue, e.Bounds);
   }
   else
   {
      // Otherwise, draw the rectangle filled in beige.
      e.Graphics.FillRectangle(Brushes.Beige, e.Bounds);
   }

   DataRowView drv = (DataRowView)listBox1.Items[e.Index];
   Image img = Image.FromFile(drv.Row["fullpath"].ToString());
   img = img.GetThumbnailImage(e.Bounds.Width, e.Bounds.Height, null, IntPtr.Zero);
   e.Graphics.DrawImage(img, e.Bounds.X, e.Bounds.Y);

   // Draw the focus rectangle around the selected item.
   e.DrawFocusRectangle();
}

Ответы [ 2 ]

1 голос
/ 29 марта 2012

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

В принципе, если вы идете по Документация MSDN :

Происходит при изменении визуального аспекта нарисованного владельцем ListBox.

Таким образом, это означает, что каждый раз при добавлении элементаэто событие называется.Кроме того, я думаю, что даже когда вы выполняете определенные действия рисования в этом методе, он будет вызываться сам (вы можете избежать этого, используя SuspendLayout и ResumeLayout в списке при обновлении),но я не уверен.

Вот кикер, насколько я знаю.Каждый раз, когда это событие срабатывает, это в значительной степени для каждого элемента в списке.(Это может быть полезно, так как вы можете отменить цвет ранее выбранного элемента, поэтому не переходите непосредственно к тому, что я собираюсь предложить, не продумав его до конца).Итак, DrawItemEventArgs имеет индекс нарисованного элемента.Используя это, вы можете сосредоточиться на конкретном элементе, который вам нужно нарисовать.Это может помочь вам повторно обработать что-то, что уже было обработано (имейте в виду примечания из вышеприведенной статьи ... скопированной ниже, о допустимом индексе, равном -1).

Для визуализации этого процесса:

Add 1 item  ->DrawItem

Add 2nd item->DrawItem
            ->DrawItem

Add 3rd item->DrawItem
            ->DrawItem
            ->DrawItem

Add nth item->DrawItem * n

Таким образом, это фактически приводит к некоторой ситуации типа Фибоначчи (3 элемента привели к 6 вызовам ... 5 приведет к вашему 15 номеру), и вы можете увидетькак начальная загрузка может быть громоздкой, и как добавление нового элемента делает для n вызовов метода.

Из статьи выше:

ListBox вызывает DrawItemметод для каждого элемента в его коллекции Items.

Аргумент DrawItemEventArgs для обработчика события DrawItem предоставляет свойство Index, значением которого является индекс элемента, который нужно нарисовать.Осторожно!Система вызывает событие DrawItem со значением индекса -1, когда коллекция Items пуста.Когда это происходит, вы должны вызвать методы DrawItemEventArgs.DrawBackground () и DrawFocusRectangle () и затем выйти.Цель вызова события - позволить элементу управления нарисовать прямоугольник фокуса, чтобы пользователи могли сказать, что он имеет фокус, даже когда нет элементов.Код перехватывает это условие, вызывает два метода и немедленно завершает работу обработчика

1 голос
/ 29 марта 2012

Если диалоговое окно появляется поверх вашего элемента управления, угадайте, что должен делать ваш элемент управления при закрытии диалогового окна? Он должен перерисовать себя!

Попробуйте использовать Debug.Writeline для записи отладочной информации, когда смотрите на элементы управления рисованием. Конечно, вы все равно должны быть осторожны, чтобы не использовать Visual Studio поверх формы! Это ситуация, когда два монитора действительно могут помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...