【问题标题】:How to change default selection color of a ListView?如何更改 ListView 的默认选择颜色?
【发布时间】:2019-04-03 12:18:08
【问题描述】:

我正在尝试更改 ListView 中选择栏的默认(蓝色)颜色。
我拒绝使用 ObjectListView,因为我必须更改所有代码。

我已经搜索过这个主题并在这里找到了一些答案:
Change background selection color of 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

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

我希望它会显示不同的选择颜色,但我得到了一些错误:

如何将这段代码正确地用于 ListView?

【问题讨论】:

  • 您只需要:if (e.Item.Selected) =&gt; 绘制背景 (e.Graphics.FillRectangle()) 和文本。如果未选中,e.DrawDefault = true;。您可以使用e.Bounds 度量值填充矩形,使用您想要的任何颜色。如果 ListView 包含任何位图,您还需要绘制位图。
  • @Jimi 你能给我看一下代码示例吗?我正在尝试,但没有做对。

标签: c# winforms listview colors selection


【解决方案1】:

所有者绘制 ListView 控件比 ListBox 控件更复杂:需要处理更多细节。 这是一个考虑 ListView 的四个 View 设置的示例:
View.DetailsView.ListView.TileView.SmallIcon

此处仅绘制文本(这就是不包括 View.LargeIcon 的原因),以将代码包含在适当的限制范围内。
绘制链接到 ListView 的 ImageList 中包含的位图的示例是 here

设置 ListView
启用您的 ListView OwnerDraw 模式,然后订阅其 DrawItemDrawSubItemDrawColumnHeader 事件,如示例代码所示(如果您希望 ListView 显示任何内容,则为强制性)。

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

常用操作说明
项目文本是使用TextRenderer.DrawText 绘制的:这是 ListView 用来绘制其项目的原始方法。它允许与默认渲染完全匹配,因此我们不会注意到文本的一些错位。

DrawItem 事件用于在所有 View 模式下绘制自定义背景,并将在除 View.Details 之外的所有模式下绘制项目的文本, DrawSubItems 事件发挥作用的地方:如果DrawItem 事件执行相同的任务,我们将绘制第一个项目的文本两次。

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

此处提供的代码的详细信息
辅助方法 GetTextAlignment 负责设置项目的对齐方式,因为每个列都可以有特定的文本对齐方式。

Color listViewSelectionColor 字段用于设置/更改所选项目的颜色。要修改选择颜色,将此字段设置为任意值并Invalidate() ListView:将立即应用新颜色。

结果示例

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;  

【讨论】:

  • 感谢您的深入反馈。像魅力一样工作!
  • 唯一的问题是,当我将鼠标悬停在 listView 中的第一个结果上时,我正在使用的其他列(listView 详细信息模式)消失了:imgur.com/Fkst2Ux 知道这是否与代码有关还是我需要分配一个 MouseEnter?
  • 由于我在这里发布的代码没有发生这种情况,它可能与我不知道的 ListView 中的其他事件有关。你能用你现在使用的代码更新问题,以便我测试它吗?
  • 这已在此处提供的代码中处理。阅读常见操作说明:部分。您需要按照此处所述实现您的代码。制作一个带有 3 列和一些项目/子项目的测试 ListView,并完全按照它的方式使用此代码。它的行为应该与您在图形示例中看到的一样。
猜你喜欢
  • 2016-05-26
  • 1970-01-01
  • 2016-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多