在 WinForm ListView 控件中覆盖 Drawitem 事件

4

当失去焦点时,我希望我的ListView的选定项目仍然清晰可见(在Windows 7上是暗灰色)。我将HideSelection属性设置为False。

我想对列表视图做与此处针对TreeView控件所做的相同操作,即重写Drawnode事件:

C# WinForms highlight treenode when treeview doesnt have focus

我推测我需要将OwnerDraw属性设置为True并覆盖DrawItem事件,但我不确定在此事件中需要做什么.... :-)

1个回答

13
你需要类似这样的东西:
private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
    e.DrawDefault = true;
}

private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
    const int TEXT_OFFSET = 1;    // I don't know why the text is located at 1px to the right. Maybe it's only for me.

    ListView listView = (ListView)sender;

    // Check if e.Item is selected and the ListView has a focus.
    if (!listView.Focused && e.Item.Selected)
    {
        Rectangle rowBounds = e.SubItem.Bounds;
        Rectangle labelBounds = e.Item.GetBounds(ItemBoundsPortion.Label);
        int leftMargin = labelBounds.Left - TEXT_OFFSET;
        Rectangle bounds = new Rectangle(rowBounds.Left + leftMargin, rowBounds.Top, e.ColumnIndex == 0 ? labelBounds.Width : (rowBounds.Width - leftMargin - TEXT_OFFSET), rowBounds.Height);
        TextFormatFlags align;
        switch (listView.Columns[e.ColumnIndex].TextAlign)
        {
            case HorizontalAlignment.Right:
                align = TextFormatFlags.Right;
                break;
            case HorizontalAlignment.Center:
                align = TextFormatFlags.HorizontalCenter;
                break;
            default:
                align = TextFormatFlags.Left;
                break;
        }
        TextRenderer.DrawText(e.Graphics, e.SubItem.Text, listView.Font, bounds, SystemColors.HighlightText,
            align | TextFormatFlags.SingleLine | TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.VerticalCenter | TextFormatFlags.WordEllipsis);
    }
    else
        e.DrawDefault = true;
}

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
    ListView listView = (ListView)sender;

    // Check if e.Item is selected and the ListView has a focus.
    if (!listView.Focused && e.Item.Selected)
    {
        Rectangle rowBounds = e.Bounds;
        int leftMargin = e.Item.GetBounds(ItemBoundsPortion.Label).Left;
        Rectangle bounds = new Rectangle(leftMargin, rowBounds.Top, rowBounds.Width - leftMargin, rowBounds.Height);
        e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds);
    }
    else
        e.DrawDefault = true;
}

编辑:针对 View = View.DetailsFullRowSelect = true 进行了改进。
编辑2:考虑到列的不同对齐方式,还添加了自动省略标志。


很不幸,我的列表有多列,当选择一行时,除了第一列外,所有文本都会消失。我能否请你帮忙实现绘制子项的方法?我正在使用完整行选择。 - Chad
我已根据您的需求更新了代码。现在,所有三个事件 DrawItemDrawSubItemDrawColumnHeader 都已处理完毕。 - Dmitry
除了当列不够大以显示完整文本并添加省略号时出现的小问题外,它运行得非常好。完整的列文本溢出到下一列。 - Chad
已修复。同时考虑了列的不同对齐方式。 - Dmitry
当我在一个表单上有多个ListView并且在表单显示后鼠标悬停在第二个ListView上时,除了第一列以外的所有文本都会消失,我遇到了同样的问题。当我点击这个第二个ListView之后,这种情况就会得到解决,然后一切都正常绘制。 - Andark
1
我已经找到了问题所在,它是底层Win32控件中的一个错误。当鼠标指针移动到行上时,在详细视图中每行会发生一次DrawItem事件,但没有相应的DrawSubItem事件。如果有人遇到同样的问题,解决方案在这里:链接 - Andark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接