I have successfully created DragDrop functionality with user controls. Now I am trying to allow DragDrop functionality on some components such as ToolStripButton.
The base class, ToolStripItem, supports AllowDrop and the DragEnter/DragDrop events...
ToolStripButton hides these properties in the designer, but they are publicly accessable.
Originally I tried doing the following for each ToolStripButton:
button.AllowDrop = true;
button.DragEnter += new DragEventHandler(button_DragEnter);
button.DragDrop += new DragEventHandler(button_DragDrop);
However, the events were not ever firing. These buttons are contained within a MenuStrip, so I changed the MenuStrip.AllowDrop to true. Then I started getting DragEnter and DragDrop events, but the DragDrop event would fail due to a threading/invoke problem when accessing the Tag property of the ToolStripItem.
The ToolStripItems cannot be invoked upon. So I have tried invoking their container, the MenuStrip, with the same function. I am still getting a threading/invoke problem where the thread stops running as soon as I try to access the ToolStripItem.
Here is the code I'm using to retrieve the Tag information after invoke:
void button_DragDrop(object sender, DragEventArgs e)
{
menuStrip.Invoke(new DragEventHandler(MyDragFunction), new object[] { sender, e });
}
void MyDragFunction(object sender, DragEventArgs e)
{
int id = (int)((ToolStripButton)sender).Tag;
// Debugging never reaches this line
int dragId = (int)e.Data.GetData(DataFormatName, false);
MoveItem(id, dragId);
}
Is Drag and Drop to a component like a ToolStripItem simply not possible? Or am I doing something wrong?
Here is the code I got to work for me.
I assign the DragDrop properties in the form constructor since they are hidden in the designer.
foreach (object o in menuStrip.Items)
{
if (o is ToolStripButton)
{
ToolStripItem item = (ToolStripItem)o;
item.AllowDrop = true;
item.DragEnter += new DragEventHandler(item_DragEnter);
item.DragOver += new DragEventHandler(item_DragOver);
item.DragDrop += new DragEventHandler(item_DragDrop);
}
}
private void item_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormatName, false))
{
e.Effect = DragDropEffects.Move;
}
}
private void item_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormatName, false))
{
e.Effect = DragDropEffects.Move;
}
}
private void item_DragDrop(object sender, DragEventArgs e)
{
int id = (int)e.Data.GetData(DataFormatName, false);
int category = Convert.ToInt32((sender as ToolStripButton).Tag);
MyFunction(category, id);
}
Related
Yesterday I try to implement a new listview that support sub-item edit, my solution is to show a textbox when double click the sub-item. The key code as following:
protected override void OnDoubleClick(EventArgs e)
{
Point pt = this.PointToClient(Cursor.Position);
ListViewItem curItem;
int subItemIndex = GetSubItemAt(pt.X, pt.Y, out curItem);
DoubleClickEventArgs args = new DoubleClickEventArgs(subItemIndex);
base.OnDoubleClick(args);
if (subItemIndex>=0 && !args.Cancel)
{
//StartEdit(...);
}
}
public void EndEdit(bool acceptChanges)
{
//validation
.................
.................
AfterSubItemEventArgs e = new AfterSubItemEventArgs(this.SelectedItems[0], m_editSubItemIndex, this.SelectedItems[0].SubItems[m_editSubItemIndex].Text, m_textbox.Text, false);
OnAfterSubItemEdit(e);
if (e.Cancel)
{
//....
}
else
{
//set new value
}
m_textbox.Visible = false;
m_editSubItemIndex = -1;
}
OnAfterSubItemEdit is a event that user can do some validations or other operations. I add a check in this method, if the new value exist, I will show a messagebox to user firstly, then hide the textbox. But now, the problem comes, when i move the mouse, the listview items can be selected, I don't how to solve this issue, I tried my best to find out the way, but failed. So, please help me!
Listview has a LabelEdit property; when you set it "true", then in an event handler you can call Listview.Items[x].BeginEdit(), and edit an item. As an example, you can handle ListView.DoubleClick event and call BeginEdit right there:
private void Form1_Load(object sender, System.EventArgs e)
{
listView1.LabelEdit = true;
}
private void listView1_DoubleClick(object sender, System.EventArgs e)
{
if(this.listView1.SelectedItems.Count==1)
{
this.listView1.SelectedItems[0].BeginEdit();
}
}
The problem is that your form still calls the DoubleClick event whether the value exists or not. Add appropriate condition before calling base DoubleClick in your code, i.e.:
if(!new value exists)
base.OnDoubleClick(args);
Is there a way to allow Drag and Drop anywhere in a form full of controls?
The idea is to allow user to drag a file anywhere in a form in order to "load" it. I will not need any other DragDrop behavior but this.
By setting AllowDrop=True to the form only, I get DragEnter events but not DragDrop ones.
An idea would be to make a topmost panel visible on DragEnter and handle DragDrop events there, but I wonder if I miss something obvious here since I have little experience in the field.
Another Idea would be to iterate through all controls and subscribe to Drag-related events. I really don't like this approach, though.
Sure, iterating the controls will work, it doesn't take much code:
public Form1() {
InitializeComponent();
WireDragDrop(this.Controls);
}
private void WireDragDrop(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
ctl.AllowDrop = true;
ctl.DragEnter += ctl_DragEnter;
ctl.DragDrop += ctl_DragDrop;
WireDragDrop(ctl.Controls);
}
}
void ctl_DragDrop(object sender, DragEventArgs e) {
// etc..
}
void ctl_DragEnter(object sender, DragEventArgs e) {
// etc..
}
If you still don't like the approach then use a recognizable single drop target that the user will always hit. Could be as simple as a label that says "Drop here".
I'm not sure what kinds of control you have on your form. But I've tested with a Button, a GroupBox, a PictureBox and a TextBox. All these controls have AllowDrop = false by default. And I can drag-n-drop something from outside onto the form OK. The DragDrop is fired OK. Everything is OK. What is actually your problem? I guess your controls have AllowDrop = true.
In the case the DragDrop event is not fired (which I think only happens if the target is one of your Control with AllowDrop = true). I think the following may work. But if the target is one of your Control with AllowDrop = true, the effect icon is gone away.
public Form1(){
InitializeComponents();
t.Interval = 1;
t.Tick += Tick;
}
IDataObject data;
Timer t = new Timer();
int i = 0;
private void Tick(object sender, EventArgs e)
{
Text = (i++).ToString();
if (ClientRectangle.Contains(PointToClient(new Point(MousePosition.X, MousePosition.Y))) && MouseButtons == MouseButtons.None)
{
t.Stop();
if (data != null)
{
//Process data here
//-----------------
data = null;
}
}
else if (MouseButtons == MouseButtons.None)
{
data = null;
t.Stop();
}
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
if (data == null)
{
data = e.Data;
t.Start();
}
}
And I think you may have to use the loop through all the Controls to add appropriate event handlers. There is no other better way.
In the Drop event.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string file in files) Console.WriteLine(file);
In the DragEnter event.
if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effects = DragDropEffects.Copy;
I've got a TreeView and Canvas in my WPF application. I'm trying to implement functionality whereby users can drag a TreeViewItem and a method should be called when the user drops on the canvas, passing the TreeViewItem header as a parameter to this method.
This is what I've done so far:
private void TreeViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.Source.GetType().Name.Equals("TreeViewItem"))
{
TreeViewItem item = (TreeViewItem)e.Source;
if (item != null)
{
DataObject dataObject = new DataObject();
dataObject.SetData(DataFormats.StringFormat, item.Header.ToString());
DragDrop.DoDragDrop(item, dataObject, DragDropEffects.Copy);
}
}
}
When I drag and drop to the canvas nothing happens. I am thus unsure of what I should do next. I feel that it's something really small, but I'm at a loss. How can I call the method and detect that the header has been dropped?
Any ideas?
You need to set AllowDrop to true on your target element and then handle DragOver and Drop events on the target element.
Example:
private void myElement_DragOver(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(MyDataType)))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
}
private void myElement_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(MyDataType)))
{
// do whatever you want do with the dropped element
MyDataType droppedThingie = e.Data.GetData(typeof(MyDataType)) as MyDataType;
}
}
am doing something very very simple.
I have a listbox whose events are set like this :
public Form1()
{
InitializeComponent();
this.listBox1.AllowDrop = true;
this.listBox1.DragEnter += new DragEventHandler(listBox1_DragEnter);
this.listBox1.DragDrop += new DragEventHandler(listBox1_DragDrop);
}
void listBox1_DragDrop(object sender, DragEventArgs e)
{
//code to add labelText to Items of ListBox
}
void listBox1_DragEnter(object sender, DragEventArgs e)
{
//set DragDropEffects;
}
now I have a label, code for which is as follows:
private void label1_MouseDown(object sender, MouseEventArgs e)
{
DoDragDrop((sender as Label).Text, DragDropEffects.Copy);
//this.label1.DoDragDrop((sender as Label).Text, DragDropEffects.Copy);
//used one of them at a time.
}
but nothing happens. listbox DragEnter event never fires up. in fact, drag never happens.
whenever i try to drag label (text), not allowed windows cursor appears, instead of 'DragDropEffects.Copy's cursor
Drag and Drop doesn't take place..
when I modify the listbox (and the associated code) to accept files to be dropped on it from any other window, that works perfectly.
so..am unable to perform drag from a control kept on the form to another control kept on the same form.
am I missing something? am running windows XP.
I went through this and through this
please help...
Your code does work actually.
You just have to set the right drag effects in your event handlers.
void listBox1_DragDrop(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
void listBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
Check if ListBox.AllowDrop is set to TRUE or not
The following is an example of what you need, with all the code (adding it here for whoever finds this post).
#region Initial Values
//Constructor:
public Form1() {
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e ) {
InitialValues();
}
private void InitialValues() {
PrepareDragAndDrop();
}
#endregion Initial Values
#region Drag & Drop
private void PrepareDragAndDrop() {
//For the object that receives the other dragged element:
TheSamplListBox.AllowDrop = true;
TheSamplListBox.DragEnter += TheSamplListBox_DragEnter;
TheSamplListBox.DragLeave += TheSamplListBox_DragLeave;
TheSamplListBox.DragDrop += TheSamplListBox_DragDrop;
//For the object that will be dragged:
TheSampleLabel.MouseDown += ( sender, args ) => DoDragDrop( TheSampleLabel.Text, DragDropEffects.Copy );
}
private void TheSamplListBox_DragEnter( object theReceiver, DragEventArgs theEventData ) {
theEventData.Effect = DragDropEffects.Copy;
//Only the code above is strictly for the Drag & Drop. The following is for user feedback:
//You can use [TheSamplListBox] but this approach allows for multiple receivers of the same type:
var theReceiverListBox = (ListBox) theReceiver;
theReceiverListBox.BackColor = Color.LightSteelBlue;
}
private void TheSamplListBox_DragLeave( object theReceiver, EventArgs theEventData ) {
//No code here for the Drag & Drop. The following is for user feedback:
//You can use [TheSamplListBox] but this approach allows for multiple receivers of the same type:
var theReceiverListBox = (ListBox) theReceiver;
theReceiverListBox.BackColor = Color.White;
}
private void TheSamplListBox_DragDrop( object theReceiver, DragEventArgs theEventData ) {
//You can use [TheSamplListBox] but this approach allows for multiple receivers of the same type:
var theReceiverListBox = (ListBox) theReceiver;
//Get the data being dropped. In this case, a string:
var theStringBeingDropped = theEventData.Data.GetData( "System.String" );
//Add the string to the ListBox:
theReceiverListBox.Items.Add( theStringBeingDropped );
//Only the code above is strictly for the Drag & Drop. The following is for user feedback:
theReceiverListBox.BackColor = Color.White;
}
#endregion Drag & Drop
.
So I have an objectlistview (actually a treelistview). I want to be able to drag an item from this onto a richtextbox, and have it insert a property of the dragged item (in this case Default_Heirarchy_ID)
The TreeListView's objectmodel is a List<T> of a class called SpecItem.
This is what I have so far:
public frmAutospecEditor(SpecItem siThis_, List<SpecItem> lstStock_)
{
InitializeComponent();
txtFormula.DragEnter += new DragEventHandler(txtFormula_DragEnter);
txtFormula.DragDrop += new DragEventHandler(txtFormula_DragDrop);
...
}
void txtFormula_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void tlvSpecItem_ItemDrag(object sender, ItemDragEventArgs e)
{
int intID = ((SpecItem)tlvSpecItem.GetItem(tlvSpecItem.SelectedIndex).RowObject).Default_Heirarchy_ID ??0;
DoDragDrop(intID, DragDropEffects.Copy);
}
private void txtFormula_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
object objID = e.Data.GetData(typeof(String));
//this is where it goes wrong - no matter what I try to do with this, it
//always returns either null, or the text displayed for that item in the TreeListView,
//NOT the ID as I want it to.
string strID = (string)objID;
txtFormula.Text = strID;
}
Where am I going wrong?
Cheers
The Drag is the control you want to take data from (your OLV).
The Drop is the destination control (your textbox). So:
Set the IsSimpleDragSource property of your OLV to true.
In the textbox set AllowDrop property to true. Then handle the DragEnter event of your textbox and use the DragEventArgs param.
Handle the ModelDropped event:
private void yourOlv_ModelDropped(object sender, ModelDropEventArgs e)
{
// If they didn't drop on anything, then don't do anything
if (e.TargetModel == null) return;
// Use the dropped data:
// ((SpecItem)e.TargetModel)
// foreach (SpecItem si in e.SourceModels) ...
// e.RefreshObjects();
}
Read more: http://objectlistview.sourceforge.net/cs/dragdrop.html#ixzz1lEt7LoGr