C# WPF 选项卡导航以从 ListBox 外部控制 ListBoxItem 内部
C# WPF Tab Navigate to control inside of a ListBoxItem from outside of ListBox
我有一个 window,带有版本文本框、按钮和一个 ListBox
,可以展开和收缩。每个 ListBox
项都有多个文本框。我正在尝试找到一种方法,以便从 ListBox
之外的选项卡导航到 ListBox
的第一项中的第一个 TextBox
。我可以让它导航到 ListBox
本身,如果我按下向下箭头键,它将 select 第一项,但这很笨拙。我需要它直接从 ListBox
之外的内容切换到 ListBox
.
内的内容
下面是我在 ListBox
.
中使用的一些 XAML
<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>
只需制作您的 ListBox IsTabStop="False"
和 LitBoxItemStyle。
我将此 setter 添加到 ListBoxItemStyle:<Setter Property="IsTabStop" Value="False"/>
<ListBox x:Name="add_users_listbox" Grid.Row="1" BorderThickness="0" Height="322" Padding="0,0,0,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
HorizontalAlignment="Center" VerticalAlignment="Top"
SelectionMode="Single"
IsTabStop="False"
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}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
<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"
MaxLength="20"
TextChanged="first_last_textbox_TextChanged"
PreviewTextInput="first_last_textbox_PreviewTextInput"
Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
因此,这可能不是执行此操作的理想方法,但它确实有效。首先我们在DataTemplate
中的TextBox
添加绑定标签
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>
为简单起见,我将该标签绑定到 ItemSource
集合中的相应索引。
int index = Add_User_Binding.Count;
ListBox_Item_Collection.Add(new SomeDataType()
{
Index = index,
The_Textbox = "Stuff in TextBox",
});
下一步是在该事件之前的用户控件中添加一个 KeyDown 事件。在这种情况下,我们将找到带有该标签的元素,然后使用 Dispatcher
将其聚焦。
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);
}));
}
}
如您所见,许多工作本应简单得多。
我有一个 window,带有版本文本框、按钮和一个 ListBox
,可以展开和收缩。每个 ListBox
项都有多个文本框。我正在尝试找到一种方法,以便从 ListBox
之外的选项卡导航到 ListBox
的第一项中的第一个 TextBox
。我可以让它导航到 ListBox
本身,如果我按下向下箭头键,它将 select 第一项,但这很笨拙。我需要它直接从 ListBox
之外的内容切换到 ListBox
.
下面是我在 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>
只需制作您的 ListBox IsTabStop="False"
和 LitBoxItemStyle。
我将此 setter 添加到 ListBoxItemStyle:<Setter Property="IsTabStop" Value="False"/>
<ListBox x:Name="add_users_listbox" Grid.Row="1" BorderThickness="0" Height="322" Padding="0,0,0,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
HorizontalAlignment="Center" VerticalAlignment="Top"
SelectionMode="Single"
IsTabStop="False"
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}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
<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"
MaxLength="20"
TextChanged="first_last_textbox_TextChanged"
PreviewTextInput="first_last_textbox_PreviewTextInput"
Text="{Binding First_Textbox, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
因此,这可能不是执行此操作的理想方法,但它确实有效。首先我们在DataTemplate
TextBox
添加绑定标签
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>
为简单起见,我将该标签绑定到 ItemSource
集合中的相应索引。
int index = Add_User_Binding.Count;
ListBox_Item_Collection.Add(new SomeDataType()
{
Index = index,
The_Textbox = "Stuff in TextBox",
});
下一步是在该事件之前的用户控件中添加一个 KeyDown 事件。在这种情况下,我们将找到带有该标签的元素,然后使用 Dispatcher
将其聚焦。
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);
}));
}
}
如您所见,许多工作本应简单得多。