wpf xaml listbox selecteditem

wpf - Seleccionar un elemento de cuadro de texto en un cuadro de lista no cambia el elemento seleccionado del cuadro de lista



xaml listbox (14)

Tengo un Listbox wpf que muestra una lista de cuadros de texto. Cuando hago clic en el cuadro de texto, la selección del cuadro de lista no cambia. Tengo que hacer clic al lado de TextBox para seleccionar el elemento de la lista. ¿Hay alguna propiedad que deba establecer para que Textbox reenvíe el evento click al Listbox?


¿Hay alguna propiedad que deba establecer para que Textbox reenvíe el evento click al Listbox?

No es una propiedad simple, pero puede manejar el evento GotFocus en su TextBox , luego usar VisualTreeHelper para encontrar el ListBoxItem y seleccionarlo:

private void TextBox_GotFocus(object sender, RoutedEventArgs e) { TextBox myTextBox = sender as TextBox; DependencyObject parent = VisualTreeHelper.GetParent(myTextBox); while (!(parent is ListBoxItem)) { parent = VisualTreeHelper.GetParent(parent); } ListBoxItem myListBoxItem = parent as ListBoxItem; myListBoxItem.IsSelected = true; }


Asegúrese de utilizar TargetType apropiado: ListViewItem, ListBoxItem o TreeViewItem.

<Style TargetType="ListViewItem"> <Style.Triggers> <Trigger Property="IsKeyboardFocusWithin" Value="true"> <Setter Property="IsSelected" Value="true" /> </Trigger> </Style.Triggers> </Style>


La forma más simple que he podido encontrar para hacer esto es usar el evento PreviewMouseDown y establecer la propiedad IsSelected del padre con plantilla. Dado que los eventos de vista previa brillan, ListBoxItem procesará el evento tan pronto como el usuario haga clic en el cuadro de texto, en el cuadro combinado o en cualquier otro control en el que establezca el evento.

Una cosa buena de esto es que puede usar el mismo evento para todos los tipos de controles ya que todos derivan del elemento Framework. Además, establecer IsSelected (en lugar de establecer SelectedItem) hará que se seleccionen varios elementos cuando configure el SelectionMode del listbox en "Extendido", que podría o no ser lo que está buscando.

es decir:

código c #

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e) { ((sender as FrameworkElement).TemplatedParent as ListBoxItem).IsSelected = true; }

xaml

... <ComboBox PreviewMouseDown="Element_PreviewMouseDown"/> <TextBox PreviewMouseDown="Element_PreviewMouseDown"/> ...


La siguiente es una simplificación de la respuesta de @ Ben sin tener que anular DataTemplate. Incluso se puede aplicar como un estilo estático. Probado con un ListView que contiene un GridView > GridViewColumn > TextBox .

Ejemplo:

<ListView.Resources> <Style TargetType="{x:Type ListViewItem}"> <Style.Triggers> <Trigger Property="IsKeyboardFocusWithin" Value="True"> <Setter Property="IsSelected" Value="True"></Setter> </Trigger> </Style.Triggers> </Style> </ListView.Resources>


Listbox maneja la selección de elementos pero no sabe sobre el foco del cuadro de texto incrustado dentro. Si desea cambiar la selección cada vez que un cuadro de texto obtiene el foco de entrada, entonces necesita cambiar manualmente la selección del cuadro de lista, afaik.


No es muy específico acerca de su situación inicial. Pero asumo que usa DataBinding y una ItemTemplate. Esa es una forma fácil de hacer esto, así como si eres principiante en este tema. Esto debería funcionar:

<ListBox ItemsSource="{Binding someDataCollection}" Name="myListBox"> <ListBox.ItemTemplate> <DataTemplate> <TextBox Text="{Binding datafield}" Tag="{Binding .}" GotFocus="TextBox_GotFocus"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

private void TextBox_GotFocus(object sender, RoutedEventArgs e) { myListBox.SelectedItem = (sender as TextBox).Tag; /* Maybe you need to cast to the type of the objects contained in the collection(bound as ItemSource above) */ }


No estoy del todo seguro de que quieras establecer la selección directamente como se describió en la respuesta anterior porque creo que rompería la multiselección y algunos otros escenarios

. Es posible que desee intentar volver a diseñar un botón como a continuación y ver qué sucede.

<Button ClickMode="Pressed" Focusable="False"> <Button.Template> <ControlTemplate> // change the template to get rid of all the default chrome <Border Background="Transparent"> // Button won''t be clickable without some kind of background set <ContentPresenter /> </Border> </ControlTemplate> </Button.Template> <TextBox />


No tengo suficientes representantes para comentar, así que estoy publicando mi comentario como respuesta. La solución de Grazer anterior no funciona en los casos en los que tiene otro control, como un Button que necesita el elemento SelectedItem . Esto se debe a que, según el Style Trigger , IsKeyboardFocusWithin convierte en falso al hacer clic en ese Button , y el elemento SelectedItem vuelve nulo.


Prueba este código:

foreach (object item in this.listBox1.Items) { if (textbox1.text.equals(item.toString())) { //show error message; break } }


Usamos el siguiente estilo para establecer un PreviewGotKeyboardFocus que maneja todos los eventos del control TextBox y los ComboBoxes y cosas por el estilo:

<ListView.ItemContainerStyle> <Style TargetType="ListViewItem"> <EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/> </Style> </ListView.ItemContainerStyle>

Y luego seleccionamos la fila en el código detrás:

protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e) { ListViewItem item = (ListViewItem) sender; item.IsSelected = true; }


Uso un controlador de clase para establecer este comportamiento. Hacerlo de esta manera arreglará todas las vistas de lista en la aplicación. No sé por qué este no es el comportamiento predeterminado.

En su App.xaml.cs, agregue lo siguiente a OnStartup:

protected override void OnStartup(StartupEventArgs e) { EventManager.RegisterClassHandler(typeof (ListViewItem), ListViewItem.PreviewGotKeyboardFocusEvent, new RoutedEventHandler((x,_) => (x as ListViewItem).IsSelected = true)); }


Utilicé una solución similar a la de Robert, pero sin código detrás (usando el comportamiento adjunto).

Para hacerlo,

Primero. Crea una clase separada FocusBehaviour:

using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace MyBehaviours { public class FocusBehaviour { #region IsFocused public static bool GetIsFocused(Control control) { return (bool) control.GetValue(IsFocusedProperty); } public static void SetIsFocused(Control control, bool value) { control.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached( "IsFocused", typeof(bool), typeof(FocusBehaviour), new UIPropertyMetadata(false, IsFocusedPropertyChanged)); public static void IsFocusedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var control = sender as Control; if (control == null || !(e.NewValue is bool)) return; if ((bool)e.NewValue && !(bool)e.OldValue) control.Focus(); } #endregion IsFocused #region IsListBoxItemSelected public static bool GetIsListBoxItemSelected(Control control) { return (bool) control.GetValue(IsListBoxItemSelectedProperty); } public static void SetIsListBoxItemSelected(Control control, bool value) { control.SetValue(IsListBoxItemSelectedProperty, value); } public static readonly DependencyProperty IsListBoxItemSelectedProperty = DependencyProperty.RegisterAttached( "IsListBoxItemSelected", typeof(bool), typeof(FocusBehaviour), new UIPropertyMetadata(false, IsListBoxItemSelectedPropertyChanged)); public static void IsListBoxItemSelectedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var control = sender as Control; DependencyObject p = control; while (p != null && !(p is ListBoxItem)) { p = VisualTreeHelper.GetParent(p); } if (p == null) return; ((ListBoxItem)p).IsSelected = (bool)e.NewValue; } #endregion IsListBoxItemSelected } }

Segundo. Agregue un estilo en la sección de recursos (mi estilo se redondea de negro en el foco). Aviso setter para la propiedad FocusBehaviour.IsListBoxItemSelected. Debes referenciarlo en xmlns:behave="clr-namespace:MyBehaviours"

`

<Style x:Key="PreviewTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}"> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Padding" Value="1"/> <Setter Property="AllowDrop" Value="true"/> <Setter Property="Background" Value="White"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Border Margin="6,2,0,4" BorderBrush="#FFBDBEBD" BorderThickness="1" CornerRadius="8" Background="White" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="100" x:Name="bg"> <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsKeyboardFocusWithin" Value="True"> <Setter Property="Background" TargetName="bg" Value="Black"/> <Setter Property="Background" Value="Black"/><!-- we need it for caret, it is black on black elsewise --> <Setter Property="Foreground" Value="White"/> <Setter Property="behave:FocusBehaviour.IsListBoxItemSelected" Value="True"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>

`

Tercero. (opcional, para la tarea inversa)

Se encontrará, si no hay alguna, tarea inversa, centrándose en TextBox cuando se seleccione ListBoxItem. Recomiendo usar otra propiedad de la clase de Comportamiento, IsFocused. Aquí hay una plantilla de muestra para ListBoxItem , por favor, tenga en cuenta Property="behave:FocusBehaviour.IsFocused" y FocusManager.IsFocusScope="True"

<DataTemplate x:Key="YourKey" DataType="{x:Type YourType}"> <Border Background="#FFF7F3F7" BorderBrush="#FFBDBEBD" BorderThickness="0,0,0,1" FocusManager.IsFocusScope="True" x:Name="bd" MinHeight="40"> <TextBox x:Name="textBox" Style="{StaticResource PreviewTextBox}" Text="{Binding Value}" /> </Border> <DataTemplate.Triggers> <DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True"> <Setter TargetName="textBox" Property="behave:FocusBehaviour.IsFocused" Value="True" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>


Vieja discusión, pero tal vez mi respuesta ayude a otros ...

La solución de Ben tiene el mismo problema que la solución de Grazer. Lo malo es que la selección depende del enfoque [del teclado] del cuadro de texto. Si tiene otro control en su diálogo (es decir, un botón), el foco se pierde al hacer clic en el botón y el elemento del cuadro de lista queda sin seleccionar (SelectedItem == null). Por lo tanto, tiene un comportamiento diferente al hacer clic en el elemento (fuera del cuadro de texto) y al hacer clic en el cuadro de texto. Esto es muy tedioso de manejar y se ve muy extraño.

Estoy bastante seguro de que no hay una solución XAML pura para esto. Necesitamos código detrás de esto. La solución está cerca de lo que Mark sugirió.

(en mi ejemplo, uso ListViewItem en lugar de ListBoxItem, pero la solución funciona para ambos).

Código detrás:

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e) { var frameworkElement = sender as FrameworkElement; if (frameworkElement != null) { var item = FindParent<ListViewItem>(frameworkElement); if (item != null) item.IsSelected = true; } }

con FindParent (tomado de http://www.infragistics.com/community/blogs/blagunas/archive/2013/05/29/find-the-parent-control-of-a-specific-type-in-wpf-and-silverlight.aspx ):

public static T FindParent<T>(DependencyObject child) where T : DependencyObject { //get parent item DependencyObject parentObject = VisualTreeHelper.GetParent(child); //we''ve reached the end of the tree if (parentObject == null) return null; //check if the parent matches the type we''re looking for T parent = parentObject as T; if (parent != null) return parent; return FindParent<T>(parentObject); }

En mi DataTemplate:

<TextBox Text="{Binding Name}" PreviewMouseDown="Element_PreviewMouseDown"/>