Reputation: 685
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:
((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.
Upvotes: 1
Views: 607
Reputation: 685
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.
Upvotes: 0
Reputation: 1141
You can try overriding PreviewKeyDown on the TreeView and handle it like this.
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;
}
}
}
EDIT
Sorry didn't notice that you used PreviewKeyDown. That said you are still missing the e.Handled = true
which will prevent the default handling of the event.
Upvotes: 1