I've a ListBox
with ListBoxItems
with a template so they contain TextBoxes
When the TextBox
gets focused I want the ListBoxItem
to be selected. One solution I've found looks like this:
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
This works great, but when the TextBox
loses focus so does the selection.
Is there a way to prevent this from happening?
Best solution I've found to do this with no code behinde is this:
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<EventTrigger RoutedEvent="PreviewGotKeyboardFocus">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="(ListBoxItem.IsSelected)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
You can also keep focus on the text box, but only have one ListBoxItem selected at any given time, with code behind.
In the ListBox XAML:
<ListBox
PreviewLostKeyboardFocus="CheckFocus">
</ListBox>
Then, in the CheckFocus()
method in code-behind:
/* Cause the original ListBoxItem to lose focus
* only if another ListBoxItem is being selected.
* If a different element type is selected, the
* original ListBoxItem will keep focus.
*/
private void CheckFocus(object sender, KeyboardFocusChangedEventArgs e)
{
// check if focus is moving from a ListBoxItem, to a ListBoxItem
if (e.OldFocus.GetType().Name == "ListBoxItem" && e.NewFocus.GetType().Name == "ListBoxItem")
{
// if so, cause the original ListBoxItem to loose focus
(e.OldFocus as ListBoxItem).IsSelected = false;
}
}
From the list of suggested solutions nothing helped me to resolve the same issue.
This is the custom solution I made:
1). Create Behavior (class that holds attached properties) that is going to enforce the focus:
public class TextBoxBehaviors
{
public static bool GetEnforceFocus(DependencyObject obj)
{
return (bool)obj.GetValue(EnforceFocusProperty);
}
public static void SetEnforceFocus(DependencyObject obj, bool value)
{
obj.SetValue(EnforceFocusProperty, value);
}
// Using a DependencyProperty as the backing store for EnforceFocus. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnforceFocusProperty =
DependencyProperty.RegisterAttached("EnforceFocus", typeof(bool), typeof(TextBoxBehaviors), new PropertyMetadata(false,
(o, e) =>
{
bool newValue = (bool)e.NewValue;
if (!newValue) return;
TextBox tb = o as TextBox;
if (tb == null)
{
MessageBox.Show("Target object should be typeof TextBox only. Execution has been seased", "TextBoxBehaviors warning",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
tb.TextChanged += OnTextChanged;
}));
private static void OnTextChanged(object o, TextChangedEventArgs e)
{
TextBox tb = o as TextBox;
tb.Focus();
/* You have to place your caret at the end of your text manually, because each focus repalce your caret at the beging of text.*/
tb.CaretIndex = tb.Text.Length;
}
}
2). Use this behavior in your XAML:
<DataTemplate x:Key="MyDataTemplate">
<TextBox behaviors:TextBoxBehaviors.EnforceFocus="True"
Text="{Binding Path=MyProperty, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>