Problem
I have two ListView's. One has options that are to be dragged into the other. This is the "fields" ListView. The other one is the "builder" ListView. I cannot figure out a way to visually show the user where the item will be inserted. I would like to draw a line in-between the ListViewItem's to visually aid the user.
private void builder_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = false;
fields.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builder_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = true;
builder.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builderAndFields_DragDrop(object sender, DragEventArgs e)
{
ListViewItem i = new ListViewItem();
i = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
// Since this function works for both the builder and the fields,
// we have to check to see where we are dropping, the sender
// is the ListView we are dropping onto
if (sender.Equals(builder))
{
ListViewItem c = new ListViewItem();
c = (ListViewItem)i.Clone();
Point cp = builder.PointToClient(new Point(e.X, e.Y));
Console.WriteLine("cp: " + cp);
ListViewItem dragToItem = builder.GetItemAt(cp.X, cp.Y);
Console.WriteLine("dragToItem: " + dragToItem);
int dropIndex = dragToItem.Index;
// Now, we have to check to see if we are reordering or adding
// So, we check the flag to see if the dragDrop was initiated
// on the builder or on the fields ListView
if (fromBuilder)
{
builder.Items.Insert(dropIndex, c);
builder.Items.Remove(i);
}
else
{
Console.WriteLine(dropIndex);
builder.Items.Insert(dropIndex, c);
}
}
// If the sender is the fields listView, the user is trying to remove
// the item from the builder.
else
{
builder.Items.Remove(i);
}
}
Take a look at PreviewDragEnter, PreviewDragOver, and PreviewDragLeave.
You can use that event to add an Adorner to your drop list. IF you search for "WPF DragDropHelper" you will find several detailed examples.
Better ListView supports drop highlighting and insertion marks out of the box:
I think you can also do this in a regular ListView using HitTest() method within DragOver event handler.
Related
I am doing a simple drag and drop operation in windows forms. Whenever I start the operation the cursor changes shape. I know this is directly related to the DragDropEffects and I can't find an option that results in the default cursor. Can anybody help? Here is the code:
a.MouseDown += new MouseEventHandler(ButtonDown);
a.DragEnter += new DragEventHandler(ButtonDragEnter);
a.AllowDrop = true;
and here are the functions:
private void ButtonDown(object sender, EventArgs e)
{
PictureBox p = (PictureBox)sender;
ButtonClick(sender, e);
p.DoDragDrop(p.BackColor, DragDropEffects.All);
}
private void ButtonDragEnter(object sender, DragEventArgs e)
{
e.Data.GetFormats();
e.Effect = DragDropEffects.None;
Color c = new Color();
c = (Color) e.Data.GetData(c.GetType());
ButtonClick(sender, e,c);
}
Ok answered my own question here:
You first need to add an event to giveFeedBack:
a.GiveFeedback += new GiveFeedbackEventHandler(DragSource_GiveFeedback);
and here is the feedback function:
private void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = false;
}
Suppose we have a ListBox with the Custom elements and the blank panel . When you drag these items to the panel , they must build on a certain logic. For example, if there's nothing panel , the element is located in the middle. But if there is , then the new element is to stay near the element that is closest to it . As such it is possible to implement?
For example:
I modified this answer of working drag and drop implementation by adding some sorting logic. Placing an item in the middle visually can be done with CSS styling.
Assumption: "closest to it" means closest alphabetically.
public object lb_item = null;
private void listBox1_DragLeave(object sender, EventArgs e)
{
ListBox lb = sender as ListBox;
lb_item = lb.SelectedItem;
lb.Items.Remove(lb.SelectedItem);
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
if (lb_item != null)
{
listBox1.Items.Add(lb_item);
lb_item = null;
// Here I added the logic:
// Sort all items added previously
// (thereby placing item in the middle).
listBox1.Sorted = true;
}
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
lb_item = null;
if (listBox1.Items.Count == 0)
{
return;
}
int index = listBox1.IndexFromPoint(e.X, e.Y);
string s = listBox1.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s, DragDropEffects.All);
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
lb_item = null;
}
I have a range of drag drop activities all working fine on my quiz. (From answering questions to selecting an avatar)
There is a lot of repetition of the code and I am thinking it would be better implemented from a class and call the method each time that I use a drag drop.
Firstly can this be done? and secondly would I need a new method for the dragging and dropping?
Any thoughts or rough ideas would be great.
private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
{
pictureBox2.DoDragDrop(pictureBox2.Image, DragDropEffects.Copy);
}
private void panel1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void panel1_DragDrop(object sender, DragEventArgs e)
{
//Set background image of panel to selected avatar'
panel1.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
}
If all you want is to avoid duplicating the same events of several controls you should use common events:
private void commonPBox_MouseDown(object sender, MouseEventArgs e)
{
PictureBox PB = sender as PictureBox;
if (PB == null) return; //or throw an exception
PB.DoDragDrop(PB.Image, DragDropEffects.Copy);
}
private void commonPanel_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void commonPanel_DragDrop(object sender, DragEventArgs e)
{
Panel Pan = sender as Panel;
if (Pan == null) return; //or throw an exception
//Set background image of panel to selected avatar
Pan.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
}
Select the respective controls and enter the event names into the respective event name slots in the event pane of the properties tab.
Note how I cast the sender param of the event to get a types reference to the control that triggers the event. If the cast goes wrong the reference is set to null.
If you want more control and more flexibilty you may consider creating a DragAndDropcontroller class..:
static class DnDCtl
{
static List<Control> Targets = new List<Control>();
static List<Control> Sources = new List<Control>();
static public void RegisterSource(Control ctl)
{
if (!Sources.Contains(ctl) )
{
Sources.Add(ctl);
ctl.MouseDown += ctl_MouseDown;
}
}
static public void UnregisterSource(Control ctl)
{
if (Sources.Contains(ctl))
{
Sources.Remove(ctl);
}
}
static public void RegisterTarget(Control ctl)
{
if (!Targets.Contains(ctl))
{
Targets.Add(ctl);
ctl.DragEnter += ctl_DragEnter;
ctl.DragDrop += ctl_DragDrop;
ctl.AllowDrop = true;
}
}
static public void UnregisterTarget(Control ctl)
{
if (Targets.Contains(ctl))
{
Targets.Remove(ctl);
ctl.DragEnter -= ctl_DragEnter;
ctl.DragDrop -= ctl_DragDrop;
}
}
static void ctl_MouseDown(object sender, MouseEventArgs e)
{
PictureBox PB = sender as PictureBox;
if (PB != null) PB.DoDragDrop(PB.Image, DragDropEffects.Copy);
Panel Pan = sender as Panel;
if (Pan != null) Pan.DoDragDrop(Pan.BackgroundImage, DragDropEffects.Copy);
}
static void ctl_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
static void ctl_DragDrop(object sender, DragEventArgs e)
{
Panel Pan = sender as Panel;
if (Pan != null) Pan.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
PictureBox PB = sender as PictureBox;
if (PB != null) PB.BackgroundImage = (Image)e.Data.GetData(DataFormats.Bitmap);
}
}
Notes:
I have coded symmetric actions for Panels and PictureBoxes to show how you can handle different controls.
I have created but not used Lists of sources and targets. For more complex projects you will find them useful.
I have coded both Register and Unregister methods. You can register after some condition and unregister when it no longer applies
Such a Controller is good for dynamically alowing or disallowing Drag&Drop for controls, esp. when you create them dynamically.
You could also pass around delegates to decouple the dragdrop action from the controller.
Also note that some folks sometimes do prefer to use the MouseMove event over MouseDown esp. as it won't so easily interfere with making a selection.
ListView has a dedicated event instead, ListView.ItemDrag, which obviously should be used when registering!
I have two ListView's. One has options that are to be dragged into the other. This is the "fields" ListView. The other one is the "builder" ListView. The problem I am having is that I cannot have ListViewItem's inserted where the user drags it AND also be added to the bottom if they drag it to whitespace. I can do one or the other at this time. I need a solution for this.
private void builder_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = false;
fields.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builder_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = true;
builder.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builderAndFields_DragDrop(object sender, DragEventArgs e)
{
ListViewItem i = new ListViewItem();
i = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
// Since this function works for both the builder and the fields,
// we have to check to see where we are dropping, the sender
// is the ListView we are dropping onto
if (sender.Equals(builder))
{
ListViewItem c = new ListViewItem();
c = (ListViewItem)i.Clone();
Point cp = builder.PointToClient(new Point(e.X, e.Y));
Console.WriteLine("cp: " + cp);
ListViewItem dragToItem = builder.GetItemAt(cp.X, cp.Y);
Console.WriteLine("dragToItem: " + dragToItem);
int dropIndex = dragToItem.Index;
// Now, we have to check to see if we are reordering or adding
// So, we check the flag to see if the dragDrop was initiated
// on the builder or on the fields ListView
if (fromBuilder)
{
builder.Items.Insert(dropIndex, c);
builder.Items.Remove(i);
}
else
{
// ## Problem - Attempted solution ##
if (String.IsNullOrWhiteSpace(dragToItem.ToString()))
builder.Items.Add(c);
else
{
Console.WriteLine(dropIndex);
builder.Items.Insert(dropIndex, c);
}
}
}
// If the sender is the fields listView, the user is trying to remove
// the item from the builder.
else
{
builder.Items.Remove(i);
}
}
Thank you for your comment Hans. It was very helpful! Here is the solution to two problems I was having. The other was being able to reorder the ListView and drag items to the bottom of the list.
// Generic DragEnter
private void ddEnter_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
// ItemDrag Events
private void fields_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = false;
fields.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builder_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = true;
packetBuilder.DoDragDrop(e.Item, DragDropEffects.Move);
}
// DragDrop Events
private void builderAndFields_DragDrop(object sender, DragEventArgs e)
{
ListViewItem i = new ListViewItem();
i = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
// Since this function works for both the builder and the fields,
// we have to check to see where we are dropping, the sender
// is the ListView we are dropping onto
Console.WriteLine(sender.Equals(packetBuilder));
if (sender.Equals(packetBuilder))
{
ListViewItem c = new ListViewItem();
c = (ListViewItem)i.Clone();
Point cp = packetBuilder.PointToClient(new Point(e.X, e.Y));
// Now, we have to check to see if we are reordering or adding
// So, we check the flag to see if the dragDrop was initiated
// on the builder or on the fields ListView
Console.WriteLine(fromBuilder);
if (fromBuilder)
{
if (packetBuilder.HitTest(cp).Location.ToString() == "None")
{
packetBuilder.Items.Add(c);
packetBuilder.Items.Remove(i);
}
else
{
ListViewItem dragToItem = packetBuilder.GetItemAt(cp.X, cp.Y);
int dropIndex = dragToItem.Index;
packetBuilder.Items.Insert(dropIndex, c);
packetBuilder.Items.Remove(i);
}
}
else
{
if (packetBuilder.HitTest(cp).Location.ToString() == "None")
packetBuilder.Items.Add(c);
else
{
ListViewItem dragToItem = packetBuilder.GetItemAt(cp.X, cp.Y);
int dropIndex = dragToItem.Index;
packetBuilder.Items.Insert(dropIndex, c);
}
}
}
// If the sender is the fields listView, the user is trying to remove
// the item from the builder.
else
{
packetBuilder.Items.Remove(i);
}
}
I have a contextmenustrip that contains some option, I have almost implemented everything, i just want to check which items i have selected in options_MouseClick
private void ConsoleRichTextBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
options.Show();
Point currentPoint;
currentPoint = new Point(e.X, e.Y);
options.Show(ConsoleRichTextBox, currentPoint);
}
}
private void options_MouseClick(object sender, MouseEventArgs e)
{
//if options selected = clear
ConsoleRichTextBox.Clear();
}
You should just handle the Click event of each of the menu items. There's no need to handle the MouseClick event of a contextmenustrip. Also you can add multiple handlers to the same method and differentiate with the sender parameter as this will refer to the exact menu item that was clicked.
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Click += tsmi_Click;
and:
public void tsmi_Click(object sender, EventArgs e)
{
if (sender == tsmi)
{
// Do stuff
}
}