Checkboxes in WPF TreeView
Posted by K. M. on August 3, 2008
Josh Smith recently posted an interesting article on CodeProject about using CheckBoxes in a WPF TreeView.
“The fundamental problem is that as you navigate the tree via arrow keys, a TreeViewItem
will first take input focus, and then the CheckBox
it contains will take focus upon the next keystroke. Both the TreeViewItem
and CheckBox
controls are focusable. The result is that you must press an arrow key twice to navigate from item to item in the tree.”
His solution is to set Focusable
to false on the CheckBox
and make the TreeViewItem
a “virtual ToggleButton
” by applying attached properties.
Since the problem has to do with focus, it can also be solved by setting focus on the CheckBox
when the TreeViewItem
receives focus. The class shown below defines an attached property – FocusedChildName
– that sets focus on a named element in the visual tree of the target element. Adding a Setter
to the Style
for the TreeViewItem
and setting up a binding on the Focusable property of the CheckBox
Focusable=”{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=IsSelected}”
produces the desired behavior
{
public static string GetFocusedChildName(DependencyObject obj)
{
return (string)obj.GetValue(FocusedChildNameProperty);
}
public static void SetFocusedChildName(DependencyObject obj, string value)
{
obj.SetValue(FocusedChildNameProperty, value);
}
public static readonly DependencyProperty FocusedChildNameProperty =
DependencyProperty.RegisterAttached(“FocusedChildName”, typeof(string), typeof(FocusHelper),
new UIPropertyMetadata(FocusedChildName_PropertyChanged));
private static void FocusedChildName_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement element = d as UIElement;
if (element != null && !string.IsNullOrEmpty((string)e.NewValue))
element.GotKeyboardFocus += element_GotKeyboardFocus;
if (element != null && string.IsNullOrEmpty((string)e.NewValue))
element.GotKeyboardFocus -= element_GotKeyboardFocus;
}
private static void element_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (e.OriginalSource == sender)
{
(sender as DispatcherObject).Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(() =>
{
DependencyObject obj = sender as DependencyObject;
string name = (string)obj.GetValue(FocusHelper.FocusedChildNameProperty);
IInputElement element = FindNamedElementInVisualTree(obj, name) as IInputElement;
if (element != null)
element.Focus();
}));
}
}
private static DependencyObject FindNamedElementInVisualTree(DependencyObject parent, string name)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if ((string)child.GetValue(FrameworkElement.NameProperty) == name) return child;
child = FindNamedElementInVisualTree(child, name);
if (child != null) return child;
}
return null;
}
}
Leave a comment