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 falselistview.Focus()
après avoir défini la couleurlistviewitem.Focused
Défini sur truelistview.Invalidate
listview.Update()
listview.Refresh()
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:
Je sélectionne un élément en faisant:
listView1.Items[index].Selected = true;
le focus est toujours dans la zone de texte.
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.
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
:
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:
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();
}
}
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.
Voici une solution pour un ListView qui n'autorise pas plusieurs sélections et n'a pas d'images (par exemple des cases à cocher).
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;
}
}
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;
}
}
}
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
}
}
}