views:

196

answers:

2

I tried the solution discussed here: http://stackoverflow.com/questions/2030678/wpf-treeview-itemselected-moves-incorrectly-when-deleting-an-item

however, I'm still seeing the selection jump to that parent when deleting an item. What am I doing wrong?

MainWindow.xaml

<Window x:Class="TreeViewDelete.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Tree View Delete sample" Height="350" Width="525">

<Window.Resources>
    <Style TargetType="TreeViewItem">
        <Setter Property="IsSelected" Value="{Binding Selected, Mode=TwoWay}"/>
        <EventSetter Event="KeyDown" Handler="OnTreeKeyDown"/>
    </Style>
    <HierarchicalDataTemplate x:Key="recursiveTemplate" ItemsSource="{Binding Children}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Name}"/>
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>
<Grid>
    <TreeView Name="m_tree" ItemsSource="{Binding Children}" ItemTemplate="{StaticResource recursiveTemplate}"/>
</Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    private Container m_root, m_child;

    public MainWindow()
    {
        InitializeComponent();

        m_root = new Container("Root");

        m_child = new Container("main");

        m_child.Add(new Container("k1"));
        m_child.Add(new Container("k2"));
        m_child.Add(new Container("k3"));
        m_child.Add(new Container("k4"));
        m_child.Add(new Container("k5"));

        m_root.Add(m_child);

        m_tree.DataContext = m_root;
    }

    private IEnumerable<T> GetVisualAncestorsOfType<T>(DependencyObject obj) where T : DependencyObject
    {
        for (; obj != null; obj = VisualTreeHelper.GetParent(obj))
            if (obj is T)
                yield return (T)obj;
    }

    private void OnTreeKeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Delete:
                {
                    TreeViewItem item = sender as TreeViewItem;

                    if (item != null)
                    {
                        Container container = item.Header as Container;

                        if (container != null)
                        {
                            Container parent = container.Parent;

                            // Find the currently focused element in the TreeView's focus scope
                            DependencyObject focused =
                              FocusManager.GetFocusedElement(
                                FocusManager.GetFocusScope(m_tree)) as DependencyObject;

                            // Scan up the VisualTree to find the TreeViewItem for the parent
                            var parentContainer = (
                              from element in GetVisualAncestorsOfType<FrameworkElement>(focused)
                              where (element is TreeViewItem && element.DataContext == parent)
                                    || element is TreeView
                              select element
                              ).FirstOrDefault();

                            parent.Remove(container);

                            if (parentContainer != null)
                            {
                                parent.Children[0].Selected = true;
                                parentContainer.Focus();
                            }
                        }
                    }

                    e.Handled = true;
                }
                break;
        }
    }
}

and finally Container.cs

public class Container : INotifyPropertyChanged
{
    private bool m_selected;
    private string m_name;
    private ObservableCollection<Container> m_children;
    private Container m_parent;

    public Container(string name)
    {
        m_name = name;
        m_children = new ObservableCollection<Container>();
        m_parent = null;
        m_selected = false;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get
        {
            return m_name;
        }

        set
        {
            m_name = value;

            OnPropertyChanged("Name");
        }
    }

    public Container Parent
    {
        get
        {
            return m_parent;
        }

        set
        {
            m_parent = value;
        }
    }

    public bool Selected
    {
        get
        {
            return m_selected;
        }

        set
        {
            m_selected = value;

            OnPropertyChanged("Selected");
        }
    }

    public ObservableCollection<Container> Children
    {
        get
        {
            return m_children;
        }
    }

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

    public void Add(Container child)
    {
        m_children.Add(child);

        child.Parent = this;
    }

    public void Remove(Container child)
    {
        m_children.Remove(child);

        child.Parent = null;
    }
}
A: 

I didn't check this myself but i think you can make this happen by using the CollectionChanged event of the ObservableCollections. here you can attach an event to your children collection so that when one of them is deleted you go and set the Selected property of for example the first child of the collection to true. something like the following code:

attach the event:

    Children.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Children_CollectionChanged); 

event implementation:

void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
    {
        Children.First().Selected = true;
    }
}
Nima Rikhtegar
Isn't that what I'm already doing? parent.Children[0].Selected = true;
imekon
A: 

I tried the solution supplied here: "TreeView selected item when removing nodes" and in a simple application it works beautifully. Needless to say in the app I'm working on it doesn't...

Looking at what the atricle is talking about, it seems I need the TreeViewItem that is the container of my item. So I tried:

m_tree.ItemContainerGenerator.ContainerFromItem

Except this always returns null. Then I read the following: TreeView.ItemContainerGenerator.ContainerFromItem returns null, at which point my brain nearly blew a fuse!

It seems to get at the TreeViewItem I want to be selected, I have to start at the top of my hierarchy and work down the tree until I get to where I want. So, my container data has a Parent property, so I built a stack of objects:

Stack<Containerl> stack = new Stack<Container>();

Container toBeSelected = ... my object to be selected after deletion ...

while (toBeSelected != null)
{
    stack.Push(toBeSelected);

    toBeSelected = toBeSelected.Parent;
}

then I delete all the items from my hierarchy, then do the following:

TreeViewItem item = m_tree.ItemContainerGenerator.ContainerFromItem(stack.Pop()) as TreeViewItem;

while(item != null && (stack.Count > 0))
{
    item = item.ItemContainerGenerator.ContainerFromItem(stack.Pop()) as TreeViewItem;
}

// Force this item to be selected, and set focus
item.IsSelected = true;
item.Focus();

It works!!!

imekon