Não há ListBox.SelectionMode = "None"; existe outra maneira de desativar a seleção em uma caixa de listagem?

188

How do I disable selection in a ListBox?

Shimmy Weitzhandler
fonte
1
Could you provide an example where it is valid to have a ListBox that you can not select from? Since the main behavior is to select items. I would probably chose another way to display it.(This is not me trying to be a critic but rather an genuine interest in where this might occur)
Marthin
3
@Martin: for instance if you wanted to drag content from a listboxitem - in this case you're probably not interested in selecting that item. ALSO: when dragging an item: selected item of listbox changes while you drag within the listbox - see this post stackoverflow.com/questions/7589142/…
Danield
1
I believe the reason that Shimmy wants to use ListBox is that the asker can make the listbox selectable sometime. The question is also valuable to me. Say you are building a playing card game. You can select one card from your cards, sometimes, you can select multiple and at other times, you cannot select any.
Gqqnbig
1
Plus, sometimes you have 10 cards and only 4 of them are selectable. Among the 4, you can select up to 3.
Gqqnbig
1
@Marthin: When you have a GridView in a ListBox. Gridview headers provide a lot of functionality that's not available elsewhere. And you have edit controls in the cells of the gridview.
Robin Davies

Respostas:

264

Approach 1 - ItemsControl

Unless you need other aspects of the ListBox, you could use ItemsControl instead. It places items in the ItemsPanel and doesn't have the concept of selection.

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

By default, ItemsControl doesn't support virtualization of its child elements. If you have a lot of items, virtualization can reduce memory usage and improve performance, in which case you could use approach 2 and style the ListBox, or add virtualisation to your ItemsControl.

Approach 2 - Styling ListBox

Alternatively, just style the ListBox such that the selection is not 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>
Drew Noakes
fonte
24
no, it will only change the visual effect, not the actual selection behavior
Thomas Levesque
8
My first suggestion was to use ItemsControl. Did you miss that? :)
Drew Noakes
5
Re-reading these comments again I want to point out that @Thomas Levesque's comment is only true of the second approach I show. Using plain ItemsControl will completely remove any concept of selection.
Drew Noakes
1
ItemsControl solution remove out the box scrolling support (scrollbar and mousewheel).
MuiBienCarlota
1
+1 for Approach 1 - ItemsControl. If we have a huge page which we have to scroll, if the user mouses over a ListBox, it effectively disables the MouseWheel as the listbox grabs the MouseWheel events. This means that the user gets frustrated that the mousewheel used to scroll the entire page will randomly stop working, depending on whether the mouse is over a listbox or not.
Contango
159

I found a very simple and straight forward solution working for me, I hope it would do for you as well

<ListBox ItemsSource="{Items}">
    <ListBox.ItemContainerStyle>
       <Style TargetType="{x:Type ListBoxItem}">
           <Setter Property="Focusable" Value="False"/>
       </Style>
    </ListBox.ItemContainerStyle>
</ListBox>
Asad Durrani
fonte
I think he went through it quite well here: asaddurrani.wordpress.com/tag/wpf-listbox-disable-selection
Sid
3
This is perfect. it prevent selected item and other controls like buttons still works. exactly what i was looking for
Franck
1
+1 for this approach. If we have a huge page which we have to scroll, if the user mouses over a ListBox, it effectively disables the MouseWheel as the listbox grabs the MouseWheel events. This means that the user gets frustrated that the mousewheel used to scroll the entire page will randomly stop working, depending on whether the mouse is over a listbox or not.
Contango
Excellent. Similar approach also worked for me when I needed buttons on items not to cause item selection - but only if other area of the item was clicked. Simply set the buttons Focusable = "False"!
Jony Adamit
1
Add this additional property to remove the mouseover highlighting as well: <Setter Property="IsHitTestVisible" Value="False" />
DonBoitnott
25

You could switch to using an ItemsControl instead of a ListBox. An ItemsControl has no concept of selection, so there's nothing to turn off.

Wilka
fonte
2
Charming. I never knew you could directly declare ItemsControl, I thought it's virtual (MustOverride), thanks!!!
Shimmy Weitzhandler
But would ItemsControl still render my items in one line?
Chry Cheng
@Chry yes it would, and in addition, you can always manually set the ItemTemplate.
Shimmy Weitzhandler
2
Isso acaba perdendo muita funcionalidade - por exemplo, rolagem.
Jeff
@Jeff you can wrap the ItemsControl in a ScrollViewer to gain scrolling.
Wilka
12

Another option worth considering is disabling the ListBoxItems. This can be done by setting the ItemContainerStyle as shown in the following snippet.

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

If you don't want the text to be grey you can specify the disabled color by adding a brush to the style's resources with the following key: {x:Static SystemColors.GrayTextBrushKey}. The other solution would be to override the ListBoxItem control template.

Caleb Vear
fonte
Simples e funcional, obrigado! E é aplicável no WP 8.1 Runtime também.
Malutek
8

Isso também funcionará, se eu precisar usar a caixa de listagem em vez do itemscontrol, mas apenas exibir os itens que não devem ser selecionáveis, eu uso:

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

Respostas bastante boas aqui, mas eu estava procurando por algo um pouco diferente: quero a seleção, mas simplesmente não quero que ela seja mostrada (ou mostrada em um assunto diferente).

As soluções acima não funcionaram para mim (completamente), então fiz outra coisa: usei um novo estilo para minha caixa de listagem, que redefine completamente os modelos:

<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>

Começando com isso, você pode facilmente adicionar seu próprio destaque de seleção ou deixá-lo assim, se não quiser.

Andreas Kahler
fonte
2

Embora a resposta de @Drew Noakes seja uma solução rápida para a maioria dos casos, existe um pouco de falha na configuração dos pincéis x: Static.

When you set the x:Static brushes as suggested, all of the children controls within the list box item will inherit this style.

That means that, while this will work for disabling the highlighting of the list box item, it may result in undesired effects for the child controls.

For example, if you had a ComboBox within your ListBoxItem, it would disable the mouse over highlighting within the ComboBox.

Instead, consider setting the VisualStates for the Selected, Unselected, and MouseOver events as covered in the solution mentioned in this stackoverflow thread: Remove Control Highlight From ListBoxItem but not children controls.

-Frinny

Frinavale
fonte
2

I propose yet another solution. Simply re-template ListBoxItem to be nothing more than a ContentPresenter, like so...

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

My reasons for this approach are as follows:

  1. In my case, I don't want to disable user interaction with the contents of my ListBoxItems so the solution to set IsEnabled won't work for me.

  2. The other solution that attempts to re-style the ListBoxItem by overriding the color-related properties only works for those instances where you're sure the template uses those properties. That's fine for default styles, but breaks with custom styles.

  3. The solutions that use an ItemsControl breaks too many other things as the ItemsControl has a completely different look than a standard ListBox and doesn't support virtualization, meaning you have to re-template the ItemsPanel anyway.

The above doesn't change the default look of the ListBox, doesn't disable items in the data templates for the ListBox, supports virtualization by default, and works independently of whatever styles may or may not be in use in your app. It's the KISS principle.

Mark A. Donohoe
fonte
1

Note: This solution does not disable selection by keyboard navigation or right clicking (ie. arrow keys followed by space key)

All previous answers either remove the ability select completly (no switching in runtime) or simply remove the visual effect, but not the selection.

But what if you want to be able to select and show selection by code, but not by user input? May be you want to "freeze" the user's selection while not disabling the whole Listbox?

The solution is to wrap the whole ItemsContentTemplate into a Button that has no visual chrome. The size of the button must be equal to the size of the Item, so it's completely covered. Now use the button's IsEnabled-Property:

Enable the button to "freeze" the item's Selection-state. This works because the enabled button eats all mouse events before they bubble up to the ListboxItem-Eventhandler. Your ItemsDataTemplate will still receive MouseEvents because it's part of the buttons content.

Disable the button to enable changing the selection by clicking.

<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

dartrax
fonte
1

Maybe you need onlly functionality of ItemsControl? It don't allow selection:

<ItemsControl ItemsSource="{Binding Prop1}" ItemTemplate="{StaticResource DataItemsTemplate}" />
Sasha
fonte
3
@Shimmy: It is common for trivial answers to be similar. There is no duplication here worthy of any flag. If you have any more questions about this, please ask on Meta Stack Overflow.
0

You can place a Textblock above your listbox, it will not change the look of your application and also it won't allow to select any item.

Paras
fonte
1
You would still need to disable tab navigation, though.
Amanduh
0

A simple fix that works on Windows Phone for instance is on selection setting selected item to null:

    <ListBox SelectionChanged="ListBox_SelectionChanged">

And in the code behind:

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

I found a perfect way.
Set ListBox IsHitTestVisible to false so that user can't mouse hover or scroll down or scroll up.
Capture PreviewGotKeyboardFocus e.Handled = true so that user can's select item by keyboard Tab, Arrow up, Arrow down.

This way advantage:

  1. ListBox items Foreground will not become Gray.
  2. ListBox Background can set to 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;
}
user2490200
fonte
-1

For me best solution is:

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

IsEnabled = false

Viktor Jevdokimov
fonte
2
It makes it all gray, this is not what I am looking for
Shimmy Weitzhandler
1
But it's a straightforward answer to the straightforward question :)
Viktor Jevdokimov
1
A straight forward answer is this stackoverflow.com/questions/1398559/1398650#1398650, but thanks anyway
Shimmy Weitzhandler
Very help ful for me, I wanted grayed out and disabled!
Martin Robins
-3

To disable one or more options in your listbox/dropdown, you can add the "disabled" attribute as shown below. This prevent the user from selection this option, and it gets a gray overlay.

ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);
Hallgeir Engen
fonte
That time you got high and answered a WPF question with an ASP.NET solution.