I have a window with version text boxes, buttons, and a ListBox
that expands and contracts. Each ListBox
item has multiple text boxes. I am trying to find a way to tab navigate from outside of the the ListBox
to the first TextBox
in the first item for the ListBox
. I can get it to navigate to the ListBox
itself and if I hit the down arrow key it will select the first item but that is clumsy. I need it to tab directly from something outside the ListBox
to something inside the ListBox
.
Below is some of the XAML I use for the ListBox
.
<ListBox x:Name="add_users_listbox" Margin="2,116,-8,0" BorderThickness="0" Height="322" Padding="0,0,0,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
HorizontalAlignment="Center" VerticalAlignment="Top"
SelectionMode="Single"
IsTabStop="True"
TabIndex="1004"
Background="{x:Null}" BorderBrush="{x:Null}"
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding Add_User_Binding}"
SelectedIndex="{Binding Add_User_Selected_Index, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.Resources>
<Style TargetType="{x:Type ScrollBar}" BasedOn="{StaticResource ScrollBar_Rounded}"/>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource CustomListBoxItemStyle}"/>
<Style TargetType="{x:Type ListBox}" >
<Setter Property="KeyboardNavigation.TabNavigation" Value="Continue" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="60" Background="Transparent"
HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBox Height="26" Width="102" Padding="0,-1,0,1" Margin="2,2,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"
FontFamily="Segoe UI" FontSize="16" FontWeight="Bold"
TextWrapping="NoWrap" IsReadOnlyCaretVisible="True" UndoLimit="10" AllowDrop="False" MaxLines="1"
TabIndex="{Binding First_TabIndex}"
MaxLength="20"
TextChanged="first_last_textbox_TextChanged"
PreviewTextInput="first_last_textbox_PreviewTextInput"
Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So, this is likely NOT the ideal way to do this, but it works. First we add a bound tag to the TextBox
within the DataTemplate
ItemsSource="{Binding ListBox_Item_Collection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
Tag="{Binding Index}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
For simplicity I bound that tag to the corresponding index in the ItemSource
collection.
int index = Add_User_Binding.Count;
ListBox_Item_Collection.Add(new SomeDataType()
{
Index = index,
The_Textbox = "Stuff in TextBox",
});
The next step is to add a KeyDown event in the user control that comes before this one. In that event we will find the element with that tag and then use the Dispatcher
to focus it.
private void Preview_TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
string tag = "0";
IEnumerable<TextBox> elements = FindVisualChildren<TextBox>(this).Where(x => x.Tag != null && x.Tag.ToString() == tag);
foreach (TextBox element in elements)
{
FocusElement(element);
}
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private void FocusElement(IInputElement element)
{
if (element != null)
{
Dispatcher.BeginInvoke
(System.Windows.Threading.DispatcherPriority.ContextIdle,
new Action(delegate ()
{
Keyboard.Focus(element);
}));
}
}
As you can see, lots of work for something that should be much simpler.