web-dev-qa-db-fra.com

Comment changer la couleur de fond de la ligne sélectionnée de la vue de liste même lorsque vous vous concentrez sur un autre contrôle?

J'ai un programme qui utilise un lecteur de codes-barres comme périphérique d'entrée, ce qui signifie que je dois rester concentré sur une zone de texte.

Le programme a un contrôle listview et je sélectionne l'un des éléments par programme lorsqu'un certain code à barres est scanné. J'ai défini la couleur d'arrière-plan de la ligne en:

listviewitem.BackColor = Color.LightSteelBlue;

Ce que j'ai essayé:

  • listview.HideSelection Défini sur false
  • appeler listview.Focus() après avoir défini la couleur
  • listviewitem.Focused Défini sur true
  • appelez listview.Invalidate
  • appeler listview.Update()
  • appeler listview.Refresh()
  • différentes combinaisons de ce qui précède

J'ai également fait des combinaisons ci-dessus dans un minuteur pour qu'elles soient appelées sur un thread différent mais toujours sans succès.

Des idées?

Plus d'informations:

  • La clé ici est la concentration du contrôle. Le le contrôle listview n'a pas le focus lorsque je sélectionne l'un des éléments.
  • Je sélectionne un élément en faisant:

    listView1.Items[index].Selected = true;
    
  • le focus est toujours dans la zone de texte.

  • l'ordinateur n'a pas de clavier ou de souris, seulement un lecteur de codes-barres.

J'ai ce code pour garder le focus sur la zone de texte:

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

Vous devez avoir une zone de texte ajouter ce code pour simuler mon problème.

26
ian

Ce que vous décrivez fonctionne exactement comme prévu , en supposant que vous avez défini la propriété HideSelection de le contrôle ListView à False. Voici une capture d'écran à des fins de démonstration. J'ai créé un projet vide, ajouté un contrôle ListView et un contrôle TextBox à un formulaire, ajouté quelques exemples d'éléments au ListView, défini sa vue sur "Détails" (bien que cela fonctionne dans n'importe quelle vue) et définissez HideSelection sur false. J'ai géré le TextBox.Leave comme vous l'avez montré dans la question, et ajouté une logique simple pour sélectionner le ListViewItem correspondant chaque fois que son nom était entré dans le TextBox. Notez que "Test Item Six" est sélectionné dans le ListView:

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

Maintenant, comme je le soupçonnais au départ, vous allez gâcher les choses si vous vous débrouillez avec la définition de la propriété BackColor vous-même . Je ne sais pas pourquoi vous voudriez faire cela, car le contrôle utilise déjà les couleurs de sélection par défaut pour indiquer les éléments sélectionnés par défaut. Si vous souhaitez utiliser différent couleurs, vous devez changer votre thème Windows, plutôt que d'essayer d'écrire du code pour le faire.

En fait, si j'ajoute la ligne item.BackColor = Color.LightSteelBlue en plus de mon code existant pour sélectionner le ListViewItem correspondant au nom tapé dans le TextBox, j'obtiens exactement la même chose que celle montrée ci-dessus. La couleur d'arrière-plan de l'élément ne change pas jusqu'à ce que vous définissiez le focus sur le contrôle. C'est le comportement attendu, car les éléments sélectionnés sont différents lorsqu'ils ont le focus qu'ils ne le font lorsque leur contrôle parent n'est pas focalisé. Les éléments sélectionnés sur les commandes focalisées sont peints avec la couleur de surbrillance du système; les éléments sélectionnés sur les commandes floues sont peints avec la couleur 3D du système. Sinon, il serait impossible de dire si le contrôle ListView avait ou non le focus. De plus, toute propriété personnalisée BackColor est complètement ignorée par le système d'exploitation lorsque le contrôle ListView a le focus. L'arrière-plan est peint dans la couleur de surbrillance par défaut du système.

Définir explicitement le focus sur le contrôle ListView, bien sûr, entraîne l'application de la couleur d'arrière-plan personnalisée au ListViewItem et le rendu des choses avec une couleur qui contraste beaucoup avec le jeu de couleurs que je 'ai sélectionné sur mon ordinateur (rappelez-vous, tout le monde n'utilise pas les valeurs par défaut). Le problème, cependant, devient immédiatement évident: vous ne pouvez pas définir le focus sur le contrôle ListView en raison du code que vous avez écrit dans le TextBox.Leave méthode du gestionnaire d'événements!

Je peux vous dire dès maintenant que fixer le focus dans un événement qui change le focus est la mauvaise chose à faire. C'est une règle difficile dans Windows, vous n'êtes pas autorisé à faire des choses comme ça, et le documentation vous avertit même explicitement de ne pas le faire. Vraisemblablement, votre réponse sera quelque chose du genre "Je dois le faire", mais ce n'est pas une excuse. Si tout fonctionnait comme prévu, vous ne poseriez pas cette question en premier lieu.

Et maintenant? La conception de votre application est défectueuse. Je suggère de le réparer. N'essayez pas de singe en définissant la propriété BackColor vous-même pour indiquer qu'un élément est sélectionné. Cela entre en conflit avec la façon par défaut dont Windows met en évidence les éléments sélectionnés. N'essayez pas non plus de définir le focus dans un événement qui change le focus. Windows l'interdit explicitement et la documentation indique clairement que vous n'êtes pas censé le faire. Si l'ordinateur cible n'a pas de souris ou de clavier, on ne sait pas comment l'utilisateur va définir le focus sur quoi que ce soit d'autre en premier lieu, sauf si vous écrivez du code pour le faire, ce que vous ne devriez pas faire.

Mais je suis étonnamment peu convaincu que vous voudrez réparer votre application. Les personnes qui ignorent les avertissements dans la documentation ont tendance à être les mêmes personnes qui n'écoutent pas les conseils bien intentionnés sur les sites de questions/réponses. Je vais donc vous jeter un os et vous dire comment obtenir l'effet que vous désirez de toute façon. La clé réside dans le fait de ne pas définir la propriété ListViewItem de Selected, ce qui évite le conflit entre votre BackColor personnalisé et la couleur de surbrillance par défaut du système. Cela vous évite également d'avoir à définir explicitement le focus sur le contrôle ListView et inversement (ce qui, comme nous l'avons établi ci-dessus, ne se produit pas réellement, étant donné votre Leave méthode du gestionnaire d'événements). Faire cela produit le résultat suivant:

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

Et voici le code - ce n'est pas très joli, mais ce n'est qu'une preuve de concept, pas un échantillon des meilleures pratiques:

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

Un ListView standard ne vous permet pas de définir la couleur d'arrière-plan d'une ligne sélectionnée. Les couleurs d'arrière-plan (et de premier plan) d'une ligne sélectionnée sont toujours contrôlées par le thème du système d'exploitation.

Vous devez dessiner votre ListView pour contourner ce problème OR vous pouvez utiliser ObjectListView . ObjectListView est un wrapper open source autour de .NET WinForms ListView, ce qui le rend beaucoup plus facile à utiliser, tout en permettant facilement des choses qui sont très difficiles dans un ListView normal - comme changé les couleurs des lignes sélectionnées.

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

Cela montre l'ObjectListView quand il n'a pas le focus.

enter image description here

20
Grammarian

Voici une solution pour un ListView qui n'autorise pas plusieurs sélections et n'a pas d'images (par exemple des cases à cocher).

  1. Définissez des gestionnaires d'événements pour ListView (dans cet exemple, il est nommé listView1):
    • DrawItem
    • Quitter (invoqué lorsque le focus de ListView est perdu)
  2. Déclarez une variable int globale (c'est-à-dire un membre du formulaire qui contient le ListView, dans cet exemple, il est nommé gListView1LostFocusItem) et affectez-lui la valeur -1
    • int gListView1LostFocusItem = -1;
  3. Implémentez les gestionnaires d'événements comme suit:

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

Remarque: Cette solution entraînera un certain scintillement. Un correctif consiste à sous-classer le contrôle ListView afin que vous puissiez modifier la propriété protégée DoubleBuffered sur true.

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

Sur 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

Vous ne pouvez pas mettre l'accent sur le contrôle listview dans cette situation. txtBarcode_Leave la méthode empêchera cela. Mais si vous souhaitez pouvoir sélectionner des éléments listview en cliquant dessus, ajoutez simplement le code ci-dessous au gestionnaire d'événements MouseClick de listview:

    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