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 具有 Name
和 Value
作为字符串属性,IsSelected
和 IsExpanded
作为布尔属性包含在内以允许尽管 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.
}
}
}
存在以下问题:
- TextBoxes 窃取了 TreeView 的焦点,使得无法进行键盘导航。
- 使 TextBox 不可聚焦或禁用它们可以修复此问题,但要使其无法接收 MouseDoubleClick 事件。
- TreeView 的默认 KeyDown 行为并不总是被覆盖,这导致某些键具有意外效果。
- 覆盖其他事件似乎没有帮助。测试困难,因为上面的TextBoxes窃取焦点的问题。
- 在按下某个键(使用
((TextBox)(((StackPanel)(((TreeViewItem)(EntryView.ItemContainerGenerator.ContainerFromItem(EntryView.SelectedItem))).Header)).Children[0]))
之类的东西)时开始编辑文本框的明显方法不起作用,可能是因为过度虚拟化。
这些都可以解决吗?我现在最好只写自己的 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 一个新问题。我的第三个问题似乎完全无法解决。我目前最好的解决方法是,由于某种原因,Tab
与 Ctrl+Tab
导航的行为是不同的,即使它们设置为相同,所以 Tab
不会在树的项目 Ctrl+Tab
中导航。这留下了一个问题,即在树导航和文本编辑之间转换需要单击鼠标或使用 Tab
逐步通过界面返回到树。
我有一个带有文本框的 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 具有 Name
和 Value
作为字符串属性,IsSelected
和 IsExpanded
作为布尔属性包含在内以允许尽管 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.
}
}
}
存在以下问题:
- TextBoxes 窃取了 TreeView 的焦点,使得无法进行键盘导航。
- 使 TextBox 不可聚焦或禁用它们可以修复此问题,但要使其无法接收 MouseDoubleClick 事件。
- TreeView 的默认 KeyDown 行为并不总是被覆盖,这导致某些键具有意外效果。
- 覆盖其他事件似乎没有帮助。测试困难,因为上面的TextBoxes窃取焦点的问题。
- 在按下某个键(使用
((TextBox)(((StackPanel)(((TreeViewItem)(EntryView.ItemContainerGenerator.ContainerFromItem(EntryView.SelectedItem))).Header)).Children[0]))
之类的东西)时开始编辑文本框的明显方法不起作用,可能是因为过度虚拟化。
这些都可以解决吗?我现在最好只写自己的 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 一个新问题。我的第三个问题似乎完全无法解决。我目前最好的解决方法是,由于某种原因,Tab
与 Ctrl+Tab
导航的行为是不同的,即使它们设置为相同,所以 Tab
不会在树的项目 Ctrl+Tab
中导航。这留下了一个问题,即在树导航和文本编辑之间转换需要单击鼠标或使用 Tab
逐步通过界面返回到树。