如何更改ListView默认选中颜色?

4

我试图更改ListView选择栏的默认(蓝色)颜色。
我不想使用ObjectListView,因为我将不得不更改所有代码。

我搜索了这个主题并在这里找到了一些答案:
更改ListView选择背景颜色?
但这指向了ObjectListView。

之前当我使用ListBox时,以下方法可以将选择栏颜色设置为我喜欢的颜色:

  1. 在属性下设置DrawMode为OwnerDrawFixed
  2. 在事件下设置DrawItem为ListBox1_DrawItem

private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index < 0) return;
    //if the item state is selected them change the back color 
    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
        e = new DrawItemEventArgs(e.Graphics,
                                  e.Font,
                                  e.Bounds,
                                  e.Index,
                                  e.State ^ DrawItemState.Selected,
                                  e.ForeColor,
                                  Color.FromArgb(43, 144, 188));//Choose the color

    // Draw the background of the ListBox control for each item.
    e.DrawBackground();
    // Draw the current item text
    e.Graphics.DrawString(lb_result.Items[e.Index].ToString(), e.Font, Brushes.Black, e.Bounds, StringFormat.GenericDefault);
    // If the ListBox has focus, draw a focus rectangle around the selected item.
    e.DrawFocusRectangle();
}

但是我现在正在使用ListView。

  1. 我将OwnerDraw设置为True。
  2. 我将DrawItem设置为ListView1_DrawItem

...并使用上面的代码。

我期望它会像所述的那样显示不同的选择颜色,但是我得到了一些错误:

errors

如何为ListView正确地使用此代码?


你只需要:if (e.Item.Selected) => 绘制背景(e.Graphics.FillRectangle())和文本。如果未被选中,则 e.DrawDefault = true;。你可以使用 e.Bounds 测量来填充矩形,使用任何你想要的颜色。如果 ListView 包含任何位图,你还需要绘制它们。 - Jimi
@Jimi,你能给我展示一下代码的例子吗? 我正在尝试,但是做不对。 - Bas Curtiz
1个回答

6

绘制一个ListView控件比ListBox控件更加复杂:需要处理更多的细节。本例考虑了ListView的四个视图设置:View.DetailsView.ListView.TileView.SmallIcon

这里只绘制文本(这就是为什么没有包括View.LargeIcon),以限制代码长度。有关绘制与ListView链接的ImageList中包含的位图的示例,请参见此处

设置 ListView:
启用ListView的OwnerDraw模式,然后订阅其DrawItemDrawSubItemDrawColumnHeader事件,如示例代码所示(如果要ListView显示任何内容,则必须这样做)。

标题使用默认渲染方式绘制(设置e.DrawDefault = true)。

常规操作描述:
使用TextRenderer.DrawText绘制项目文本:这是ListView用于绘制项的原始方法。它允许精确匹配默认渲染效果,因此我们不会注意到文本的某些对齐问题。

DrawItem事件用于在所有View模式下绘制自定义背景,并在除View.Details之外的所有模式下绘制项目文本;而在View.Details中,DrawSubItems事件则起到作用:如果DrawItem事件执行相同任务,则会将第一个项目的文本绘制两次。

View设置为TileList时,DrawSubItems事件不会被调用

代码说明:
一个辅助方法GetTextAlignment负责设置项目的对齐方式,因为每个列可以具有特定的文本对齐方式。

一个名为Color listViewSelectionColor的属性,用于设置/更改所选项目的颜色。要修改选择颜色,请将此字段设置为任何值并使用Invalidate()方法使ListView失效:新颜色将立即应用。

结果示例:

ListView OwnerDraw

bool lvEditMode = false;
Color listViewSelectionColor = Color.Orange;

protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
    var lView = sender as ListView;

    if (lvEditMode || lView.View == View.Details) return;
    TextFormatFlags flags = GetTextAlignment(lView, 0);
    Color itemColor = e.Item.ForeColor;

    if (e.Item.Selected) {
        using (var bkBrush = new SolidBrush(listViewSelectionColor)) {
            e.Graphics.FillRectangle(bkBrush, e.Bounds);
        }
        itemColor = e.Item.BackColor;
    }
    else {
        e.DrawBackground();
    }

    TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, itemColor, flags);

    if (lView.View == View.Tile && e.Item.SubItems.Count > 1) {
        var subItem = e.Item.SubItems[1];
        flags = GetTextAlignment(lView, 1);
        TextRenderer.DrawText(e.Graphics, subItem.Text, subItem.Font, e.Bounds, SystemColors.GrayText, flags);
    }
}

private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
    var lView = sender as ListView;
    TextFormatFlags flags = GetTextAlignment(lView, e.ColumnIndex);
    Color itemColor = e.Item.ForeColor;

    if (e.Item.Selected && !lvEditMode) {
        if (e.ColumnIndex == 0 || lView.FullRowSelect) {
            using (var bkgrBrush = new SolidBrush(listViewSelectionColor)) {
                e.Graphics.FillRectangle(bkgrBrush, e.Bounds);
            }
            itemColor = e.Item.BackColor;
        }
    }
    else  {
        e.DrawBackground();
    }
    TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, itemColor, flags);
}

protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
    => e.DrawDefault = true;

private TextFormatFlags GetTextAlignment(ListView lstView, int colIndex)
{
    TextFormatFlags flags = (lstView.View == View.Tile)
        ? (colIndex == 0) ? TextFormatFlags.Default : TextFormatFlags.Bottom
        : TextFormatFlags.VerticalCenter;

    if (lstView.View == View.Details) flags |= TextFormatFlags.LeftAndRightPadding;

    if (lstView.Columns[colIndex].TextAlign != HorizontalAlignment.Left) {
        flags |= (TextFormatFlags)((int)lstView.Columns[colIndex].TextAlign ^ 3);
    }
    return flags;
}

private void listView1_BeforeLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = true;

private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = false;  

感谢您的深入反馈。非常好用! - Bas Curtiz
唯一的问题是,当我将鼠标悬停在listView中的第一个结果上时(使用listView详细模式),我正在使用的其他列会消失:https://imgur.com/Fkst2Ux 你有什么想法,这是否与代码相关,还是我需要分配一个MouseEnter? - Bas Curtiz
由于我在这里发布的代码没有出现这种情况,所以可能与您的ListView中的其他事件有关,而我不知道。您能否更新问题并附上您现在使用的代码,以便我可以测试它? - Jimi
我认为这与以下内容有关(请参见注释): https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.drawitem?redirectedfrom=MSDN&view=netframework-4.7.2#remarks 以及这个:https://stackoverflow.com/questions/45643212/vb-net-listview-ownerdraw-subitems-disappear-on-hovering - Bas Curtiz
这已经在此处呈现的代码中处理了。请阅读“常见操作描述”部分。您需要按照此处的说明实现您的代码。使用三列和一些项目/子项目创建一个测试 ListView,并完全按照此代码使用。它应该像图形示例中所示的那样运行。 - Jimi
显示剩余6条评论

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