"Когда я добавляю этот триггер, элемент выбирается, когда фокус находится на дочернем TextBox, но первое поведение исчезает. Теперь, когда я щелкаю за пределами ListBox, элемент отменяется."
На самом деле, я не думаю, что он потерял то оригинальное поведение. Я подозреваю, что происходит, когда вы щелкаете прямо в текстовом поле откуда-то еще, поэтому базовый ListBoxItem фактически никогда не выбирается. Однако, если это произойдет, вы увидите, что выбор останется после того, как вы ушли, как хотите.
Вы можете проверить это, принудительно выбрав ListBoxItem, щелкнув непосредственно по нему (примечание: вы всегда должны давать ему фон, даже если он просто «прозрачный», чтобы он мог получать щелчки мыши, чего не будет если он нулевой) или даже просто нажмите Shift-Tab, чтобы установить фокус там, обратно из текстового поля.
Однако это не решает вашу проблему, а именно то, что TextBox получает фокус, но не сообщает об этом базовому ListBoxItem.
Два подхода, которые вы можете использовать для этого, - это запуск события или прикрепленное поведение.
Первый - это триггер события IsKeyboardFocusWithinChanged, в котором для IsSelected установлено значение true, если фокус клавиатуры изменился на true. (Примечание: ответ Шеридана делает ложное уведомление об изменении, но его не следует использовать в случаях, когда вы можете выбрать несколько элементов в списке, потому что все становится выбранным.) Но даже триггер события вызывает проблемы, потому что вы теряете варианты выбора нескольких элементов. такие как переключение или щелчок по диапазону и т. д.
Другой (и мой предпочтительный подход) заключается в написании присоединенного поведения, которое вы устанавливаете в ListBoxItem, либо напрямую, либо через стиль, если хотите.
Вот прикрепленное поведение. Примечание. Если вы захотите реализовать это, вам снова потребуется обработать несколько элементов. Также обратите внимание, что, хотя я прикрепляю поведение к ListBoxItem, внутри я приводлю к UIElement. Таким образом, вы также можете использовать его в ComboBoxItem, TreeViewItem и т. Д. В основном любой ContainerItem в элементе управления на основе селектора.
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);
}
}
... и вы просто добавляете это как установщик свойства в стиле вашего ListBoxItem
<Setter Property="behaviors:AutoSelectWhenAnyChildGetsFocus.Enabled" Value="True" />
Это, конечно, предполагает, что вы импортировали пространство имен XML, называемое «поведения», которое указывает на пространство имен, в котором содержится класс. Вы можете поместить сам класс в общую библиотеку «Помощник», что мы и делаем. Таким образом, везде, где мы этого хотим, его простое свойство устанавливается в XAML, а все остальное заботится о поведении.