I need to do drag and drop using treeview in c#.For that i heard 3 events are the most common
1.itemDrag
2.DragDrop and 3.DragEnter.
whereas here itemDrag event is firing for me while dragging from a treeview ,but rest both the events are not firing for me.Tried many solutions and now came here for an solution
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
string[] strItem = e.Item.ToString().Split(':');
DoDragDrop(strItem[1], DragDropEffects.Copy | DragDropEffects.Move); }
the above method fires ,
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
the bove dragEnter event is not firing ,similarly the dragDrop event is also not firing.Why it's so??
Am dragging from the treeview and need to paste in PowerPoint or Word. (ie) treeview is something like an AddIn for Office Tools.
Regards,
Arshad
Allow Drop AND..
Ok, assuming you have all of your events being declared/created in the Form_Load... e.i.
:
tlAssemblies.DragDrop +=tlAssemblies_DragDrop;
tlAssemblies.MouseDown +=tlAssemblies_MouseDown;
tlAssemblies.MouseMove +=tlAssemblies_MouseMove;
tlAssemblies.DragEnter +=tlAssemblies_DragEnter;
tlAssemblies.DragOver += tlAssemblies_DragOver;
The drag Event is for when you fire the drag event inside your treeView which is why it is working. The dragEnter is when you enter the boundaries of a different* control.
i.e you want to drag from treeview 1 into treeview2.
If you are not trying to drag the item into a different control dragEnter is wrong.
Here is a drag drop event sample :
private void tlAssemblies_DragDrop(object sender, DragEventArgs e)
{
if (sender == null)
return;
Point p = tlAssemblies.PointToClient(new Point(e.X, e.Y));
TreeListNode dragNode = e.Data.GetData(typeof(TreeListNode)) as TreeListNode;
TreeListNode targetNode = tlAssemblies.CalcHitInfo(p).Node;
if (targetNode == null)
{
return;
}
}
Not sure if it is possible but you may want to change the dragEnter code you have and simply use it in the drag event i.e.
e.Effect = DragDropEffects.Move;
If you are not leaving the same control you are dragging both to and fro, might as well show the drag movement.
Another thing you could do is on the treeView_MouseMove Event.
private void tlAssemblies_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && downHitInfo != null)
{
Size dragSize = SystemInformation.DragSize;
Rectangle dragRect = new Rectangle(new Point(downHitInfo.HitPoint.X - dragSize.Width / 2,
downHitInfo.HitPoint.Y - dragSize.Height / 2), dragSize);
if (!dragRect.Contains(new Point(e.X, e.Y)))
{
DataRow row = viewProduct.GetDataRow(downHitInfo.RowHandle);
if(row != null)
tlAssemblies.DoDragDrop(row, DragDropEffects.Move);
//viewProduct.GridControl.DoDragDrop(row, DragDropEffects.All);
downHitInfo = null;
DevExpress.Utils.DXMouseEventArgs.GetMouseArgs(e).Handled = true;
}
}
}
From your provided code, I cannot see that you have implemented the DragDrop event nor have set the AllowDrop property on the controls involved, which is needed along with the two other events in order to perform a drag and drop.
Here is sample snatched directly from MSDN, which uses two treeviews to move nodes in between. Add a couple of treeviews, add some root and child nodes, and wire up these events. Remember the AllowDrop property.
I have added a couple of Debug.WriteLine() to help with the debugging while testing this. Can be hard to do with breakpoints ;-)
NOTE: For brewity I have not supplied the event wiring code, nor the code for creating sample nodes. If needed this can be found in the referenced article. Otherwise add those using the property window.
Sample code:
private void treeView_ItemDrag(object sender, ItemDragEventArgs e)
{
Debug.WriteLine("ItemDrag fired!");
DoDragDrop(e.Item, DragDropEffects.Move);
}
private void treeView_DragEnter(object sender, DragEventArgs e)
{
Debug.WriteLine("TreeView DragEnter fired!");
e.Effect = DragDropEffects.Move;
}
private void treeView_DragDrop(object sender, DragEventArgs e)
{
Debug.WriteLine("TreeView DragDrop fired!");
TreeNode NewNode;
if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
{
Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
TreeNode DestinationNode = ((TreeView)sender).GetNodeAt(pt);
NewNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
if (DestinationNode.TreeView != NewNode.TreeView)
{
DestinationNode.Nodes.Add((TreeNode)NewNode.Clone());
DestinationNode.Expand();
//Remove Original Node
NewNode.Remove();
}
}
}
Related
I have two pictureboxes in my Windows Forms Application. Each of these pictureboxes hold an image. pictureBox1 is small, only 122 x 52, and pictureBox2 is much larger (459 x 566). What I want to do is be able to drag-and-drop the image of picturebox1 onto picturebox2 and a new image will be created and saved. Whereever x&y coordinates I place pictureBox1's image, it will "stamp" it right at that location in pictureBox2. And then pictureBox2's image will be modified and saved. So simply by dragging-and-dropping, the user should be able to "stamp" images onto pictureBox2 easily. Is this possible?
Mr Snrub,
If you use drag drop, you are in control of what you wish to do. Generally, in the MouseDown event of the control you determine whether a dragevent is starting or not. I keep a form property.
private Point _mouseDownPoint;
I set that in the drag from control during MouseDown
protected override void OnMouseDown(MouseEventArgs e)
{
_mouseDownPoint = e.Location;
}
In the OnMouseMove event for that same control. This code ensures that the user is most likely trying to drag and begins the dragdrop. This code comes from a usercontrol, so the this in the DoDragDrop may have to be changed in your case.
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button != MouseButtons.Left) return;
var dx = e.X - _mouseDownPoint.X;
var dy = e.Y - _mouseDownPoint.Y;
if (Math.Abs(dx) > SystemInformation.DoubleClickSize.Width || Math.Abs(dy) > SystemInformation.DoubleClickSize.Height)
{
DoDragDrop(this, DragDropEffects.Move);
}
}
Your control(s) which may receive the drop should have their DragEnter events coded. Here we have a DragEnter event that differentiates between a ToolStripButton and a custom UserControl DSDPicBox. Controls without a DragEnter event coded will display the noDrop Icon when dragged over.
private void Control_DragEnter(object sender, DragEventArgs e)
{
var button = e.Data.GetData(typeof(ToolStripButton)) as ToolStripButton;
if (button != null)
{
e.Effect = DragDropEffects.Copy;
return;
}
var element = e.Data.GetData(typeof(DSDPicBox)) as DSDPicBox;
if (element == null) return;
e.Effect = DragDropEffects.Move;
}
And lastly, you must handle the drop. the panelDropPoint is the coordinates of where the item was dropped. You could use that to position your new graphic in the old one. You will have to render the picture at the new resolution if you are changing its size.
private void panel_DragDrop(object sender, DragEventArgs e)
{
// Test to see where we are dropping. Sender will be control on which you dropped
var panelDropPoint = sender.PointToClient(new Point(e.X, e.Y));
var panelItem = sender.GetChildAtPoint(panelDropPoint);
_insertionPoint = panelItem == null ? destination.Controls.Count : destination.Controls.GetChildIndex(panelItem, false);
var whodat = e.Data.GetData(typeof(ToolStripButton)) as ToolStripButton;
if (whodat != null)
{
//Dropping from a primitive button
_dropped = true;
whodat.PerformClick();
return;
}
}
I have removed a few items from the code that would just muddy the waters. This code may not work out of the box, but should get you closer.
Regards,
Marc
I have a custom user control that contains a chart element that takes up 80% of the space.
When this is placed in a form, i can click the area not occupied by the chart and the click/mousedown/mouseup events work. But yet when i click in the chart area the mouse events aren't passed through and therefore not triggered.
Is there a global way of doing this without having at add the event function for each control on the form?
void Drag_MouseDown(object sender, MouseEventArgs e)
{
activeControl = sender as UserControl;
previousLocation = e.Location;
Cursor = Cursors.SizeAll;
}
void Drag_MouseMove(object sender, MouseEventArgs e)
{
if (activeControl == null || activeControl != sender)
return;
var location = activeControl.Location;
location.Offset(e.Location.X - previousLocation.X, e.Location.Y - previousLocation.Y);
activeControl.Location = location;
}
void Drag_MouseUp(object sender, MouseEventArgs e)
{
activeControl = null;
Cursor = Cursors.Default;
}
These are at the moment having to be manually set to both the custom usercontrol and the chart
It seems like chart overrides your mouse events. So, you can try to add event listeners to chart as well.
PS: We can assign same method to different listeners.
Im trying to drag a label from a FlowLayoutPanel to another FlowLayoutPanel.Im able to drag but it wont drop the label?
My Code
private void flp_DragEnter(object sender, DragEventArgs e)
{
if ((e.AllowedEffect & DragDropEffects.Link) != 0
&& e.Data.GetDataPresent(typeof(string)))
e.Effect = DragDropEffects.Link;
}
private void flp_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(Label)))
{
FlowLayoutPanel destination = (FlowLayoutPanel)sender;
Control control = (Control)e.Data.GetData(typeof(Label));
destination.Controls.Add(control);
return;
}
}
private void lbl_MouseDown(object sender, MouseEventArgs e)
{
DoDragDrop((sender as Label).Text, DragDropEffects.Link);
}
You can set the Name of the controls as drag data, and then when dropping, find the control by name and remove it from its parent controls collection and add it to target panel controls collection. To do so:
Set AllowDrop property for each control that is target of drop. The target controls in your example can be both FlowLayoutPanel controls.
Handle MouseDown event for each control that drag starts with it and in the handler call DoDragDrop event of that control and set the data that you want to drag. The moving controls in your example are labels and the data here can be Name of control.
Handle DragEnetr event of each target of drag and set e.Effect to determine if drop is allowed or not. Here is the place that you can check if drop is allowed. For example you can check if the data is string and the string is the name of a control.
Hanlde DragDrop and use GetData method of e.Data to get the data and perform the actions when drop. The action here is removing the control from its current parent and add it to the new parent.
Code:
Using the below code, if you assign control_MouseDown to MouseDown events of all labels and assign panel_DragEnter to DragEnter event of both flow layout panels and also assign panel_DragDrop to DragDrop event of both flow layout panels, you can move labels between both flow layout panels, also you can re order labels in a panel:
private void control_MouseDown(object sender, MouseEventArgs e)
{
var control = sender as Control;
this.DoDragDrop(control.Name, DragDropEffects.Move);
}
private void panel_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(string)))
return;
var name = e.Data.GetData(typeof(string)) as string;
var control = this.Controls.Find(name, true).FirstOrDefault();
if (control != null)
{
e.Effect = DragDropEffects.Move;
}
}
private void panel_DragDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(string)))
return;
var name = e.Data.GetData(typeof(string)) as string;
var control = this.Controls.Find(name, true).FirstOrDefault();
if (control != null)
{
control.Parent.Controls.Remove(control);
var panel = sender as FlowLayoutPanel;
((FlowLayoutPanel)sender).Controls.Add(control);
}
}
I have the following code that successfully drags and drops a treeview node to a textbox in a WFA:
private void _MyTreeView_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item.ToString(), DragDropEffects.Copy);
}
private void textBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
textBox1.Text += (System.String)e.Data.GetData(typeof(System.String));
}
}
but I wanted to be able to do a few other things with the drag and drop.
(1) If the node I'm dragging to the textbox has a Text property value of 'MyTreeNode', then the value that appears in the textbox is 'TreeNode: MyTreeNode' and not 'MyTreeNode' i.e. it adds 'TreeNode: ' at the start. How can I fix this?
(2) Is there someway I can prevent all TreeNodes at the root level from being dragged and dropped?
(3) With the code I have above, when I drag and drop a treenode the treenode text is appended to the end of the text that is already in the textbox. Am I able to add a 'drop cursor'(i dont know what you would call it) and have the treenode dropped at the position its actually dropped i.e. the position of the cursor?
The TreeNode.ToString() method doesn't do what you hope it does, you'll have to use the TreeNode.Text property explicitly. Combining 1) and 2):
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) {
var node = (TreeNode)e.Item;
if (node.Level > 0) {
DoDragDrop(node.Text, DragDropEffects.Copy);
}
}
Your DragEnter event handler is too permissive, you allow anything to be dragged to the TextBox. But you only can handle a string, so check for that:
private void textBox1_DragEnter(object sender, DragEventArgs e) {
if (e.Data.GetDataPresent(typeof(string))) e.Effect = DragDropEffects.Copy;
}
And you avoid the concatenation by using a simple assignment instead of +=
private void textBox1_DragDrop(object sender, DragEventArgs e) {
textBox1.Text = (string)e.Data.GetData(typeof(string));
}
Do consider a Label instead of a TextBox if the user should not edit the text himself.
I am working on a project. It does with drag and drop this is the code I have right now.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
listBox1.DoDragDrop(listBox1.SelectedItem.ToString(), DragDropEffects.Move);
}
private void listBox2_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
listBox2.Items.Add(e.Data.GetData(DataFormats.Text));
listBox1.Items.Remove(listBox1.SelectedItem.ToString());
}
It lets you add to the second list box but I am trying to get it where you can also move the item back to first listbox if you want to. Do I repeat the code for the second list box as I did for the first one or is there a line of code I could just add.Also how can you tell if your program is “unbreakable”. Thanks.
Do I repeat the code for the second list box
Pretty much, yeah. Although you can simplify it a bit since the code will be essentially identical by having both listboxes use the same handler for MouseDown, DragEnter and DragDrop and then use the sender to figure out whether it's listBox1 or listBox2.
Also, you might want to think about your MouseDown handler. Most users won't expect a single click to immediately start a drag operation. Usually you would look for the mouse down and then for a mouse move while the button is down before starting the drag.
What I usually do is something like this:
private Size dragSize = SystemInformation.DragSize;
private Rectangle dragBounds = Rectangle.Empty;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragBounds = new Rectangle(new Point(e.X - dragSize.Width / 2, e.Y - dragSize.Height/2), dragSize);
}
else
{
dragBounds = Rectangle.Empty;
}
}
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && dragBounds != Rectangle.Empty && !dragBounds.Contains(e.X, e.Y))
{
//start drag
listBox1.DoDragDrop(listBox1.SelectedItem.ToString(), DragDropEffects.Move);
dragBounds = Rectangle.Empty;
}
}
For the main question of implementing drag & drop: yes, you would need to create handlers for listbox1 and listbox2 that mirror the functionality you already have:
A MouseDown event handler for listBox2
A DragEnter handler for listBox1
A DragDrop handler for listBox1.
Also you'd need to make sure you assign these handlers to be used for their respective events in the form designer.
You could reapeat the code, but I tend to not want to do that. Yours is an edge case; lots of methods that only have one line in them. But any time I see repetition in code, it signals to me that I need to pull that code out somewhere else. If the repetition is in the same class, move it to its own method on the class. If the repetition is in separate classes, either find another class outside the two where it makes sense to put the new method, or consider creating a new class that both classes can share. In your case, if I decided to move the code, I would do something like this:
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(listbox1);
}
private void listBox2_DragEnter(object sender, DragEventArgs e)
{
HandleDragEnter( e );
}
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
HandleDragDrop( listBox1, listBox2, e );
}
private void listBox2_MouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(listBox2);
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
HandleDragEnter( e );
}
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
HandleDragDrop( listBox2, listBox1, e );
}
private void HandleMouseDown( ListBox listBox )
{
listBox.DoDragDrop(listBox.SelectedItem.ToString(), DragDropEffects.Move);
}
private void HandleDragEnter( DragEventArgs e )
{
e.Effect = e.AllowedEffect;
}
private void HandleDragDrop( ListBox src, ListBox dst, DragEventArgs e )
{
dst.Items.Add( e.Data.GetData(DataFormats.Text) );
src.Items.Remove( src.SelectedItem.ToString() );
}
The advantage to moving the code is, if those methods grow to more than one line, you can change them in only one place. Of course, for a one line method, I would also remember that I can always move it to its own method later. My personal preference would be to leave the two one-line methods as-is, doing a copy & paste for the second listbox, and splitting out the DragDrop handler into its own method like I did above.