TreeView/TextBox 键盘交互的许多问题

Numerous problems with TreeView/TextBox keyboard interactions

我有一个带有文本框的 TreeView。我希望 TextBoxes 不可写,无论是通过使它们只读还是禁用它们或使它们无法聚焦或其他方式,除非它们收到双击事件或它们包含在其中的 TreeViewItem 被选中并按下某些键,此时他们应该获得焦点并选择所有文本并允许正常文本编辑,直到他们失去焦点,此时他们应该 return 进入不可写状态。同时,我希望能够使用鼠标或箭头键在 TreeView 中导航。

我当前的尝试将此作为 TreeView 结构:

<TreeView name="EnrtyView" PreviewKeyDown="KeyNav">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type self:Entry}" ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <TextBox Text="{Binding Name}" MouseDoubleClick="TextEdit" LostFocus="StopEdit" IsReadOnly="False" />
                <TextBlock Text=" - " Visibility="{Binding ValueVisible}" Focusable="False" />
                <TextBox Text="{Binding Value}" MouseDoubleClick="TextEdit" LostFocus="StopEdit" Visibility="{Binding ValueVisible}" IsReadOnly="False" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

其中 Entry class 具有 NameValue 作为字符串属性,IsSelectedIsExpanded 作为布尔属性包含在内以允许尽管 Microsoft 尽了最大努力,但列表中项目的编程选择恰恰相反,并且 ValueVisible 作为可见性 属性。相关事件处理程序转载如下:

public void TextEdit(object sender, RoutedEventArgs e)
{
    (sender as TextBox).IsReadOnly = false;
    (sender as TextBox).Focus();
    (sender as TextBox).SelectAll();
}
public void StopEdit(object sender, RoutedEventArgs e)
{
    string txt = (sender as TextBox).Text;
    if (!ValidateName(txt))
    {
        MessageBox.Show("Problem parsing name- please try again.");
        (sender as TextBox).Focus();
        (sender as TextBox).SelectAll();
        return;
    }
    (sender as TextBox).IsReadOnly = true;
    (sender as TextBox).Select(0, 0);
}

public void KeyNav(object sender, KeyEventArgs e)
{
    if (!Editing)
    {
        if (e.Key == Key.Left)
        {
            ((Entry)(EntryView.SelectedItem)).GetNextLeft().IsSelected = true;
        }
        else if (e.Key == Key.Right)
        {
            ((Entry)(EntryView.SelectedItem)).GetNextRight().IsSelected = true;
        }
        else if (e.Key == Key.Up)
        {
            ((Entry)(EntryView.SelectedItem)).GetNextUp().IsSelected = true;
        }
        else if (e.Key == Key.Down)
        {
            ((Entry)(EntryView.SelectedItem)).GetNextDown().IsSelected = true;
        }
        else if (e.Key == Key.Tab || e.Key == Key.Enter || e.Key == Key.Space)
        {
            //Nothing here seems to work.
        }
    }
}

存在以下问题:

这些都可以解决吗?我现在最好只写自己的 TreeView class 吗?

编辑:

根据 R.Rusev 的建议,我尝试将 e.Handled = true 添加到我的 PreviewKeyDown 事件处理程序中。虽然这解决了前两个问题,但它表明我更改 TreeView 选择的策略实际上并没有起作用,这只是 TreeView 的默认行为泄漏了,这让我面临最后一个问题,我怀疑是过度虚拟化阻止直接对 TreeViewItems 进行任何合理的访问。

您可以尝试覆盖 TreeView 上的 PreviewKeyDown 并像这样处理它。

protected override void OnPreviewKeyDown( KeyEventArgs e )
{
    if (!Editing)
    {
        if ( e.Key == Key.Left )
        {
            ((Entry)(EntryView.SelectedItem)).GetNextLeft().IsSelected = true;
            e.Handled = true;
        }
        else if ( e.Key == Key.Right )
        {
            ((Entry)(EntryView.SelectedItem)).GetNextRight().IsSelected = true;                
            e.Handled = true;
        }
        else if ( e.Key == Key.Up )
        {
            ((Entry)(EntryView.SelectedItem)).GetNextUp().IsSelected = true;
            e.Handled = true;
        }
        else if ( e.Key == Key.Down )
        {
            ((Entry)(EntryView.SelectedItem)).GetNextDown().IsSelected = true;
            e.Handled = true;
        }
        else if ( e.Key == Key.Tab || e.Key == Key.Enter || e.Key == Key.Space )
        {
             //Nothing here seems to work.
            e.Handled = true;
        }
    }
}

编辑

抱歉没有注意到您使用了 PreviewKeyDown。那就是说您仍然缺少 e.Handled = true 这将阻止事件的默认处理。

部分答案,可能是最佳答案:

通过将 KeyboardNavigation.DirectionalNavigation="None" 添加到我的数据模板中的 StackPanel 并使用 TreeView 的默认行为,我的第一个问题得到解决,第二个问题变得毫无意义。我用于以编程方式 selecting 项目的代码神秘地开始工作,尽管它有一个问题(虽然我可以以编程方式 select 事情,但它们没有成为焦点)它与我原来的问题充分分离我要 post 一个新问题。我的第三个问题似乎完全无法解决。我目前最好的解决方法是,由于某种原因,TabCtrl+Tab 导航的行为是不同的,即使它们设置为相同,所以 Tab 不会在树的项目 Ctrl+Tab 中导航。这留下了一个问题,即在树导航和文本编辑之间转换需要单击鼠标或使用 Tab 逐步通过界面返回到树。