即使专注于另一个控件,如何更改listview选择的行backcolor?

ian*_*ian 26 c# listview focus backcolor winforms

我有一个程序,它使用条形码扫描仪作为输入设备,这意味着我需要将焦点保持在文本框上.

该程序具有listview控件,当扫描某个条形码时,我以编程方式选择其中一个项目.我通过以下方式设置行的背景颜色:

listviewitem.BackColor = Color.LightSteelBlue;
Run Code Online (Sandbox Code Playgroud)

我尝试过的事情:

  • listview.HideSelection 设为false
  • listview.Focus()设置颜色后调用
  • listviewitem.Focused 设为true
  • 呼叫 listview.Invalidate
  • 呼叫 listview.Update()
  • 呼叫 listview.Refresh()
  • 以上的不同组合

我还在计时器中进行了上述组合,以便在不同的线程上调用它们但仍然没有成功.

有任何想法吗?

更多信息:

  • 这里的关键是控制焦点.当我选择其中一个项目时,listview控件没有焦点.
  • 我这样选择一个项目:

    listView1.Items[index].Selected = true;
    
    Run Code Online (Sandbox Code Playgroud)
  • Focus始终位于文本框中.

  • 电脑没有键盘或鼠标,只有条形码阅读器.

我有这个代码来关注文本框:

private void txtBarcode_Leave(object sender, EventArgs e)
{
   this.txtBarcode.Focus();
}
Run Code Online (Sandbox Code Playgroud)

你需要一个文本框添加该代码来模拟我的问题.

Cod*_*ray 34

假设您已将控件的HideSelection属性设置ListView为False ,您所描述的内容与预期完全一致.这是一个用于演示目的的屏幕截图.我创建了一个空白项目,向表单添加了一个ListView控件和一个TextBox控件,添加了一些示例项ListView,将其视图设置为"Details"(虽然这适用于任何视图),并设置HideSelection为false.我TextBox.Leave正如你在问题中所展示的那样处理了这个事件,并添加了一些简单的逻辑来选择相应ListViewItem的名称输入到TextBox.请注意,在以下位置选择"测试项目六" ListView:

     测试项目的屏幕截图 - 请注意,即使ListView控件没有焦点,也会突出显示

现在,正如我最初所怀疑的那样,如果你BackColor自己设置房产,那么你就会搞砸了.我不确定你为什么要这样做,因为控件已经默认使用默认选择颜色来指示所选项目.如果要使用不同的颜色,则应更改Windows主题,而不是尝试编写代码来执行此操作.

实际上,如果我item.BackColor = Color.LightSteelBlue在现有代码中添加该行以选择ListViewItem与键入的名称相对应的行TextBox,我得到的结果与上面显示的完全相同.在将焦点设置到控件之前,项目的背景颜色不会更改.这是预期的行为,因为所选项目在具有焦点时看起来与它们的父控件未聚焦时看起来不同.聚焦控件上的选定项目使用系统高亮颜色绘制; 未聚焦控件上的选定项目使用系统3D颜色绘制.否则,无法判断ListView控件是否具有焦点.此外,当控件具有焦点时,操作系统完全忽略任何自定义BackColor属性.背景以默认系统高亮颜色绘制.ListView

ListView当然,将焦点显式设置到控件会导致自定义背景颜色应用于ListViewItem,并且使用与我在计算机上选择的颜色方案形成鲜明对比的颜色渲染事物(请记住,并非每个人都使用默认值).但问题显而易见:由于您在事件处理程序方法中编写的代码,您无法将焦点设置为ListView控件TextBox.Leave!

我现在可以告诉你,把重点放在改变焦点的事件上是不对的.这是在Windows中你不能做这样的事情,一个硬性规定,以及文档甚至警告您明确不这样做.据推测,你的回答将是"我必须"的,但这不是理由.如果一切都按预期工作,那么你首先不会问这个问题.

所以现在怎么办?您的应用程序的设计已被破坏.我建议修理它.不要尝试和猴子BackColor自己设置属性以指示项目被选中.它与Windows突出显示所选项目的默认方式冲突.另外,请勿尝试将焦点设置在焦点变化事件中.Windows明确禁止这样做,文档很清楚,你不应该这样做.如果目标计算机没有鼠标或键盘,则不清楚用户如何将焦点设置到其他任何位置,除非您编写代码来执行此操作,而您不应该这样做.

但我很少相信你会想要修复你的应用程序.忽略文档中警告的人往往是那些不在Q&A网站上听取善意建议的人.因此,我会告诉你如何获得你想要的效果.关键在于没有设置ListViewItemSelected属性,从而避免了您的自定义之间的冲突BackColor和系统默认高亮颜色.它还使您不必将焦点显式设置为ListView控件并再次返回(正如我们上面所建到的,实际上并没有发生,给定您的Leave事件处理程序方法).这样做会产生以下结果:

     修复了样本 - 注意

这里是代码 - 它不是很漂亮,但这只是一个概念证明,而不是最佳实践的样本:

public partial class Form1 : Form
{
   public Form1()
   {
      InitializeComponent();
      listView1.View = View.Details;
      listView1.HideSelection = false;
   }

   private void textBox1_TextChanged(object sender, EventArgs e)
   {
      foreach (ListViewItem item in listView1.Items)
      {
         if (item.Text == textBox1.Text)
         {
            item.BackColor = Color.LightSteelBlue;
            return;
         }
      }
   }

   private void textBox1_Leave(object sender, EventArgs e)
   {
      this.textBox1.Focus();
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 哇 - 你这里的部分有点严厉!但总的来说这是一个很好的答案;) (9认同)
  • @Vidar:嗯,经过反思,你是对的.我不知道为什么我这么沮丧.也许在评论帖中有一个扩展的交换,现在已被删除.或者我心情不好.我并不总是那么高兴!但令人沮丧的应用程序*确实*弄错了...... (4认同)
  • 谢谢!今天早上和我一起战斗了几个小时. (2认同)

Gra*_*ian 20

标准ListView不允许您设置所选行的背景颜色.所选行的背景(和前景)颜色始终由OS的主题控制.

你必须由所有者绘制你ListView来解决这个问题,或者你可以使用ObjectListView.ObjectListView是一个围绕.NET WinForms ListView的开源包装器,它使得它容易使用,并且可以轻松地允许在普通ListView中非常困难的东西 - 比如改变了所选行的颜色.

this.objectListView1.UseCustomSelectionColors = true;
this.objectListView1.HighlightBackgroundColor = Color.Lime;
this.objectListView1.UnfocusedHighlightBackgroundColor = Color.Lime;
Run Code Online (Sandbox Code Playgroud)

这显示了ObjectListView 没有焦点时.

在此输入图像描述

  • 好主意,但请注意它不适用于"列表"视图模式; 你必须切换到"详细信息"查看模式 (3认同)

小智 6

这是一个 ListView 的解决方案,它不允许多项选择并且没有图像(例如复选框)。

  1. 为 ListView 设置事件处理程序(在本例中其名为listView1):
    • 绘制项目
    • Leave(当ListView失去焦点时调用)
  2. 声明一个全局 int 变量(即包含 ListView 的 Form 的成员,在本例中它名为gListView1LostFocusItem)并为其分配值 -1
    • int gListView1LostFocusItem = -1;
  3. 按如下方式实现事件处理程序:

    private void listView1_Leave(object sender, EventArgs e)
    {
        // Set the global int variable (gListView1LostFocusItem) to
        // the index of the selected item that just lost focus
        gListView1LostFocusItem = listView1.FocusedItem.Index;
    }
    
    private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
    {
        // If this item is the selected item
        if (e.Item.Selected)
        {
            // If the selected item just lost the focus
            if (gListView1LostFocusItem == e.Item.Index)
            {
                // Set the colors to whatever you want (I would suggest
                // something less intense than the colors used for the
                // selected item when it has focus)
                e.Item.ForeColor = Color.Black;
                e.Item.BackColor = Color.LightBlue;
    
               // Indicate that this action does not need to be performed
               // again (until the next time the selected item loses focus)
                gListView1LostFocusItem = -1;
            }
            else if (listView1.Focused)  // If the selected item has focus
            {
                // Set the colors to the normal colors for a selected item
                e.Item.ForeColor = SystemColors.HighlightText;
                e.Item.BackColor = SystemColors.Highlight;
            }
        }
        else
        {
            // Set the normal colors for items that are not selected
            e.Item.ForeColor = listView1.ForeColor;
            e.Item.BackColor = listView1.BackColor;
        }
    
        e.DrawBackground();
        e.DrawText();
    }
    
    Run Code Online (Sandbox Code Playgroud)

注意:此解决方案会导致一些闪烁。解决此问题的方法包括对 ListView 控件进行子类化,以便您可以将受保护的属性DoubleBuffered更改为 true。

public class ListViewEx : ListView
{
    public ListViewEx() : base()
    {
        this.DoubleBuffered = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 控件或窗体不必监听它们自己的事件。只需重写 OnDrawItem 等即可。 (2认同)