WPF treeview: how to implement keyboard navigation like in Explorer?

Funny, this does not seem to be a popular topic. Anyway, in the meantime I have developed a solution to the problem that satisfies me:

I attach a behavior to the TreeViewItems. In that behavior, I handle KeyUp events. In the KeyUp event handler, I search the visual tree top to bottom as it is displayed. If I find a first matching node (whose name starts with the letter on the key pressed) I select that node.


I know that is an old topic, but I guess it is still relevant for some people. I made this solution. It is attached to the KeyUp and the TextInput event on a WPF TreeView. I'm using TextInput in addition to KeyUp as I had difficulty translating "national" chars to real chars with KeyEventArgs. That went much more smooth with TextInput.

// <TreeView Name="treeView1" KeyUp="treeView1_KeyUp" TextInput="treeView1_TextInput"/>

    private bool searchdeep = true;             // Searches in subitems
    private bool searchstartfound = false;      // true when current selected item is found. Ensures that you don't seach backwards and that you only search on the current level (if not searchdeep is true)
    private string searchterm = "";             // what to search for
    private DateTime LastSearch = DateTime.Now; // resets searchterm if last input is older than 1 second.

    private void treeView1_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {  
        // reset searchterm if any "special" key is pressed
        if (e.Key < Key.A)
            searchterm = "";

    }

    private void treeView1_TextInput(object sender, TextCompositionEventArgs e)
    {
        if ((DateTime.Now - LastSearch).Seconds > 1)
            searchterm = "";

        LastSearch = DateTime.Now;
        searchterm += e.Text;
        searchstartfound = treeView1.SelectedItem == null;

        foreach (var t in treeView1.Items)
            if (SearchTreeView((TreeViewItem) t, searchterm.ToLower()))
                break;
    }

   private bool SearchTreeView(TreeViewItem node, string searchterm)
    {
        if (node.IsSelected)
            searchstartfound = true;

        // Search current level first
        foreach (TreeViewItem subnode in node.Items)
        {
            // Search subnodes to the current node first
            if (subnode.IsSelected)
            {
                searchstartfound = true;
                if (subnode.IsExpanded)
                    foreach (TreeViewItem subsubnode in subnode.Items)
                        if (searchstartfound && subsubnode.Header.ToString().ToLower().StartsWith(searchterm))
                        {
                            subsubnode.IsSelected = true;
                            subsubnode.IsExpanded = true;
                            subsubnode.BringIntoView();
                            return true;
                        }
            }
            // Then search nodes on the same level
            if (searchstartfound && subnode.Header.ToString().ToLower().StartsWith(searchterm))
            {
                subnode.IsSelected = true;
                subnode.BringIntoView();
                return true;
            }
        }

        // If not found, search subnodes
        foreach (TreeViewItem subnode in node.Items)
        {
            if (!searchstartfound || searchdeep)
                if (SearchTreeView(subnode, searchterm))
                {
                    node.IsExpanded = true;
                    return true;
                }
        }

        return false;
    }