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.