0
votes

So Microsoft has decided to make the process of programmatically selecting items in a TreeView obscenely difficult and malfunctional for some insane reason or other, and the only way to do it (since virtualization ensures that any TreeViewItem you try to select doesn't currently exist) is to create a boolean IsVisible property on whatever you want to use for your source data, which by the way has to implement INotifyPropertyChanged and include an event handler now if it didn't before, and then add an ItemContainerStyle to your TreeView binding the property to the IsVisible property of the TreeViewItem.

What this doesn't do however, is set focus to the selected item, so if for example your goal was to let the user delete tree items with the keyboard and have the focus automatically shift to the deleted item's parent so the user doesn't have to continuously tab through items or click on things to get their place back, this is nearly useless. It somehow doesn't even avoid the virtualization problem, allowing you to set the selection to something that it claims in the next line of code doesn't exist.

Here's my XAML:

and relevant C#:

public partial class MainWindow : Window
{
    public ObservableCollection<TreeEdit> Source { get; set; }

    public MainWindow()
    {
        Source = new ObservableCollection<TreeEdit>();
        Source.Add(new TreeEdit("Hi"));
        DataContext = this;
        InitializeComponent();
    }

    private void KeyNav(object sender, KeyEventArgs e)
    {
        TreeEdit Selection = (sender as TreeView).SelectedItem as TreeEdit;
        ObservableCollection<TreeEdit> TSource = (ObservableCollection<TreeEdit>)(sender as TreeView).ItemsSource;

        if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
        {
            if (e.Key == Key.Left && Selection != null)
            {
                if (Selection.Parent == null)
                {
                    TSource.Remove(Selection);
                }
                else
                {
                    Selection.Parent.IsSelected = true;
                    ((TreeViewItem)((sender as TreeView).ItemContainerGenerator.ContainerFromItem(Selection.Parent))).Focus();
                    Selection.Parent.Remove(Selection);
                }
            }
        }
    }
}

public class TagEdit : INotifyPropertyChanged
{
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            OnPropertyChanged("Name");
            name = value;
        }
    }
    private string name;
    public bool IsSelected
    {
        get
        {
            return selected;
        }
        set
        {
            OnPropertyChanged("IsSelected");
            selected = value;
        }
    }
    private bool selected;

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    public TagEdit Parent;
    public ObservableCollection<TagEdit> Children { get; set; }

    public TagEdit(string n)
    {
        Name = n;
        Children = new ObservableCollection<TagEdit>();
    }

    public void Remove(TagEdit tag)
    {
        Children.Remove(tag);
    }
}

The idea is that the user can navigate the TreeView normally with the arrow keys, then use Ctrl+Left to delete the selected item and select its parent. (sender as TreeView).ItemContainerGenerator.ContainerFromItem(Selection.Parent) often returns null. Removing it causes the proper item to be selected, but the TreeView loses focus. When it doesn't return null, I get the expected behavior.

Also, despite both the TreeView's KeyboardNavigation.TabNavigation and KeyboardNavigation.ControlTabNavigation being set to the default value of Continue, they behave differently (Tab ignores the TreeView's component elements while Ctrl+Tab steps into them).

I've been trying to get something, anything to work for almost a week now and if my opening paragraph didn't tip you off I've long since worn out my patience. Please don't tell me to try anything unless you have already, personally typed exactly what you're about to tell me to try into VisualStudio and it worked.

Apologies for the harsh tone, but this problem went beyond ridiculous and into obscene some time ago.

1

1 Answers

0
votes

The WPF TreeView doesn't have a single ItemContainerGenerator. Every item in a tree view is an ItemsControl and thus has its own ItemContainerGenerator for its child items. What you really need to do is to get the grand parent of the item you are going to delete and use THAT ItemContainerGenerator to call ContainerFromItem.

It's not working because you are trying to use the top level ItemContainerGenerator which only contains the top level items.

P.S. on a friendly side note :), who deletes items with a Ctrl+Left? That's undiscoverable. Why not just do this behavior when they hit Delete?