c# winform treeview Dragdrop effect - c#

I would like to indicate to the user that they are trying to drag to wrong node level by changing the icon on the mouse. I can't seem to get a handle on this...any suggestions?

Dropping on a treeview is an iffy proposition, the node that the user would want to drop on might not be visible. Either because it is collapsed and hidden or scrolled off the screen. Anyhoo, you want to use the DragOver event and check where the mouse is located. Here's a sample form that does this. Drop a treeview on it and add some nodes. Click on the form and drag. It only allows drops on the 2nd level nodes:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
treeView1.AllowDrop = true;
treeView1.DragEnter += treeView1_DragEnter;
treeView1.DragOver += treeView1_DragOver;
}
protected override void OnMouseDown(MouseEventArgs e) {
DoDragDrop("foo", DragDropEffects.Copy);
base.OnMouseDown(e);
}
private void treeView1_DragEnter(object sender, DragEventArgs e) {
// TODO: check e.Data
e.Effect = DragDropEffects.Copy;
}
private void treeView1_DragOver(object sender, DragEventArgs e) {
Point pos = treeView1.PointToClient(new Point(e.X, e.Y));
var hit = treeView1.HitTest(pos);
TreeNode node = hit.Node;
if (hit.Node != null) {
node.Expand();
if (node.Level != 1) node = null;
}
e.Effect = node != null ? DragDropEffects.Copy : DragDropEffects.None;
}
}

Check the http://msdn.microsoft.com/en-us/library/system.windows.forms.drageventargs.aspx for the documentation of DragEventArgs. You should set the effect to none:
e.Effect = DragDropEffects.None;

Related

How to regain focus in application after drag and drop to grid

In my application, I have a form with two panels. Inside one panel is a button. Inside the other is a DevExpress Grid control. The grid is made up of 3 columns. You can drag values from one column into the other to copy it.
My problem is that whenever I do a drag-and-drop from one column to another, the focus on the application goes into an unusual state. The grid remains focused; I can mouse over the headers and see them react as normal. However the rest of the application is not focused. Mouse over the button in the other panel does not react, nor do the menus or form controls. If I click on the button, it reacts like I clicked on an unfocused application. I have to click again to actually activate the button. Same for every control except the grid.
I have tried using Activate() and Focus() on the button and form but to no avail.
namespace Company.StuffUploader
{
public partial class ComputationGrid : DevExpress.XtraEditors.XtraUserControl
{
private BindingList<ComputationLinkModel> _links = new BindingList<ComputationLinkModel>();
public List<ComputationLinkModel> ComputationLinkModels
{
get
{
return new List<ComputationLinkModel>(_links);
}
}
public ComputationGrid()
{
InitializeComponent();
}
private void ComputationGrid_Load(object sender, EventArgs e)
{
_gridControl.DataSource = _links;
}
private DragDropEffects GetDragEffect(DragEventArgs e)
{
var text = e.Data.GetData("System.String") as string;
if (text == null)
return DragDropEffects.None;
var link = GetLinkFromScreenPoint(new Point(e.X, e.Y));
if (link == null)
return DragDropEffects.None;
var tokens = text.Split('\t');
if (tokens.Count() != 2)
return DragDropEffects.None;
var dateString = link.movedate.ToString("yyyy-MM-dd");
if (link.StuffSurfaceName == tokens[0] && dateString != tokens[1])
return DragDropEffects.Move;
else
return DragDropEffects.None;
}
private ComputationLinkModel GetLinkFromScreenPoint(Point screenPt)
{
var pt = _gridControl.PointToClient(screenPt);
var hitInfo = _gridView.CalcHitInfo(pt);
return _gridView.GetRow(hitInfo.RowHandle) as ComputationLinkModel;
}
private void _gridControl_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var hitInfo = _gridView.CalcHitInfo(e.Location);
if (hitInfo == null || !hitInfo.InRowCell)
return;
// Only allow dragging from target column
if (hitInfo.Column.AbsoluteIndex != 0)
return;
var link = _gridView.GetRow(hitInfo.RowHandle) as ComputationLinkModel;
if (link == null)
return;
var item = string.Format("{0}\t{1}", link.StuffSurfaceName, link.movedate.ToString("yyyy-MM-dd"));
DoDragDrop(item, DragDropEffects.Move);
}
}
private void _gridControl_DragOver(object sender, DragEventArgs e)
{
e.Effect = GetDragEffect(e);
}
private void _gridControl_DragDrop(object sender, DragEventArgs e)
{
}
private void _gridControl_DragEnter(object sender, DragEventArgs e)
{
e.Effect = GetDragEffect(e);
}
private void _unlinkButton_Click(object sender, EventArgs e)
{
}
}
}
I figured out my own problem. Calling DoDragDrop() from within MouseDown event does not seem to work correctly. The proper way is to call it from MouseMove(). The documentation on MSDN hints at this in its example code.
Ensure that you set the DXMouseEventArgs.Handled property to true in the GridView's Mouse~ event handlers. It guarantees that default handling of these events will be prohibited. Review this example to see how to do this.

Create, Drag drop and resize controls on runtime

I am trying to create a form which contains panels created programmaticaly and controls able to drag drop and resize just like Microsoft Visual Studio IDE.
And I created something like this. there should be so many lines (blue one) and also so many boxes(yellow one) and I can be able to move yellow boxes inside of blue lines. everything works with defined controls on design time.
and the source codes here
public partial class Form1 : Form
{
bool allowResize = false;
public Form1()
{
InitializeComponent();
panel1.AllowDrop = true;
panel2.AllowDrop = true;
panel3.AllowDrop = true;
panel4.AllowDrop = true;
panel1.DragEnter += panel_DragEnter;
panel2.DragEnter += panel_DragEnter;
panel3.DragEnter += panel_DragEnter;
panel4.DragEnter += panel_DragEnter;
panel1.DragDrop += panel_DragDrop;
panel2.DragDrop += panel_DragDrop;
panel3.DragDrop += panel_DragDrop;
panel4.DragDrop += panel_DragDrop;
panelMove.MouseDown += panelMove_MouseDown;
}
void panelMove_MouseDown(object sender, MouseEventArgs e)
{
panelMove.DoDragDrop(panelMove, DragDropEffects.Move);
}
void panel_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
void panel_DragDrop(object sender, DragEventArgs e)
{
((Panel)e.Data.GetData(typeof(Panel))).Parent = (Panel)sender;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
allowResize = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
allowResize = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (allowResize)
{
this.panelMove.Height = pictureBox1.Top + e.Y;
this.panelMove.Width = pictureBox1.Left + e.X;
}
}
}
but I dont know how to create thoose controls (blue and yellow boxes ) on runtime.
You should check the Anchor property of Control. Anchor allows the control to automatically re-sized at runtime.
Use the Anchor property to define how a control is automatically
resized as its parent control is resized. Anchoring a control to its
parent control ensures that the anchored edges remain in the same
position relative to the edges of the parent control when the parent
control is resized.
You can anchor a control to one or more edges of its container. For
example, if you have a Form with a Button whose Anchor property value
is set to Top and Bottom, the Button is stretched to maintain the
anchored distance to the top and bottom edges of the Form as the
Height of the Form is increased.
MSDN : Control.Anchor

C# Drag and Drop from one Picture box into Another

I'm working in visual studio 2012 with C# and I need to Drag a Picture box into another picture box, basically replace the target Picturebox Image with the Dragged Picture box image.
How do I do this?
Please be specific and try to explain as simplest and as best as possible.
I'm extremely new to programming, and a bit desperate so please be patient with me.
Drag+drop is hidden on the PictureBox control. Not sure why, it works just fine. The probable guidance here is that it will not be obvious to the user that you could drop an image on the control. You'll have to do something about that, at least set the BackColor property to a non-default value so the user can see it.
Anyhoo, you'll need to implement the MouseDown event on the first picturebox so you can click it and start dragging:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) {
var img = pictureBox1.Image;
if (img == null) return;
if (DoDragDrop(img, DragDropEffects.Move) == DragDropEffects.Move) {
pictureBox1.Image = null;
}
}
I assumed you wanted to move the image, tweak if necessary if copying was intended. Then you'll have to implement the DragEnter and DragDrop events on the second picturebox. Since the properties are hidden, you should set them in the form's constructor. Like this:
public Form1() {
InitializeComponent();
pictureBox1.MouseDown += pictureBox1_MouseDown;
pictureBox2.AllowDrop = true;
pictureBox2.DragEnter += pictureBox2_DragEnter;
pictureBox2.DragDrop += pictureBox2_DragDrop;
}
void pictureBox2_DragEnter(object sender, DragEventArgs e) {
if (e.Data.GetDataPresent(DataFormats.Bitmap))
e.Effect = DragDropEffects.Move;
}
void pictureBox2_DragDrop(object sender, DragEventArgs e) {
var bmp = (Bitmap)e.Data.GetData(DataFormats.Bitmap);
pictureBox2.Image = bmp;
}
This does allow you to drag an image from another application into the box. Let's call it a feature. Use a bool flag if you want to disallow this.
Hans's answer led me to the correct solution. The problem with that answer is that putting DoDragDrop inside MouseDown will prevent MouseClick events from firing.
Here's my solution:
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var pb = (PictureBox)sender;
if (pb.BackgroundImage != null)
{
pb.DoDragDrop(pb, DragDropEffects.Move);
}
}
}
private void PictureBox_DragEnter (object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void PictureBox_DragDrop (object sender, DragEventArgs e)
{
var target = (PictureBox)sender;
if (e.Data.GetDataPresent(typeof(PictureBox)))
{
var source = (PictureBox)e.Data.GetData(typeof(PictureBox));
if (source != target)
{
// You can swap the images out, replace the target image, etc.
SwapImages(source, target);
}
}
}
Full working example on my GitHub.
You can use mouse enter and leave events to do this easily. For example you have two picture boxes pictureBox1 and pictureBox2. And you want to drag the image from picture box1 and drop it onto picture box2 do somthing like this.
private void pictureBox2_MouseUp(object sender, MouseEventArgs e)
{
if (a == 1)
{
pictureBox1.Image = pictureBox2.Image;
a = 0;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
a = 1;
}
Where 'a' is just a lock or key which checks whether the mouse has entered the control on which we want to drop this image on. Hope it helped, worked for me.
You can't set AllowDrop on PictureBox...set it for your whole form.
Code Snippet
Form1.AllowDrop = true;
Use the Form DragEnter, DragDrop events, they will work even if you drop it over the pictureBox.
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
int x = this.PointToClient(new Point(e.X, e.Y)).X;
int y = this.PointToClient(new Point(e.X, e.Y)).Y;
if(x >= pictureBox1.Location.X && x <= pictureBox1.Location.X + pictureBox1.Width && y >= pictureBox1.Location.Y && y <= pictureBox1.Location.Y + pictureBox1.Height)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
pictureBox1.Image = Image.FromFile(files[0]);
}
}

Drag & Drop between DataGridView

I have copied some code and modified it to suit my application. And I will continue to tweak and clean up the code until I am statisfied with it. But I have encountered a little error. I have two datagridviews and wish to move datagridrows from one to another. However, while the drag&drop events all fire, the dataGridView_Routes_DragDrop() will execute the log command because there is no data in e.Data.GetData. What have I done wrong? Am I missing something? I've tried to look through several guides but nothing specifically covers this issue.
How can I get the datagrid pass the dragged datagridrow over to the other datagrid?
/* Drag & Drop */
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private void dataGridView_Trips_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView_Trips.DoDragDrop(dataGridView_Trips.Rows[rowIndexFromMouseDown], DragDropEffects.Copy);
}
}
}
private void dataGridView_Trips_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
rowIndexFromMouseDown = dataGridView_Trips.HitTest(e.X, e.Y).RowIndex;
if (rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void dataGridView_Routes_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void dataGridView_Routes_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(DataRowView)))
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView_Routes.PointToClient(new Point(e.X, e.Y));
// If the drag operation was a copy then add the row to the other control.
if (e.Effect == DragDropEffects.Copy)
{
DataGridViewRow rowToMove = e.Data(typeof(DataGridViewRow)) as DataGridViewRow;
dataGridView_Routes.Rows.Add(rowToMove);
}
}
else
{
log("Geen data! #01", "Fout");
}
}
/* End Drag & Drop */
I don't know. But the following function has been adjusted and it works as intended. Not quite sure how the previous code broke.
EDIT: The typeof was written with DataViewRow instead of DataGridViewRow. Fail.
private void dataGridView_Routes_DragDrop(object sender, DragEventArgs e)
{
try
{
if (e.Data.GetDataPresent(typeof(DataGridViewRow)))
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView_Routes.PointToClient(new Point(e.X, e.Y));
// If the drag operation was a copy then add the row to the other control.
if (e.Effect == DragDropEffects.Copy)
{
DataGridViewRow Row = (DataGridViewRow)e.Data.GetData(typeof(DataGridViewRow));
dataGridView_Routes.Rows.Add(Row.Cells[0].Value, Row.Cells[1].Value, Row.Cells[2].Value);
}
}
else
{
log("Geen data! #01", "Fout");
}
}
catch (Exception msg)
{
log(msg.Message,"Fout");
}
}
This solution is for the people who have the datagridViews bound to customObjects. It works like a charm with multiple selection. Suggestions are accepted.
Assuming you want to drag from datagridview1 to datagridview2
//datagridview1 is bound to this BindingList
BindingList<myObject> object_bound_list1;
//datagridview2 is bound to this BindingList
BindingList<myObject> object_bound_list2;
List<myObject> selected_Object_list = new List<myObject>();
List<int> selected_pos_list = new List<int>();
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView1.DoDragDrop(
selected_Object_list,
DragDropEffects.Move);
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
int rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
//if shift key is not pressed
if (Control.ModifierKeys != Keys.Shift && Control.ModifierKeys != Keys.Control)
{
//if row under the mouse is not selected
if (!selected_pos_list.Contains(rowIndexFromMouseDown) && rowIndexFromMouseDown > 0)
{
//if there only one row selected
if (dataGridView1.SelectedRows.Count == 1)
{
//select the row below the mouse
dataGridView.ClearSelection();
dataGridView1.Rows[rowIndexFromMouseDown].Selected = true;
}
}
}
//clear the selection lists
selected_Object_list.Clear();
selected_pos_list.Clear();
//add the selected objects
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
selected_Object_list.Add(object_bound_list1[row.Index]);
selected_pos_list.Add(row.Index);
}
}
private void dataGridView2_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dataGridView2_DragDrop(object sender, DragEventArgs e)
{
if (e.Effect == DragDropEffects.Move)
{
foreach (var item in selected_Object_list)
{
object_bound_list2.Add(item);
}
}
}

knowing if the left button of mouse is released on the same list box

I have two list boxes, one for Avaiable Items and one for Selected items, so the user can move items between these two by either drag-drop or double-click.
It is not a perfect code and most of the logic is written in MouseMove event where I can have X and Y of the mouse location also...I am looking for this scenario to Prevent it: user holds left mouse button on the left list box and select an item BUT he releases the mouse button again on the same list box, so I need a way to know if it is still on the same list box then do not do the drag drop...so is there some method can tell me the boundaries of the list box that I can use? or any other better thoughts that you have?
private void lstAvailable_MouseMove(Object eventSender, MouseEventArgs eventArgs)
{
//*************** FUNCTION DETAILS ***************
//User moves mouse in the Available list
//***************** INSTRUCTIONS *****************
MouseButtons Button = eventArgs.Button;
int Shift = (int)Control.ModifierKeys / 0x10000;
float X = (float)VB6.PixelsToTwipsX(eventArgs.X);
float Y = (float)VB6.PixelsToTwipsY(eventArgs.Y);
moDualListBox.List1_MouseMove(Button, Shift, X, Y);
if (eventArgs.Button == MouseButtons.Left )
{
if (!mbClickProcessed) // it is a DragDrop
{
this.lstAvailable.DoDragDrop(this.lstAvailable.SelectedItems, DragDropEffects.Move);
mbClickProcessed = true;
}
if (mbClickProcessed) // it is a DoubleClick
{
MoveClick();
MoveLostFocus();
mbClickProcessed = true;
}
}
}
Sample for drag-drop (no error checking):
private ListBox _DraggingListBox = null;
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
if (_DraggingListBox != listBox1)
MoveItem(listBox2, listBox1, (int)e.Data.GetData(typeof(int)));
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
_DraggingListBox = listBox1;
listBox1.DoDragDrop(listBox1.IndexFromPoint(e.X,e.Y), DragDropEffects.Move);
}
private void listBox1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
if (_DraggingListBox != listBox2)
MoveItem(listBox1, listBox2, (int)e.Data.GetData(typeof(int)));
}
private void listBox2_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listBox2_MouseDown(object sender, MouseEventArgs e)
{
_DraggingListBox = listBox2;
listBox2.DoDragDrop(listBox2.IndexFromPoint(e.X,e.Y), DragDropEffects.Move);
}
private void MoveItem(ListBox fromLB, ListBox toLB, int index)
{
toLB.Items.Add(fromLB.Items[index]);
fromLB.Items.RemoveAt(index);
}
If you catch DragDrop event you have th sender of drag-drop operation.
So you can easily discard the operation (or do nothing) if sender is same as destination control...
If you want to know if mouse is leaving a control (and set a variable according to this) you can catch MouseLeave event, while MouseEnter event is handy to know when mouse enters a control from another one.

Categories