ListView OwnerDraw 的默认实现
我有一个 ListView 我想调整项目的绘制(例如突出显示列表视图项目中的某些字符串),但是我不想从根本上改变项目的显示方式。
我已将 OwnerDraw 设置为 true并且可以让我了解如何绘制突出显示效果,但是每当我尝试遵循默认实现来绘制列表视图项的其余部分时,就会出现问题,并且我会留下一大堆图形问题,表明实际上我已经完全出了问题。
是否有地方可以看到 DrawItem
和 DrawSubItem
事件这样做以便我可以更好地理解并更轻松地调整我的代码?
作为参考,这里是一个显示我当前正在做的事情的片段:
public MyListView()
{
this.OwnerDraw = true;
this.DoubleBuffered = true;
this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader);
this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem);
this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem);
}
private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Not interested in changing the way columns are drawn - this works fine
e.DrawDefault = true;
}
private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
}
private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0)
{
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
}
e.DrawText();
}
I have a ListView where I wish to tweak the drawing of items (for example highlighting certain strings in list view itmes), however I don't want to radically alter the way that items are displayed.
I have set the OwnerDraw to true and can get my head around how to draw my highlighting effect, however whenever I try to defer to the default implementation to draw the rest of the list view item things go wrong and I'm left with a whole load of graphical problems indicating that actually I've completely gone wrong.
Is there somewhere that I can see what the "Default" handlers for the DrawItem
and DrawSubItem
events do so that I can better my understanding and more easily tweak my code?
For reference here is a snippet showing what I'm currently doing:
public MyListView()
{
this.OwnerDraw = true;
this.DoubleBuffered = true;
this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader);
this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem);
this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem);
}
private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Not interested in changing the way columns are drawn - this works fine
e.DrawDefault = true;
}
private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
}
private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0)
{
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
}
e.DrawText();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我现在没有时间写出完整的答案,所以我会记下一些快速笔记并稍后再回来。
正如 LarsTech 所说,所有者绘制
ListView
控件是一件痛苦的事情 - .NetListView
类是底层 Win32 列表视图控件 以及“所有者抽奖”由NM_CUSTOMDRAW
通知代码。因此,不存在“默认 .Net 实现”——默认是使用底层 Win32 控件。为了让生活变得更加困难,需要考虑一些额外的因素:
DrawItem
和 < code>DrawSubItem 您很可能将第一个单元格的内容绘制两次。DrawItem
事件,而没有相应的DrawSubItem
事件,这意味着如果您在DrawItem
事件,然后在DrawSubItem
事件中绘制文本,当您将鼠标悬停在项目上时,项目文本将消失。ItemState
属性并不总是正确的,例如在调整列大小后。因此我发现最好不要依赖它。DrawItem
事件首先发生,因此您在DrawItem
处理程序中绘制的任何内容(例如选择效果)很可能会被您在DrawSubItem
中执行的操作覆盖code> 处理程序(例如,某些单元格具有不同的背景颜色)。总而言之,处理所有者绘图是一件相当复杂的事情 - 我发现最好在
DrawSubItem
事件中处理所有绘图,最好通过以下方式执行您自己的双缓冲使用BufferedGraphics
类。我还发现查看 ObjectListView 的源代码非常方便。
最后,所有这些只是为了处理列表视图的详细信息模式(我正在使用的唯一模式),如果您希望其他模式也能工作,那么我相信还有一些额外的事情需要考虑。
当我有机会时,我会尝试发布我的工作示例代码。
I haven't got the time now to write up a complete answer so instead I'll put down some quick notes and come back to it later.
As LarsTech said, owner drawing a
ListView
control is a pain - the .NetListView
class is a wrapper around the underlying Win32 List View Control and the ability to "Owner draw" is provided by theNM_CUSTOMDRAW
notification code. As such there is no "default .Net implementation" - the default is to use the underlying Win32 control.To make life even more difficult there are a number of extra considerations to make:
DrawItem
andDrawSubItem
you may well be drawing the contents of the first cell twice.DrawItem
event will occur without correspondingDrawSubItem
events, meaning that if you draw a background in theDrawItem
event and then draw the text in theDrawSubItem
event your item text will disappear when you mouse over.ItemState
property is not always correct, for example just after resizing a column. Consequently I've found its best not to rely on it.DrawItem
event occurs first, anything you draw in theDrawItem
handler (e.g. the selection effect) may well be overlayed by things you do in theDrawSubItem
handler (e.g. having certain cells with a different background color).All in all handling owner drawing is a fairly involved affair - I found it best to handle all drawing inside the
DrawSubItem
event, its also best to perform your own double-buffering by using theBufferedGraphics
class.I also found looking at the source code for ObjectListView very handy.
Finally, all of this is just to handle the details mode of the list view (the only mode I am using), if you want the other modes to work too then I believe that there are extra things to take account of.
When I get a chance I'll try and post my working example code.
我不知道这是否完全对您有帮助,但我会添加一些注释:
需要记住的一件事是
DrawSubItem
也会绘制第一个项目,这可能就是您的位置正在获得双重渲染
外观。一些需要尝试的事情(不考虑速度):
对于您的 DrawSubItem 例程,请确保您没有在第一列中绘制,并且我添加了
DrawBackground()
例程。我在突出显示矩形中添加了一些剪辑,这样它就不会在列参数之外进行绘制。一般来说,绘制 ListView 控件的所有者在一个受伤的世界中是受欢迎的。您不再使用视觉样式进行绘图,您也必须自己这样做。啊。
I don't know if this will completely help you, but I'll add a few notes:
One thing to keep in mind is that
DrawSubItem
will draw the first item, too, and that's probably where you are getting thedouble-rendered
look from.Some things to try (not factored for speed):
For your DrawSubItem routine, make sure you aren't drawing in the first column and I added the
DrawBackground()
routine. I added some clipping to the highlight rectangle so it wouldn't paint outside the column parameters.In general, owner drawing a ListView control is welcoming in a world of hurt. You aren't drawing in Visual Styles anymore, you would have to do that yourself, too. Ugh.
所选项目的背景颜色已更改。 Windows 中默认为蓝色。此代码将对您在任何颜色下都有帮助:
Item selected back color changed. In by default blue in windows. This code will help for u in any colors:
ComponentOwl 最近发布了一个名为 Better ListView Express 的免费软件组件。
它的外观和行为与 ListView 完全相同,但具有 更多功能强大的所有者绘图功能 - 您可以在所有元素上准确绘图,甚至可以关闭某些绘图(例如,选择让您打开)。
ComponentOwl recently released a freeware component called Better ListView Express.
It looks and behaves exactly like the ListView, but has much more powerful owner drawing capabilities - you can draw accurately over all elements and even turn off some drawing (e.g. selection to make you on).