Il n'y a pas de ListBox.SelectionMode = "None", existe-t-il un autre moyen de désactiver la sélection dans une listbox?


189

Comment désactiver la sélection dans une ListBox?


1
Pourriez-vous donner un exemple où il est valide d'avoir une ListBox que vous ne pouvez pas sélectionner? Puisque le comportement principal est de sélectionner des éléments. J'aurais probablement choisi une autre façon de l'afficher. (Ce n'est pas moi qui essaie d'être un critique, mais plutôt un réel intérêt pour l'endroit où cela pourrait se produire)
Marthin

3
@Martin: par exemple si vous vouliez faire glisser du contenu à partir d'un élément de liste - dans ce cas, vous n'êtes probablement pas intéressé par la sélection de cet élément. AUSSI: lors du glissement d'un élément: l'élément sélectionné de la zone de liste change pendant que vous faites glisser dans la zone de liste - voir cet article stackoverflow.com/questions/7589142
...

1
Je crois que la raison pour laquelle Shimmy veut utiliser ListBox est que le demandeur peut rendre la listbox sélectionnable à un moment donné. La question me tient également à cœur. Disons que vous construisez un jeu de cartes à jouer. Vous pouvez sélectionner une carte parmi vos cartes, parfois, vous pouvez en sélectionner plusieurs et à d'autres moments, vous ne pouvez en sélectionner aucune.
Gqqnbig

1
De plus, vous avez parfois 10 cartes et seulement 4 d'entre elles sont sélectionnables. Parmi les 4, vous pouvez en sélectionner jusqu'à 3.
Gqqnbig

1
@Marthin: lorsque vous avez un GridView dans une ListBox. Les en-têtes Gridview fournissent de nombreuses fonctionnalités qui ne sont pas disponibles ailleurs. Et vous avez des contrôles d'édition dans les cellules de la grille.
Robin Davies le

Réponses:


264

Approche 1 - ItemsControl

À moins que vous n'ayez besoin d'autres aspects de ListBox, vous pouvez utiliser à la ItemsControlplace. Il place les éléments dans le ItemsPanelet n'a pas le concept de sélection.

<ItemsControl ItemsSource="{Binding MyItems}" />

Par défaut, ItemsControlne prend pas en charge la virtualisation de ses éléments enfants. Si vous avez beaucoup d'éléments, la virtualisation peut réduire l'utilisation de la mémoire et améliorer les performances, auquel cas vous pouvez utiliser l'approche 2 et styliser le ListBox, ou ajouter la virtualisation à votreItemsControl .

Approche 2 - Stylisme ListBox

Sinon, stylisez simplement le ListBox de sorte que la sélection ne soit pas visible.

<ListBox.Resources>
  <Style TargetType="ListBoxItem">
    <Style.Resources>
      <!-- SelectedItem with focus -->
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                       Color="Transparent" />
      <!-- SelectedItem without focus -->
      <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}"
                       Color="Transparent" />
      <!-- SelectedItem text foreground -->
      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}"
                       Color="Black" />
    </Style.Resources>
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
  </Style>
</ListBox.Resources>

24
non, cela ne changera que l'effet visuel, pas le comportement de sélection réel
Thomas Levesque

8
Ma première suggestion a été d'utiliser ItemsControl. Vous avez manqué ça? :)
Drew Noakes

5
En relisant à nouveau ces commentaires, je tiens à souligner que le commentaire de @Thomas Levesque n'est vrai que pour la deuxième approche que je montre. L'utilisation de plain ItemsControlsupprimera complètement tout concept de sélection.
Drew Noakes le

1
La solution ItemsControl supprime le support de défilement des boîtes (barre de défilement et molette de la souris).
MuiBienCarlota

1
+1 pour l'approche 1 - ItemsControl. Si nous avons une énorme page que nous devons faire défiler, si l'utilisateur passe la souris sur une ListBox, cela désactive effectivement MouseWheel lorsque la listbox saisit les événements MouseWheel. Cela signifie que l'utilisateur est frustré que la molette de souris utilisée pour faire défiler la page entière cesse de fonctionner au hasard, selon que la souris se trouve ou non sur une zone de liste.
Contango

160

J'ai trouvé une solution très simple et directe qui fonctionne pour moi, j'espère que cela le ferait aussi pour vous

<ListBox ItemsSource="{Items}">
    <ListBox.ItemContainerStyle>
       <Style TargetType="{x:Type ListBoxItem}">
           <Setter Property="Focusable" Value="False"/>
       </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

Je pense qu'il l'a assez bien traversé
Sid

3
C'est parfait. il empêche l'élément sélectionné et d'autres contrôles comme les boutons fonctionnent toujours. exactement ce que je cherchais
Franck

1
+1 pour cette approche. Si nous avons une énorme page que nous devons faire défiler, si l'utilisateur passe la souris sur une ListBox, cela désactive effectivement MouseWheel lorsque la listbox saisit les événements MouseWheel. Cela signifie que l'utilisateur est frustré que la molette de souris utilisée pour faire défiler la page entière cesse de fonctionner au hasard, selon que la souris se trouve ou non sur une zone de liste.
Contango

Excellent. Une approche similaire a également fonctionné pour moi lorsque j'avais besoin de boutons sur des éléments pour ne pas provoquer de sélection d'éléments - mais uniquement si une autre zone de l'élément était cliquée. Réglez simplement les boutons Focusable = "False"!
Jony Adamit

2
Ajoutez cette propriété supplémentaire pour supprimer également la surbrillance au <Setter Property="IsHitTestVisible" Value="False" />
survol de

25

Vous pouvez passer à l'utilisation d'un ItemsControlfichier ListBox. An ItemsControln'a pas de concept de sélection, il n'y a donc rien à désactiver.


2
Charmant. Je n'ai jamais su que vous pouviez déclarer directement ItemsControl, je pensais que c'était virtuel (MustOverride), merci !!!
Shimmy Weitzhandler le

Mais ItemsControl afficherait-il toujours mes éléments sur une seule ligne?
Chry Cheng

@Chry oui, et en plus, vous pouvez toujours définir manuellement le fichier ItemTemplate.
Shimmy Weitzhandler

2
Cela finit par perdre trop de fonctionnalités - par exemple, le défilement.
Jeff

@Jeff vous pouvez envelopper le ItemsControl dans un ScrollViewer pour obtenir le défilement.
Wilka

12

Une autre option à considérer est la désactivation de ListBoxItems. Cela peut être fait en définissant ItemContainerStyle comme indiqué dans l'extrait de code suivant.

<ListBox ItemsSource="{Binding YourCollection}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsEnabled" Value="False" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

Si vous ne voulez pas que le texte soit gris, vous pouvez spécifier la couleur désactivée en ajoutant un pinceau aux ressources du style avec la clé suivante: {x: Static SystemColors.GrayTextBrushKey}. L'autre solution serait de remplacer le modèle de contrôle ListBoxItem.


Simple et fonctionnel, merci! Et cela s'applique également à WP 8.1 Runtime.
Malutek le

8

Cela fonctionnera également, si j'ai besoin d'utiliser listbox au lieu de itemscontrol, mais que j'affiche simplement les éléments qui ne devraient pas être sélectionnables, j'utilise:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="IsHitTestVisible" Value="False" />
    </Style>
</ListBox.ItemContainerStyle>

3

Assez bonnes réponses ici, mais je cherchais quelque chose de légèrement différent: je veux une sélection, mais je ne veux pas qu'elle soit montrée (ou montrée dans un autre sujet).

Les solutions ci-dessus n'ont pas fonctionné pour moi (complètement), j'ai donc fait autre chose: j'ai utilisé un nouveau style pour ma listbox, qui redéfinit complètement les modèles:

<Style x:Key="PlainListBoxStyle" TargetType="ListBox">
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <ContentPresenter />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <ItemsPresenter/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

En commençant par cela, vous pouvez facilement ajouter votre propre mise en surbrillance de sélection, ou la laisser comme ça si vous n'en voulez pas du tout.


2

Bien que la réponse de @Drew Noakes soit une solution rapide dans la plupart des cas, il existe un petit défaut lié au réglage des brosses x: Static.

Lorsque vous définissez les pinceaux x: Static comme suggéré, tous les contrôles enfants de l'élément de zone de liste héritent de ce style.

Cela signifie que, bien que cela fonctionnera pour désactiver la mise en évidence de l'élément de zone de liste, cela peut entraîner des effets indésirables pour les contrôles enfants.

Par exemple, si vous aviez un ComboBox dans votre ListBoxItem, cela désactiverait la souris sur la mise en évidence dans le ComboBox.

Au lieu de cela, envisagez de définir les VisualStates pour les événements Selected, Unselected et MouseOver comme indiqué dans la solution mentionnée dans ce thread stackoverflow: Supprimez la surbrillance du contrôle de ListBoxItem mais pas les contrôles enfants .

-Frinny


2

Je propose encore une autre solution. Re-template simplement ListBoxItempour n'être rien de plus qu'un ContentPresenter, comme ça ...

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <ContentPresenter />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Mes raisons de cette approche sont les suivantes:

  1. Dans mon cas, je ne souhaite pas désactiver l'interaction de l'utilisateur avec le contenu de mon ListBoxItemsafin que la solution à définir IsEnabledne fonctionne pas pour moi.

  2. L'autre solution qui tente de remodeler le style ListBoxItemen remplaçant les propriétés liées à la couleur ne fonctionne que pour les instances où vous êtes sûr que le modèle utilise ces propriétés. C'est bien pour les styles par défaut, mais rompt avec les styles personnalisés.

  3. Les solutions qui utilisent un ItemsControlcassent trop d'autres choses car elles ItemsControlont un aspect complètement différent d'un standard ListBoxet ne prennent pas en charge la virtualisation, ce qui signifie que vous devez de ItemsPaneltoute façon remodeler.

Ce qui précède ne modifie pas l'apparence par défaut du ListBox, ne désactive pas les éléments dans les modèles de données pour le ListBox, prend en charge la virtualisation par défaut et fonctionne indépendamment de tout style utilisé ou non dans votre application. C'est le principe KISS.


1

Remarque: cette solution ne désactive pas la sélection par navigation au clavier ou clic droit (c.-à-d. Touches fléchées suivies d'une touche d'espace)

Toutes les réponses précédentes suppriment complètement la sélection de capacité (pas de changement à l'exécution) ou suppriment simplement l'effet visuel, mais pas la sélection.

Mais que faire si vous voulez pouvoir sélectionner et afficher la sélection par code, mais pas par entrée utilisateur? Peut-être voulez-vous "figer" la sélection de l'utilisateur sans désactiver toute la Listbox?

La solution consiste à encapsuler l'ensemble ItemsContentTemplate dans un Button qui n'a pas de chrome visuel. La taille du bouton doit être égale à la taille de l'élément, il est donc complètement couvert. Utilisez maintenant la propriété IsEnabled du bouton:

Activez le bouton pour "figer" l'état de sélection de l'élément. Cela fonctionne car le bouton activé mange tous les événements de la souris avant qu'ils ne remontent au ListboxItem-Eventhandler. Votre ItemsDataTemplate recevra toujours MouseEvents car il fait partie du contenu des boutons.

Désactivez le bouton pour activer la modification de la sélection en cliquant sur.

<Style x:Key="LedCT" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Button IsEnabled="{Binding IsSelectable, Converter={StaticResource BoolOppositeConverter}}" Template="{DynamicResource InvisibleButton}">
                        <ContentPresenter />
                </Button>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<ControlTemplate x:Key="InvisibleButton" TargetType="{x:Type Button}">
    <ContentPresenter/>
</ControlTemplate>

Dartrax


1

Peut-être avez-vous besoin uniquement de la fonctionnalité ItemsControl? Il ne permet pas la sélection:

<ItemsControl ItemsSource="{Binding Prop1}" ItemTemplate="{StaticResource DataItemsTemplate}" />

3
@Shimmy: Il est courant que les réponses triviales soient similaires. Il n'y a ici aucune duplication digne de n'importe quel drapeau. Si vous avez d'autres questions à ce sujet, veuillez les poser sur Meta Stack Overflow .

0

Vous pouvez placer un bloc de texte au-dessus de votre zone de liste, cela ne changera pas l'apparence de votre application et ne permettra pas de sélectionner un élément.


1
Cependant, vous devrez toujours désactiver la navigation par onglets.
Amanduh

0

Une solution simple qui fonctionne sur Windows Phone, par exemple, consiste à définir l'élément sélectionné sur null:

    <ListBox SelectionChanged="ListBox_SelectionChanged">

Et dans le code derrière:

    private void ListBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        (sender as ListBox).SelectedItem = null;
    }

0

J'ai trouvé un moyen parfait.
Définissez ListBox IsHitTestVisible sur false afin que l'utilisateur ne puisse pas survoler la souris, faire défiler vers le bas ou faire défiler vers le haut.
Capture PreviewGotKeyboardFocus e.Handled = true afin que l'utilisateur puisse sélectionner l'élément à l'aide du clavier Tab, Flèche vers le haut, Flèche vers le bas.

De cette façon, l'avantage:

  1. Les éléments ListBox Foreground ne deviendront pas gris.
  2. L'arrière-plan de ListBox peut être défini sur Transparent

xmal

<ListBox Name="StudentsListBox" ItemsSource="{Binding Students}" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0" Background="Transparent" IsHitTestVisible="False" PreviewGotKeyboardFocus="StudentsListBox_PreviewGotKeyboardFocus">

    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Padding" Value="0"/>
            <Setter Property="Margin" Value="0"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="Bd">
                            <ContentPresenter/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Selector.IsSelectionActive" Value="False" />
                                    <Condition Property="IsSelected" Value="True" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Bd" Property="Background" Value="Yellow" />
                                <Setter TargetName="Bd" Property="BorderBrush" Value="Transparent" />
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid Margin="0,0,0,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Name="GradeBlock" Text="{Binding Grade}" FontSize="12" Margin="0,0,5,0"/>
                <TextBlock Grid.Column="1" Name="NameTextBlock" Text="{Binding Name}" FontSize="12" TextWrapping="Wrap"/>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ListBox>

code

private void StudentsListBox_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    e.Handled = true;
}

-1

Pour moi, la meilleure solution est:

        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Focusable" Value="True"/>
                <Setter Property="IsHitTestVisible" Value="False" />
            </Style>
        </ListBox.ItemContainerStyle>


-3

Pour désactiver une ou plusieurs options dans votre zone de liste / liste déroulante, vous pouvez ajouter l'attribut «désactivé» comme indiqué ci-dessous. Cela empêche l'utilisateur de sélectionner cette option, et il obtient une superposition grise.

ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);

Cette fois, vous avez plané et avez répondu à une question WPF avec une solution ASP.NET.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.