为什么Listbox.Drawitem被多次调用?

时间:2012-03-29 18:39:10

标签: c# winforms listbox

我的C#WinForm上有一个数据绑定列表框,其中包含指向图像文件位置的链接。我想将图像显示为缩略图,供用户单击和查看。我通过设置DrawMode = OwnerDrawVariable并处理DrawItem和MeasureItem事件使其正常工作。

但是我注意到我必须单击退出2次以退出应用程序(看起来在第一次单击时称为selectedIndexChanged,然后在第二次退出时退出)。在进一步检查时,我注意到当我单击列表框中的项目(例如15次以上)时,DrawItem事件被多次触发。列表框中一次只有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 个答案:

答案 0 :(得分:1)

如果控件顶部弹出一个对话框,请猜出关闭对话框时控件必须执行的操作?它必须重绘自己!

在查看绘图控件时,尝试使用Debug.Writeline编写调试信息。当然,您仍然需要注意不要将Visual Studio拉到表单顶部!这是两个监视器真正可以提供帮助的情况。

答案 1 :(得分:1)

我不确定这是否必须进行测试,但this article makes me believe that what I am going to say is true.

基本上,如果你去MSDN documentation

  

当所有者绘制的ListBox的可视方面发生更改时发生。

因此,这意味着每次添加项目时,都会调用此事件。此外,我认为即使您在此方法中执行某些绘制操作,它也会调用自身(您可能可以在更新时使用列表框上的SuspendLayoutResumeLayout来避免这种情况),但我不确定

就我所知,这是踢球者。每次触发此事件时,它几乎都是列表中的每个项目。 (这可能很有用,因为你可以对以前选择的项目进行去色处理,所以不要直接跳到我要建议的内容而不考虑它)。因此,DrawItemEventArgs具有正在绘制的项目的索引。使用它,您可以专注于您需要绘制的特定项目。这可能会帮助您重新处理已经处理过的内容(请记住上面文章中的注释。如下所示,关于索引允许为-1)。

想象这个过程:

Add 1 item  ->DrawItem

Add 2nd item->DrawItem
            ->DrawItem

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

Add nth item->DrawItem * n

所以,这实际上会产生一种fibonacci type situation(3个项目导致6个调用... 5个会导致你的15个数字),你可以看到初始加载如何繁琐,以及如何为该方法的n次调用添加一个新项目。

从上面的文章:

  

ListBox对其中的每个项重复调用DrawItem方法   物品集合。

     

DrawItem事件处理程序的DrawItemEventArgs参数公开   一个Index属性,其值是要绘制的项的索引。   小心!系统引发DrawItem事件,索引值为   当Items集合为空时为-1。当发生这种情况时,你应该调用DrawItemEventArgs.DrawBackground()和DrawFocusRectangle()   方法然后退出。举起这个活动的目的是为了让   控件绘制一个焦点矩形,以便用户可以告诉它有   焦点,即使没有物品存在。代码陷阱   condition,调用这两个方法,然后退出处理程序   立即