Windows Forms: Highlight destination node in TreeView during Drag&Drop - c#

I created Drag&Drop mechanism for my TreeView. I added DragEnter, DragDrop and ItemDrag methods and everything works fine.
But when you are doing D&D with standard Windows controls, destination node is highlighted.
Image is worth 1000 words, video probably even more:
http://www.youtube.com/watch?v=PlltSiihHPo
I mean such highlight effect like you can see in this video on Recycle Bin.

That's not a TreeView, it's a ListView with View = LargeIcons. TreeView isn't a great control as a drop target since it hides sub-nodes. But you can solve both problems by implementing the DragOver event. Test where the mouse is at and expand and select the node:
void treeView1_DragOver(object sender, DragEventArgs e) {
var pos = treeView1.PointToClient(new Point(e.X, e.Y));
var hit = treeView1.HitTest(pos);
if (hit.Node != null) {
hit.Node.Expand();
treeView1.SelectedNode = hit.Node;
}
}

Related

Getting the actual dropped UIElement object on Drop Event in UWP

I'm converting an application I wrote in WinForms to UWP and as far as I can tell, the Drag n Drop functionality is slightly different. Here is my code from my WinForms application that I used to get the 'dragged' object, which is a Control called FunctionButton;
private void flowLayoutPanel_ActiveGroup_DragDrop(object sender, DragEventArgs e)
{
Function_Button draggedItem;
/* Check if the dragged item is one of the allowed dragged item TYPES. */
draggedItem = (Function_Button)e.Data.GetData(type);
if (draggedItem != null)
{
//DO STUFF
}
}
I'm currently setting my own StringDataFormats when the Drag Starts for the information i need, which I read using DataView.GetDataAsync(), although how can I get direct access to the dragged UIElement object in UWP?
I am not sure if this is the best way but it works.
First you need to handle the DragStarting event and store the UIElement that will be dragged inside the DataPackage that is exposed by the Data property. The DataPackage type seems to be very conditioned on files and file formats but fortunately it has a general purpose dictionary exposed by property Properties.
<local:YourElement CanDrag="True" DragStarting="dragStarting">
</local:YourElement>
private void dragStarting(UIElement sender, DragStartingEventArgs args)
{
args.Data.Properties.Add("anykeyworks", sender);
}
Next you handle the Drop event as follows:
<local:YourOtherElement AllowDrop="True" DragOver="dragOver" Drop="drop">
</<local:YourOtherElement>
private void drop(object sender, DragEventArgs e)
{
UIElement element = e.DataView.Properties["anykeyworks"] as UIElement;
}
private void dragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
}
If you do not implement the DragOver handler, the Drop event won't be fired.
As you've known, the drag and drop function in UWP is different from what in WinForms. In UWP apps, what we are dragging and dropping is not the UIElement but the DataPackage. So we can't get direct access to the dragged UIElement object.
I'm not sure why you want to get the dragged or dropped UIElement object. If you want to do some checks while dropping, I think you can check the content of the DataPackageView class which is exposed by DataView property.
For more information about drag and drop function in UWP, please see Drag and drop and also the official Drag and drop sample on GitHub.

Ignore parent in PreviewMouseButtonDown on TreeviewItem

I have a treeview and im trying to implement a drag and drop functionallity.
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="TreeViewItem.PreviewMouseLeftButtonDown" Handler="PreviewMouseLeftButtonDown" />
</Style>
</TreeView.ItemContainerStyle>
Then i have an event in the code behind to handle the click, and start the drag:
void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is TreeViewItem)
{
//clone the tree item, create an adorner, apply adorner
//doDragDrop
}
}
My problem is since PreviewMouseLftButtonDown is a routed event using the tunneling strategy, when the mouse is clicked on a treenode item, I get the root node first. I am not interested in the root node, only the leaf nodes. My structure is like this:
HeaderNodeObject
|_LeafnodeObject
|_LeafNodeOject
So in my event I need to basically not do anything and just proceed if the sender treeViewItem is a HeaderNodeObject. But how can i tell what it is? the sender comes in as a treeViewItem, im not sure how to tell what the object it holds is.
You should be able to use e.Source as TreeViewItem to retrieve the item that was clicked. You can check it against sender to confirm that you only process the event when it has reached the source.
This should result in the event only being processed for the clicked item:
void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.Source is TreeViewItem item && sender == item)
/* do something */;
}
Note, however, that using the event so bluntly means that you'll receive the event if any part of the item receives a mouse down event, including the expansion button. Thus, be careful to *not* set e.Handled = true, lest you prevent the user from toggling expansion, among other things.
The above solution only works for leafs, and I had to do this for a TreeView where I wanted to process the PreviewMouseLeftDown event on any node in the tree. I finally solved it by ignoring the event for all nodes where a child had mouse over. Like this:
var treeViewItem = sender as TreeViewItem;
if (treeViewItem.Items.OfType<TreeViewItem>().All(item => !item.IsMouseOver))
{
//do my stuff
}
Sort of a hackish solution, but I couldn't find any better, and at least it gets the job done...
I solved this fairly simply by just checking if the itemsource on the sender is null. If its null that means its a leaf node:
if (sender is TreeViewItem)
{
Item = sender as TreeViewItem;
//make sure we have a leaf node, if we dont just move on.
if (Item.ItemsSource == null)
{
//do my stuff
}
}
Will not call this a duplicate answer but will simply mention that Øystein Kolsrud's answer is as correct as this can be without data binding. I don't have enough reputation or else I would simply comment and up-vote, but "User's" answer will only work for leafs on the tree, while Øystein's answer works for all nodes on the tree, including all the leaf nodes.
My case involved going through a fairly shallow (yet heavily populated) tree of recursively populated 'work items.' I was using a TreeView to display the work flow hierarchy, where a selected node would raise an event that captured the Work ID text from the header of the TreeViewItem, so I could then be able to use that WorkID in a meaningful way. When I wanted this to happen for more than just the root or just the first level of children (leaving out the root), Øystein's answer is the way I had to go. Definitely hackish, but definitely effective.
All of the following code I had (besides his conditional check), was there previously. All I had to do was include his check and everything worked as I initially intended.
Just wanted to share a real-world example of this working and verify his work. This condition works like a charm and saved my butt. Thanks again, Øystein Kolsrud!!
private void TreeItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem workItem = sender as TreeViewItem;
if(workItem.Items.OfType<TreeViewItem>().All(item => !item.IsMouseOver))
{
string workItemHeader = workItem.Header.ToString();
string stopAt = "-";
currentWorkIDToEdit = workItemHeader.Substring(0, workItemHeader.IndexOf(stopAt));
workItemToEdit.Content = "Work Item To Edit: " + currentWorkIDToEdit + " ";
}
}

how can i achieve this in c#

i'm developing a windows form application.in the form, the left part is a tree menu, and the right part is show area. how can i change the show area according to what i click on the tree menu.
(source: 126.net)
i use treenode class to implement treemenu like this:
System.Windows.Forms.TreeNode treeNode27 = new System.Windows.Forms.TreeNode("basic operation");
what i try to do is use several panels. each panel bounds to a menu item. by setting the visible property, i can achieve that goal. but it is too inconvenient.especially when i try to design each panel.
any good suggestion?
You could design each "Panel" as a new User Control. That way you can design all of the "panels / areas" on their own, independently of the Main Form.
On your Main Form, create a single panel for the right hand side area and add all of the controls to that one panel.
Then when the TreeNode selection event happens you can set all the user controls to .Visible = false; except for the one you are showing and set that to .Visible = true; and .Dock = DockStyle.Fill;
What you need is an event handler that will be called at the time of the user clicking the treeview (Use TreeView from the toolBox). You can do that by selecting the treeview on the design page and under properties click on Events. Then select NodeMouseDoubleClick or NodeMouseClick depending upon what you want. Below is a code that captures the values selected...Enjoy...;)
private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (treeView1.SelectedNode.Level == 2)
{
//text on the first level
string text = treeView1.SelectedNode.Text;
}
else if (treeView1.SelectedNode.Level == 1)
{
//text on the second level
string text = treeView1.SelectedNode.Text;
}
}

C# Drag & Drop Between ListViews

I'm trying to create a self-contained Winforms control called DragDropListView. It derives from ListView.
I have code that allows the user to sort list items within the control by dragging and dropping the items in the new location. I achieved that by overriding OnDragDrop, OnDragOver, OnDragEnter, OnItemDrag.
The issue I have is with dragging from one listview to a completely different listview. The event fires on the other list view as expected, but the method doesn't take a "sender" argument, so there's no good way to tell where the items are being dragged from, and no way I can figure out to actually grab the items being dragged. The current code works with stuff like "this.SelectedItems," but I'd like it to be "sender.SelectedItems".
I guess the reason there is no sender argument is that the control isn't supposed to responsible for knowing that much about its environment, and the host Form should handle the interaction between two controls, but I'm trying to build self contained controls that have this functionality, so letting it bleed onto the form isn't going to work.
Ideas?
I think you can know the ListView from the Items by listViewItem.ListView property, Check it.
I didn't test the code:
private void listView1_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(ListView.ListViewItemCollection)))
{
e.Effect = DragDropEffects.None;
return;
}
var items = (ListView.ListViewItemCollection)e.Data.GetData(typeof(ListView.ListViewItemCollection));
if (items.Count > 0 && items[0].ListView != listView1)
{
e.Effect = DragDropEffects.None;
return;
}
}
Check DragEventArgs , this sample in CodeProject [VB.Net]
Good luck!

How to get ListViewItem under MouseCursor while dragging sth. over it

I'm implementing drag & drop on a ListView. I already managed to get the ListViewItem under the cursor when dropping on it but I would like to get the ListViewItem under the mouse cursor while I'm dragging sth. over the ListView-Control.
I would like to select the ListViewItem (selected=true) like in Windows Explorer when you are dragging files over a folder.
I thought about events like ItemMouseHover, MouseMove in the ListView but they are not fired when dragging sth. over it.
Hope you can help me...
Regards,
inno
P.S.: I'm using .Net2.0
Have you tried responding to the DragOver event in the listview class? You should be able to do it this way.
private void listBox_DragOver(object sender,
DragEventArgs e)
{
//for ListView
var point = listView.PointToClient(new Point(e.X, e.Y));
var item = listView.GetItemAt( point.X, point.Y);
if(item != null)
{
//do whatever - select it, etc
}
//or, for ListBox
var indexOfItem =
listBox.IndexFromPoint(listBox.PointToClient(new Point(e.X, e.Y)));
if (indexOfItem != ListBox.NoMatches)
{
//do whatever - select it, etc
}
}
If you are doing drag and drop in a ListView, you learn a lot by looking at the code of ObjectListView (an open source wrapper around .NET WinForms ListView).
If you use an ObjectListView instead of a normal ListView, a lot of things, like drag and drop, happen automatically.

Categories