P... P... - 3 months ago 16
C# Question

Numerous problems with TreeView/TextBox keyboard interactions

I have a TreeView with TextBoxes in it. I want the TextBoxes not to be writable, whether that's by making them read-only or disabling them or making them unfocusable or whatever, unless they either receive a double-click event or the TreeViewItem they are contained in is selected and some Key is pressed, at which point they should gain focus and have all their text selected and allow normal text editing until they lose focus, at which point they should return to their un-writable state. At the same time, I want to be able to navigate the TreeView using either the mouse or the arrow keys.

My current attempt has this as the TreeView structure:

<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>


where the
Entry
class has
Name
and
Value
as string properties,
IsSelected
and
IsExpanded
as bool properties included to allow programmatic selection of items in the list despite Microsoft's best efforts to the contrary, and
ValueVisible
as a Visibility property. The relevant event handlers are reproduced below:

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.
}
}
}


It has the following problems:


  • The TextBoxes steal focus from the TreeView, making keyboard navigation impossible.


    • Making the TextBoxes unfocusable or disabling them fixes this, but makes it so they can't receive the MouseDoubleClick events either.


  • The default KeyDown behavior of the TreeView is not always overridden, which leads to some keys having unexpected effects.


    • Overriding other events doesn't seem to help. Testing is difficult because of the above problem with TextBoxes stealing focus.


  • The obvious way to start editing the TextBoxes when hitting some key (with something like
    ((TextBox)(((StackPanel)(((TreeViewItem)(EntryView.ItemContainerGenerator.ContainerFromItem(EntryView.SelectedItem))).Header)).Children[0]))
    ) doesn't work, probably because of excessive virtualization.



Are any of these solvable? Am I better off just writing my own TreeView class at this point?

Edit:

In accordance with R.Rusev's suggestion, I tried adding
e.Handled = true
to my PreviewKeyDown event handler. While this stopped the first two problems, it revealed that my strategy for changing the selection of the TreeView wasn't actually working, it was just the TreeView's default behavior leaking through, which leaves me with the last problem of what I suspect is excessive virtualization preventing any reasonable access of the TreeViewItems directly.

Answer

Partial answer, possibly the best available:

By adding KeyboardNavigation.DirectionalNavigation="None" to the StackPanel in my data template and going with the default behavior of the TreeView, my first problem is solved and my second is rendered moot. My code for programmatically selecting items mysteriously started working, and though it has a problem (while I can select things programatically, they don't come into focus) it is sufficiently divorced from my original problem that I am going to post a new question. My third problem appears to be completely unsolvable. The best workaround I have at the moment is that for some reason the behavior of Tab versus Ctrl+Tab navigation is different even when they are set to be the same, and so while Tab does not navigate within the tree's items Ctrl+Tab does. This leaves the problem that transitioning between tree navigation and text editing requires either a mouse click or stepping through the interface with Tab to get back to the tree.