Shimmy Weitzhandler
Shimmy Weitzhandler

Reputation: 104801

There is no ListBox.SelectionMode="None", is there another way to disable selection in a listbox?

How do I disable selection in a ListBox?

Upvotes: 212

Views: 109081

Answers (17)

bytecode77
bytecode77

Reputation: 14860

The solution should be simple and straight forward.

This one has several advantages:

  • Keyboard navigation is also disabled. This is not the case with IsFocusable, IsHitTestVisible, etc.
  • No visual cues of "disabled" elements: Only the ListBoxItem is disabled, however the TextBlock.Foreground property sets the correct color.

Result: An item cannot be selected by keyboard or by mouse and the color is not "gray" because we don't disable an entire control.

<ListBox>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsEnabled" Value="False" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" Foreground="Black" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Upvotes: 0

Mark A. Donohoe
Mark A. Donohoe

Reputation: 30458

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.

Upvotes: 3

user2490200
user2490200

Reputation: 11

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

Upvotes: 1

Michal Dobrodenka
Michal Dobrodenka

Reputation: 1144

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>

Upvotes: 0

Asad Durrani
Asad Durrani

Reputation: 2237

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>

Upvotes: 177

Drew Noakes
Drew Noakes

Reputation: 311275

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>

Upvotes: 295

Andreas Kahler
Andreas Kahler

Reputation: 2353

Quite good answers here, but I was looking for something slightly different: I want selection, but just do not want it to be shown (or shown in a different matter).

The solutions above did not work for me (completely), so I did something else: I used a new style for my listbox, which completely redefines the templates:

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

Starting with that, you can easily add you own selection highlighting, or leave it like that if you dont want any at all.

Upvotes: 5

Andrej Kikelj
Andrej Kikelj

Reputation: 800

This will also work, if I have the need to use listbox instead of itemscontrol, but am just displaying the items which shouldn't be selectable, I use:

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

Upvotes: 13

Jason94
Jason94

Reputation: 13620

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

Upvotes: 1

Hallgeir Engen
Hallgeir Engen

Reputation: 971

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

Upvotes: -3

Paras
Paras

Reputation: 37

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.

Upvotes: 0

Frinavale
Frinavale

Reputation: 3950

While @Drew Noakes's answer is a quick solution for most cases there is a bit of a flaw that comes with setting the x:Static brushes.

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

Upvotes: 2

Sasha
Sasha

Reputation: 8860

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

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

Upvotes: 2

Caleb Vear
Caleb Vear

Reputation: 2647

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.

Upvotes: 13

dartrax
dartrax

Reputation: 11

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

Upvotes: 0

Wilka
Wilka

Reputation: 29613

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.

Upvotes: 32

Viktor Jevdokimov
Viktor Jevdokimov

Reputation: 1056

IsEnabled = false

Upvotes: -2

Related Questions