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);
            }));
        }
    }

如您所见,许多工作本应简单得多。