bindingfocusmanager

Binding FocusedElement in HierarchicalDataTemplate


I am trying to focus a control within a HierarchicalDataTemplate. Unfortunately, my attempt to bind to a control within the template is failing. Here is my code:

        <HierarchicalDataTemplate DataType="{x:Type TreeView_Experiment:BookmarkPage}">
            <DockPanel>
                <ToggleButton ... edited for brevity ... />
                <Controls:EditableTextBlock x:Name="EditBox" Text="{Binding Path=Title}" VerticalAlignment="Center" IsEditable="True" Focusable="True"/>
            </DockPanel>
            <HierarchicalDataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=IsFocused, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}" Value="true">
                    <!-- The Value binding fails with the error: Cannot find source for binding with reference 'ElementName=EditBox -->
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=EditBox}"/>
                </DataTrigger>
            </HierarchicalDataTemplate.Triggers>
        </HierarchicalDataTemplate>

The trigger fires when the treeview item gets the focus, but the {Binding ElementName=EditBox} fails with the message "Cannot find source for binding with reference 'ElementName=EditBox,..."

How can I fix this binding? Or is there a better way to set the focus of a control within a HierarchicalDataTemplate?

Thanks in advance for any help.


Solution

  • I was able to get this working by adding this ugly little bit of code behind. Attach this to the treeview's onselection changed:

        private void SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            var treeView = sender as TreeView;
    
            var selectedContainer =
                typeof (TreeView).GetProperty("SelectedContainer", BindingFlags.NonPublic | BindingFlags.Instance).
                    GetValue(treeView, null) as TreeViewItem;
    
            // Find the element we want to focus.
            var focusControl = selectedContainer.FindVisualChild<TextBox>();
            if (focusControl != null)
                focusControl.Focus();
        }
    

    The following extension method is required:

        public static T FindVisualChild<T>(this DependencyObject obj) where T : DependencyObject
        {
            var childCount = VisualTreeHelper.GetChildrenCount(obj);
            for (int i = 0; i < childCount; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                {
                    return (T)child;
                }
    
                T childOfChild = FindVisualChild<T>(child);
                if (childOfChild != null)
                {
                    return childOfChild;
                }
            }
            return null;
        }