tags:

views:

54

answers:

2

For a project I'm working on I needed to create a treeview with drag/drop reordering. Before messing with the actual project, I subclassed treeview in a separate project and added handlers for the Drop, DragOver, MouseMove, and DragLeave events and implemented them as necessary. Drag and drop reordering works fine for the side project because I had it set up such that each TreeViewItem stores the item I am interested in in the Header property.

Here is some of the code:

       void MultiSelectTreeView_Drop(object sender, System.Windows.DragEventArgs e)
    {
        if (e.Data.GetDataPresent(typeof(TreeViewItem)))
        {
            TreeViewItem sourceItem = (TreeViewItem)e.Data.GetData(typeof(TreeViewItem));
            TreeViewItem targetItem = e.Source as TreeViewItem;

            dynamic parent;

            if (targetItem == null) //not dropped in the treeview
            {
                _isDragging = false;
                return;
            }
            else if (targetItem.Parent is TreeViewItem) //drag must start from either a treeviewitem...
            {
                parent = targetItem.Parent as TreeViewItem;
            }
            else // ... or the root treeview
            {
                parent = targetItem.Parent as TreeView;
            }

            //Complete the drag/drop
            //must be same parent to successfully drop
            if(sourceItem.Parent == targetItem.Parent)
            {
                 var indexOfDropTarget = parent.Items.IndexOf(targetItem);
                 parent.Items.RemoveAt(parent.Items.IndexOf(sourceItem));
                 parent.Items.Insert(indexOfDropTarget, sourceItem);
                 sourceItem.IsSelected = true;
            }

        }

        _isDragging = false;
    }

The rules of the treeview I need are that an item being dropped for reordering can only be reordered within the same parent.

The code above works great, under the assumption that the treeviews children are all treeview items, each of which contains the data structure I am interested in.

When moving my new treeview class to the bigger project, I noticed that because the treeview is bound to a collection in the view model, e.Data is no longer a treeview item! It is now the datastructure from the view model. e.Source is also no longer a treeview item, it is the entire treeview so I have no way of telling where I am "dropping" the item.

The treeview is built automatically based off the datastructure in the view model. Each item has a Children property, and each item in the Children property can have their own children. My requirement is that the items within a Children property can be reordered, but a child could not be moved to the same collection its parent is stored in or one of it's childrens' collections.

My question is this: How do I get the TreeViewItem being dragged and the TreeViewItem being dropped upon? I need to be able to see the parent of each and make sure they are the same.

Am I going about this the wrong way? Should I be passing commands back to the view model instead?

Thanks!

+1  A: 

I think you need to find the container for your data structure. If you need the way, I have done this using this code:

TreeViewItem container = myTreeViewItem.ItemContainerGenerator.ContainerFromItem(child).As<TreeViewItem>();

you have to iterate in the tree to find the container of child. since from view you are not having reference of container of direct parent of child object, you have to find it from root(treeview) iteratively.

viky
A: 

What I ended up doing was walking the visual tree. I was able to get the textblocks that the events first occurred on and from there it was just a matter of walking up the tree until I found the proper treeview.

The items I was looking for are still stored in the treeviewitem.header property.

If anyone else runs in to the same problem, this link will teach you everything you need

brandon
Why such a fragile and, well, ugly, solution, when you got an answer 5 days ago that actually works? The TreeView's ItemContainerGenerator can give you all the TreeViewItems stored in it, and each TreeViewItem also has an ItemContainerGenerator which lets you access its children.
jalf
Because it makes more sense to me to work my way up from the textblock. The code is not fragile by any means, it always finds the treeviewitem I am looking for. If I were to start at the root tree view, i'd have to iterate through a bunch of parents until I finally get down to the tree view I want. If I start at the textblock that receives the routed event, I just walk up to the first TreeViewitem that contains it. Simple.
brandon