No hay ListBox.SelectionMode = "None", ¿hay otra forma de deshabilitar la selección en un cuadro de lista?

189

¿Cómo desactivo la selección en un ListBox?

Shimmy Weitzhandler
fuente
1
¿Podría proporcionar un ejemplo en el que sea válido tener un ListBox que no puede seleccionar? Dado que el comportamiento principal es seleccionar elementos. Probablemente elegiría otra forma de mostrarlo. (Este no soy yo tratando de ser un crítico, sino más bien un interés genuino sobre dónde podría ocurrir esto)
Marthin
3
@ Martin: por ejemplo, si desea arrastrar contenido desde un elemento de cuadro de lista; en este caso, probablemente no esté interesado en seleccionar ese elemento. TAMBIÉN: al arrastrar un elemento: el elemento seleccionado del cuadro de lista cambia mientras lo arrastra dentro del cuadro de lista; consulte esta publicación stackoverflow.com/questions/7589142/…
Danield
1
Creo que la razón por la que Shimmy quiere usar ListBox es que el autor de la pregunta puede hacer que el cuadro de lista sea seleccionable en algún momento. La pregunta también es valiosa para mí. Digamos que estás construyendo un juego de naipes. Puede seleccionar una tarjeta de sus tarjetas, a veces, puede seleccionar varias y en otras ocasiones, no puede seleccionar ninguna.
Gqqnbig
1
Además, a veces tienes 10 cartas y solo 4 de ellas son seleccionables. Entre los 4, puede seleccionar hasta 3.
Gqqnbig
1
@Marthin: cuando tienes un GridView en un ListBox. Los encabezados de Gridview proporcionan mucha funcionalidad que no está disponible en otros lugares. Y tiene controles de edición en las celdas de la vista de cuadrícula.
Robin Davies

Respuestas:

264

Enfoque 1 - ItemsControl

A menos que necesite otros aspectos de la ListBox, podría utilizar ItemsControlen su lugar. Coloca elementos en ItemsPanely no tiene el concepto de selección.

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

Por defecto, ItemsControlno admite la virtualización de sus elementos secundarios. Si tiene muchos elementos, la virtualización puede reducir el uso de memoria y mejorar el rendimiento, en cuyo caso podría usar el enfoque 2 y darle estilo ListBox, o agregar virtualización a suItemsControl .

Enfoque 2 - Estilo ListBox

Alternativamente, simplemente diseñe el ListBox de modo que la selección no sea 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
fuente
24
no, sólo cambiará el efecto visual, no el comportamiento de la selección real
Thomas Levesque
8
Mi primera sugerencia fue usar ItemsControl. ¿Te perdiste eso? :)
Drew Noakes
55
Volviendo a leer estos comentarios nuevamente, quiero señalar que el comentario de @Thomas Levesque solo es cierto sobre el segundo enfoque que muestro. El uso de plain ItemsControleliminará por completo cualquier concepto de selección.
Drew Noakes
1
La solución ItemsControl elimina el soporte de desplazamiento de la caja (barra de desplazamiento y rueda del mouse).
MuiBienCarlota
1
+1 para el Enfoque 1 - ItemsControl. Si tenemos una página enorme que tenemos que desplazar, si el usuario pasa el mouse sobre un ListBox, deshabilita efectivamente la MouseWheel cuando el cuadro de lista toma los eventos de MouseWheel. Esto significa que el usuario se siente frustrado de que la rueda del mouse utilizada para desplazarse por toda la página deje de funcionar aleatoriamente, dependiendo de si el mouse está sobre un cuadro de lista o no.
Contango
159

Encontré una solución muy simple y directa que funciona para mí, espero que también sirva para ti

<ListBox ItemsSource="{Items}">
    <ListBox.ItemContainerStyle>
       <Style TargetType="{x:Type ListBoxItem}">
           <Setter Property="Focusable" Value="False"/>
       </Style>
    </ListBox.ItemContainerStyle>
</ListBox>
Asad Durrani
fuente
Creo que lo pasó bastante bien aquí: asaddurrani.wordpress.com/tag/wpf-listbox-disable-selection
Sid
3
Esto es perfecto. evita que el elemento seleccionado y otros controles como botones sigan funcionando. exactamente lo que estaba buscando
Franck
1
+1 para este enfoque. Si tenemos una página enorme que tenemos que desplazar, si el usuario pasa el mouse sobre un ListBox, deshabilita efectivamente la MouseWheel cuando el cuadro de lista toma los eventos de MouseWheel. Esto significa que el usuario se siente frustrado de que la rueda del mouse utilizada para desplazarse por toda la página deje de funcionar aleatoriamente, dependiendo de si el mouse está sobre un cuadro de lista o no.
Contango
Excelente. Un enfoque similar también funcionó para mí cuando necesitaba botones en los elementos para no causar la selección del elemento, pero solo si se hizo clic en otra área del elemento. ¡Simplemente configure los botones Focusable = "False"!
Jony Adamit
1
Agregue esta propiedad adicional para eliminar también el resaltado del mouseover: <Setter Property="IsHitTestVisible" Value="False" />
DonBoitnott
25

Puede cambiar a usar un en ItemsControllugar de un ListBox. Un ItemsControlno tiene un concepto de selección, por lo que no hay nada que apagar.

Wilka
fuente
2
Encantador. Nunca supe que podía declarar directamente ItemsControl, pensé que era virtual (debe anular), ¡gracias!
Shimmy Weitzhandler
¿Pero ItemsControl todavía representaría mis artículos en una línea?
Chry Cheng el
@Chry sí lo haría, y además, siempre puedes configurarlo manualmente ItemTemplate.
Shimmy Weitzhandler
2
Esto termina perdiendo demasiada funcionalidad, por ejemplo, desplazamiento.
Jeff
@Jeff puedes envolver el ItemsControl en un ScrollViewer para ganar desplazamiento.
Wilka
12

Otra opción que vale la pena considerar es deshabilitar ListBoxItems. Esto se puede hacer configurando el ItemContainerStyle como se muestra en el siguiente fragmento.

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

Si no desea que el texto sea gris, puede especificar el color deshabilitado agregando un pincel a los recursos del estilo con la siguiente clave: {x: Static SystemColors.GrayTextBrushKey}. La otra solución sería anular la plantilla de control ListBoxItem.

Caleb Vear
fuente
Simple y funcional, gracias! Y también es aplicable en WP 8.1 Runtime.
Malutek
8

Esto también funcionará, si tengo la necesidad de usar listbox en lugar de itemscontrol, pero solo estoy mostrando los elementos que no deberían ser seleccionables, uso:

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

Muy buenas respuestas aquí, pero estaba buscando algo ligeramente diferente: quiero selección, pero simplemente no quiero que se muestre (o se muestre en un asunto diferente).

Las soluciones anteriores no me funcionaron (completamente), así que hice algo más: utilicé un nuevo estilo para mi cuadro de lista, que redefine completamente las plantillas:

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

Comenzando con eso, puede agregar fácilmente su propio resaltado de selección, o dejarlo así si no desea ninguno.

Andreas Kahler
fuente
2

Si bien la respuesta de @Drew Noakes es una solución rápida para la mayoría de los casos, hay un pequeño defecto que viene con la configuración de x: cepillos estáticos.

Cuando configura los pinceles x: Static como se sugiere, todos los controles secundarios dentro del elemento del cuadro de lista heredarán este estilo.

Eso significa que, si bien esto funcionará para deshabilitar el resaltado del elemento del cuadro de lista, puede provocar efectos no deseados para los controles secundarios.

Por ejemplo, si tuviera un ComboBox dentro de su ListBoxItem, deshabilitaría el mouse sobre el resaltado dentro del ComboBox.

En su lugar, considere configurar VisualStates para los eventos Selected, Unselected y MouseOver como se cubre en la solución mencionada en este hilo de stackoverflow: Eliminar Control Highlight From ListBoxItem pero no los controles secundarios .

-Frinny

Frinavale
fuente
2

Propongo otra solución más. Simplemente cambie la plantilla ListBoxItempara que no sea más que un ContentPresenter, así ...

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

Mis razones para este enfoque son las siguientes:

  1. En mi caso, no quiero deshabilitar la interacción del usuario con el contenido de mi, ListBoxItemspor lo que la solución para configurar IsEnabledno funcionará para mí.

  2. La otra solución que intenta cambiar el estilo ListBoxItemanulando las propiedades relacionadas con el color solo funciona para aquellas instancias en las que está seguro de que la plantilla usa esas propiedades. Eso está bien para los estilos predeterminados, pero rompe con los estilos personalizados.

  3. Las soluciones que usan un sistema ItemsControlrompen demasiadas cosas, ya que ItemsControltienen un aspecto completamente diferente al estándar ListBoxy no admiten la virtualización, lo que significa que de ItemsPaneltodos modos tiene que volver a crear la plantilla .

Lo anterior no cambia el aspecto predeterminado del ListBox, no deshabilita los elementos en las plantillas de datos para ListBox, admite la virtualización de forma predeterminada y funciona independientemente de los estilos que pueden o no estar en uso en su aplicación. Es el principio KISS.

Mark A. Donohoe
fuente
1

Nota: Esta solución no deshabilita la selección mediante la navegación con el teclado o haciendo clic derecho (es decir, las teclas de flecha seguidas de la barra espaciadora)

Todas las respuestas anteriores eliminan la selección de habilidad por completo (sin cambiar en tiempo de ejecución) o simplemente eliminan el efecto visual, pero no la selección.

Pero, ¿qué sucede si desea poder seleccionar y mostrar la selección por código, pero no por entrada del usuario? ¿Es posible que desee "congelar" la selección del usuario sin deshabilitar todo el cuadro de lista?

La solución es envolver todo el ItemContentTemplate en un botón que no tiene cromo visual. El tamaño del botón debe ser igual al tamaño del artículo, por lo que está completamente cubierto. Ahora use la propiedad IsEnabled-button del botón:

Habilite el botón para "congelar" el estado de selección del elemento. Esto funciona porque el botón habilitado come todos los eventos del mouse antes de que aparezcan en el ListboxItem-Eventhandler. Your ItemsDataTemplate seguirá recibiendo MouseEvents porque es parte del contenido de los botones.

Deshabilite el botón para permitir cambiar la selección haciendo clic.

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

¿Tal vez solo necesita la funcionalidad de ItemsControl? No permite la selección:

<ItemsControl ItemsSource="{Binding Prop1}" ItemTemplate="{StaticResource DataItemsTemplate}" />
Sasha
fuente
3
@Shimmy: Es común que las respuestas triviales sean similares. Aquí no hay duplicación digna de ninguna bandera. Si tiene más preguntas al respecto, consulte Meta Stack Overflow .
0

Puede colocar un bloque de texto sobre su cuadro de lista, no cambiará el aspecto de su aplicación y tampoco permitirá seleccionar ningún elemento.

Paras
fuente
1
Sin embargo, aún deberá deshabilitar la navegación con pestañas.
Amanduh
0

Una solución simple que funciona en Windows Phone, por ejemplo, es la selección que establece el elemento seleccionado en nulo:

    <ListBox SelectionChanged="ListBox_SelectionChanged">

Y en el código detrás:

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

Encontré una manera perfecta.
Establezca ListBox IsHitTestVisible en false para que el usuario no pueda desplazar el mouse o desplazarse hacia abajo o hacia arriba.
Capture PreviewGotKeyboardFocus e.Handled = true para que el usuario pueda seleccionar el elemento con el teclado Tab, Flecha arriba, Flecha abajo.

De esta manera ventaja:

  1. Los elementos de ListBox en primer plano no se volverán grises.
  2. ListBox Background puede establecerse en 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>

código

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

Para mí la mejor solución es:

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

IsEnabled = falso

Viktor Jevdokimov
fuente
2
Lo hace todo gris, esto no es lo que estoy buscando
Shimmy Weitzhandler
1
Pero es una respuesta directa a la pregunta directa :)
Viktor Jevdokimov
1
Una respuesta directa es esta stackoverflow.com/questions/1398559/1398650#1398650 , pero gracias de todos modos
Shimmy Weitzhandler
Muy útil para mí, ¡quería gris y deshabilitado!
Martin Robins
-3

Para deshabilitar una o más opciones en su cuadro de lista / menú desplegable, puede agregar el atributo "deshabilitado" como se muestra a continuación. Esto evita que el usuario seleccione esta opción y obtiene una superposición gris.

ListItem item = new ListItem(yourvalue, yourkey);
item.Attributes.Add("disabled","disabled");
lb1.Items.Add(item);
Hallgeir Engen
fuente
Esa vez te drogaste y respondiste una pregunta de WPF con una solución ASP.NET.