web-dev-qa-db-ja.com

別のコントロールにフォーカスしている場合でも、リストビューで選択した行の背景色を変更するにはどうすればよいですか?

入力デバイスとしてバーコードスキャナーを使用するプログラムがあるため、テキストボックスに焦点を合わせる必要があります。

プログラムにはリストビューコントロールがあり、特定のバーコードがスキャンされるときにプログラムで項目の1つを選択します。行の背景色を設定するには:

_listviewitem.BackColor = Color.LightSteelBlue;
_

私が試したもの:

  • _listview.HideSelection_をfalseに設定
  • 色を設定した後、listview.Focus()を呼び出します
  • _listviewitem.Focused_をtrueに設定
  • _listview.Invalidate_を呼び出します
  • listview.Update()を呼び出します
  • listview.Refresh()を呼び出します
  • 上記のさまざまな組み合わせ

また、タイマーで上記のものを組み合わせて、異なるスレッドで呼び出されるようにしましたが、まだ成功していません。

何か案は?

詳細:

  • ここで重要なのは、コントロールの焦点です。 リストビューコントロールにフォーカスがありませんアイテムの1つを選択すると。
  • 以下を実行して1つの項目を選択します。

    _listView1.Items[index].Selected = true;
    _
  • フォーカスは常にテキストボックスにあります。

  • コンピューターにはキーボードやマウスはなく、バーコードリーダーのみがあります。

私はテキストボックスに焦点を当てるためにこのコードを持っています:

_private void txtBarcode_Leave(object sender, EventArgs e)
{
   this.txtBarcode.Focus();
}
_

私の問題をシミュレートするには、そのコードをテキストボックスに追加する必要があります。

26
ian

説明したとおりに動作しますexactly期待どおりHideSelectionコントロールのListViewプロパティをFalseに設定したと仮定します。デモ用のスクリーンショットを次に示します。空のプロジェクトを作成し、ListViewコントロールとTextBoxコントロールをフォームに追加し、いくつかのサンプル項目をListViewに追加し、ビューを「詳細」に設定しました(ただし、任意のビュー)、およびHideSelectionをfalseに設定します。質問で示したようにTextBox.Leaveイベントを処理し、名前がListViewItemに入力されるたびに対応するTextBoxを選択する簡単なロジックを追加しました。 「テスト項目6」がListViewで選択されていることに注意してください

Screenshot of test project — note that "Test Item Six" is highlighted, even though the ListView control does not have the focus.

さて、最初に疑ったように、自分でBackColorプロパティを設定することで気が狂うと混乱するでしょう。コントロールがデフォルトの選択色を使用してデフォルトで選択された項目を示すため、なぜこれを行う必要があるのか​​わかりません。 differentの色を使用したい場合は、Windowsのテーマを変更する必要があります。それを行うためのコードを記述する必要はありません。

実際、既存のコードに加えてitem.BackColor = Color.LightSteelBlue行を追加して、ListViewItemに入力した名前に対応するTextBoxを選択すると、exactlyになります。上記と同じもの。アイテムの背景色は、コントロールにフォーカスを設定するまで変わりません。これはexpectedの動作です。選択されたアイテムは、親コントロールがフォーカスされていない場合とフォーカスが合っている場合の外観が異なるためです。フォーカスされたコントロール上の選択されたアイテムは、システムのハイライトカラーでペイントされます。フォーカスされていないコントロール上の選択されたアイテムは、システムの3Dカラーでペイントされます。そうしないと、ListViewコントロールにフォーカスがあるかどうかを判断できません。さらに、BackColorコントロールにフォーカスがある場合、オペレーティングシステムはカスタムListViewプロパティを完全に無視にします。背景は、デフォルトのシステムハイライトカラーでペイントされます。

もちろん、ListViewコントロールに明示的にフォーカスを設定すると、カスタム背景色がListViewItemに適用され、物事は私が持っている配色と非常に対照的な色でレンダリングされます私のコンピューターで選択されている(すべてのユーザーがデフォルトを使用しているわけではないことに注意してください)。ただし、問題はすぐに明らかになります。TextBox.Leaveイベントハンドラメソッドで記述したコードのため、ListViewコントロールにフォーカスを設定できません!

フォーカスを変更するイベントにフォーカスを設定することは、間違ったことです。それはあなたがそのようなことをすることを許可されていないWindowsの難しいルールであり、そして documentation はあなたにexplicitlyもしないことを警告します。おそらく、あなたの答えは「私がしなければならない」の線に沿ったものになるでしょうが、それは言い訳にはなりません。すべてが期待どおりに機能していれば、そもそもこの質問をすることはないでしょう。

ならどうしよう? アプリケーションの設計が壊れています。修正することをお勧めします。アイテムが選択されていることを示すためにBackColorプロパティを自分で設定しようとしないでください。 Windowsが選択したアイテムを強調表示するデフォルトの方法と競合します。また、フォーカスを変更するイベントでフォーカスを設定しようとしないでください。 Windowsはこれを明示的に禁止しており、ドキュメントでは、これを行うべきではないことが明確になっています。ターゲットコンピューターにマウスやキーボードがない場合、ユーザーがそれを行うためのコードを記述しない限り、ユーザーが最初に他の何かにフォーカスを設定する方法は不明です。

しかし、私はあなたがあなたのアプリケーションを修正したいと思うだろうという驚くほど小さな信仰を持っています。ドキュメントの警告を無視する人は、Q&Aサイトで善意のアドバイスを聞かない人と同じ傾向があります。だから私はあなたに骨を投げて、とにかくあなたが望む効果を得る方法を教えます。 重要な点は、ListViewItemSelectedプロパティを設定しないことです。これにより、カスタムBackColorとシステムのデフォルトのハイライト色との競合を回避できます。明示的にフォーカスをListViewコントロールに設定し、再度設定する必要がなくなります(上記で確立したように、Leaveイベントハンドラーメソッドが与えられた場合、実際には発生しません)。これを行うと、次の結果が生成されます。

Fixed sample — notice the ugly blue color of the "selected" item contrasting with my current theme settings.

コードは次のとおりです。あまりきれいではありませんが、これは単なる概念実証であり、ベストプラクティスのサンプルではありません。

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();
   }
}
34
Cody Gray

標準のListViewでは、selected行の背景色を設定できません。選択した行の背景(および前景)色は、OSのテーマによって常に制御されます。

これを回避するには、所有者がListViewを描画する必要がありますOR ObjectListView を使用できます。ObjectListViewは.NET WinForms ListViewのオープンソースラッパーです。これにより、muchが使いやすくなり、選択した行の色を変更するなど、通常のListViewでは非常に難しいことも簡単に許可されます。

this.objectListView1.UseCustomSelectionColors = true;
this.objectListView1.HighlightBackgroundColor = Color.Lime;
this.objectListView1.UnfocusedHighlightBackgroundColor = Color.Lime;

これはObjectListViewにnotフォーカスがない場合を示しています。

enter image description here

20
Grammarian

以下は、複数の選択を許可せず、画像(チェックボックスなど)を持たないListViewのソリューションです。

  1. ListViewのイベントハンドラーを設定します(この例では、名前はlistView1):
    • DrawItem
    • 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();
    }
    

注:このソリューションでは、ちらつきが発生します。これに対する修正には、保護されたプロパティDoubleBufferedをtrueに変更できるように、ListViewコントロールをサブクラス化することが含まれます。

public class ListViewEx : ListView
{
    public ListViewEx() : base()
    {
        this.DoubleBuffered = true;
    }
}
1
user2232952

SelectedIndexChangedで:

    private void lBxDostepneOpcje_SelectedIndexChanged(object sender, EventArgs e)
    {

        ListViewItem item = lBxDostepneOpcje.FocusedItem as ListViewItem;
        ListView.SelectedIndexCollection lista = lBxDostepneOpcje.SelectedIndices;
        foreach (Int32 i in lista)
        {
            lBxDostepneOpcje.Items[i].BackColor = Color.White;
        }
        if (item != null)
        {
            item.Selected = false;
            if (item.Index == 0)
            {
            }
            else
            {
                lBxDostepneOpcje.Items[item.Index-1].BackColor = Color.White;
            }
            if (lBxDostepneOpcje.Items[item.Index].Focused == true)
            {
                lBxDostepneOpcje.Items[item.Index].BackColor = Color.LightGreen;
                if (item.Index < lBxDostepneOpcje.Items.Count-1)
                {
                    lBxDostepneOpcje.Items[item.Index + 1].BackColor = Color.White;
                }
            }
            else if (lBxDostepneOpcje.Items[item.Index].Focused == false)
            {
                lBxDostepneOpcje.Items[item.Index].BackColor = Color.Blue;
            }
        }

    }
1
legmen

この状況ではリストビューコントロールにフォーカスを設定できません。 txtBarcode_Leaveメソッドはこれを防ぎます。ただし、リストビュー項目をクリックして選択できるようにする場合は、リストビューのMouseClickイベントハンドラーに以下のコードを追加してください。

    private void listView1_MouseClick(object sender, MouseEventArgs e)
    {
        ListView list = sender as ListView;

        for (int i = 0; i < list.Items.Count; i++)
        {
            if (list.Items[i].Bounds.Contains(e.Location) == true)
            {
                list.Items[i].BackColor = Color.Blue; // highlighted item
            }
            else
            {
                list.Items[i].BackColor = SystemColors.Window; // normal item
            }
        }
    }
0
Anton Semenov