"Quando aggiungo questo innesco l'elemento è selezionato quando il focus è su un TextBox bambino, ma il primo comportamento scompare. Ora, quando si fa clic al di fuori della ListBox, l'elemento viene de-selezionata."
In realtà, non penso che abbia perso quel comportamento originale. Quello che sospetto stia accadendo è che stai facendo clic direttamente nella casella di testo da qualche altra parte, quindi il ListBoxItem sottostante non è mai stato effettivamente selezionato. Se lo facesse, vedresti che la selezione rimarrà comunque dopo che te ne sei andato come vuoi.
È possibile testare questo forzando il ListBoxItem da selezionare facendo clic direttamente su di esso (nota a margine: si dovrebbe sempre dare uno sfondo, anche se solo 'trasparente' in modo che possa ricevere clic del mouse, che ha vinto ' t se è null) o anche solo premendo 'Maiusc-Tab' per impostare lo stato attivo lì, indietro dalla casella di testo.
Tuttavia, ciò non risolve il problema, ovvero che il controllo TextBox viene attivato ma non consente al ListBoxItem sottostante di conoscerlo.
I due approcci che è possibile utilizzare per questo sono un trigger di evento o un comportamento collegato.
Il primo è un evento innescato nell'evento IsKeyboardFocusWithinChanged in cui si imposta "IsSelected" su true se lo stato attivo della tastiera è impostato su true. (Nota: la risposta di Sheridan fa una notifica di falsi cambiamenti ma non dovrebbe essere usata nei casi in cui è possibile selezionare multipla nell'elenco perché tutto viene selezionato.) Ma anche un evento trigger causa problemi perché si perdono i comportamenti di selezione multipla come toggling o range-click, ecc.
L'altro (e il mio approccio preferito) è scrivere un comportamento collegato che si imposta su ListBoxItem, direttamente o tramite uno stile se si preferisce.
Ecco il comportamento allegato. Nota: dovrai ancora gestire le cose a selezione multipla se vuoi implementarle. Si noti inoltre che sebbene io stia collegando il comportamento a un ListBoxItem, all'interno del cast di UIElement. In questo modo è anche possibile utilizzarlo in ComboBoxItem, TreeViewItem, ecc. Fondamentalmente qualsiasi ContainerItem in un controllo basato su selettore.
public class AutoSelectWhenAnyChildGetsFocus
{
public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached(
"Enabled",
typeof(bool),
typeof(AutoSelectWhenAnyChildGetsFocus),
new UIPropertyMetadata(false, Enabled_Changed));
public static bool GetEnabled(DependencyObject obj){ return (bool)obj.GetValue(EnabledProperty); }
public static void SetEnabled(DependencyObject obj, bool value){ obj.SetValue(EnabledProperty, value); }
private static void Enabled_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var attachEvents = (bool)e.NewValue;
var targetUiElement = (UIElement)sender;
if(attachEvents)
targetUiElement.IsKeyboardFocusWithinChanged += TargetUiElement_IsKeyboardFocusWithinChanged;
else
targetUiElement.IsKeyboardFocusWithinChanged -= TargetUiElement_IsKeyboardFocusWithinChanged;
}
static void TargetUiElement_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var targetUiElement = (UIElement)sender;
if(targetUiElement.IsKeyboardFocusWithin)
Selector.SetIsSelected(targetUiElement, true);
}
}
... ed è sufficiente aggiungere questo come un setter struttura in stile del vostro ListBoxItem
<Setter Property="behaviors:AutoSelectWhenAnyChildGetsFocus.Enabled" Value="True" />
Questo ovviamente presuppone che si sia importato un namespace XML chiamato 'comportamenti' che punta allo spazio dei nomi dove la classe è contenuta. Puoi mettere la classe stessa in una libreria 'Helper' condivisa, che è ciò che facciamo. In questo modo, ovunque lo vogliamo, è una semplice proprietà impostata in XAML e il comportamento si prende cura di tutto il resto.
Bella soluzione XAML-only: https://stackoverflow.com/a/15383435/419761 – l33t