为什么Listbox.Drawitem被调用多次

本文关键字:调用 Listbox Drawitem 为什么 | 更新日期: 2023-09-27 18:29:22

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

然而,我注意到我必须单击exit两次才能退出应用程序(看起来第一次单击时它被称为selectedIndexChanged,第二次单击时退出)。在进一步检查时,我注意到当我单击列表框中的项目时,DrawItem事件会被触发多次(如15次以上)。列表框中一次只有1-2个项目!为什么它被叫了很多次soo?

我用一个非数据绑定的简单列表框测试了这一点,它也做了同样的事情。我只是好奇,因为我必须从磁盘上读取图像,并将其缩略图放入列表框,如果这样做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();
}

为什么Listbox.Drawitem被调用多次

我不确定,这必须经过测试,但这篇文章让我相信我要说的是真的。

基本上,如果您查看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

因此,这实际上导致了一种fibonacci类型的情况(3个项目导致6个调用…5个项目将导致15个数字),您可以看到初始加载是多么麻烦,以及添加一个新项目如何导致对该方法的n个调用。

来自上面的文章:

ListBox为其项目集合

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

如果控件顶部弹出一个对话框,请猜测关闭对话框时控件要做什么?它必须重新绘制自己!

查看绘图控件时,请尝试使用Debug.Writeline编写调试信息。当然,你仍然需要小心,不要把VisualStudio也拉到表单的顶部!在这种情况下,两台显示器确实可以提供帮助。