I have a winform with a listbox and a treeview.
Once my listbox is filled with items, I want to drag them (multiple or single) from the listbox and drop them in a node in the treeview.
If somebody has a good example in C# that would be great.
It's been a while since I've messed with Drag/Drop so I figured I'll write a quick sample.
Basically, I have a form, with a listbox on the left, and a treeview on the right. Then I put a button on top. When the button is clicked, it just puts the date of the next ten days into the list box. It also populates the TreeView with 2 parents nodes and two child nodes. Then, you just have to handle all the subsequent drag/drop events to make it work.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.treeView1.AllowDrop = true;
this.listBox1.AllowDrop = true;
this.listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
this.listBox1.DragOver += new DragEventHandler(listBox1_DragOver);
this.treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
this.treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
}
private void button1_Click(object sender, EventArgs e)
{
this.PopulateListBox();
this.PopulateTreeView();
}
private void PopulateListBox()
{
for (int i = 0; i <= 10; i++)
{
this.listBox1.Items.Add(DateTime.Now.AddDays(i));
}
}
private void PopulateTreeView()
{
for (int i = 1; i <= 2; i++)
{
TreeNode node = new TreeNode("Node" + i);
for (int j = 1; j <= 2; j++)
{
node.Nodes.Add("SubNode" + j);
}
this.treeView1.Nodes.Add(node);
}
}
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
TreeNode nodeToDropIn = this.treeView1.GetNodeAt(this.treeView1.PointToClient(new Point(e.X, e.Y)));
if (nodeToDropIn == null) { return; }
if(nodeToDropIn.Level > 0)
{
nodeToDropIn = nodeToDropIn.Parent;
}
object data = e.Data.GetData(typeof(DateTime));
if (data == null) { return; }
nodeToDropIn.Nodes.Add(data.ToString());
this.listBox1.Items.Remove(data);
}
private void listBox1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
this.listBox1.DoDragDrop(this.listBox1.SelectedItem, DragDropEffects.Move);
}
}
You want to use the GetItemAt(Point point) function to translate X,Y location to the listview item.
Here's quite good article about it: Drag and Drop Using C#.
To make the item being dragged visible while dragging, you need to use COM ImageList, which is well described in the following article Custom Drag-Drop Images Using ImageLists.
Related
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;
}
Let's say I have a ListView on a form and it is populated with records.
How can I do this : when I click (single click) on a row , something has to happen - for example MessageBox.Show("row selected");
How to make this happen? Do I need a mouse click event ? And how can I do this?
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedItemText = (listBox1.SelectedItem ?? "(none)").ToString();
MessageBox.Show("Selected: " + selectedItemText);
}
private void listBox1_MouseClick(object sender, MouseEventArgs e)
{
for (int i = 0; i < listBox1.Items.Count; i++)
{
var rectangle = listBox1.GetItemRectangle(i);
if (rectangle.Contains(e.Location))
{
MessageBox.Show("Item " + i);
return;
}
}
MessageBox.Show("None");
}
#Tommy answer is for ListBox, this one is for ListView :
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
for (int i = 0; i < listView1.Items.Count; i++)
{
var rectangle = listView1.GetItemRect(i);
if (rectangle.Contains(e.Location))
{
//Write your code here
return;
}
}
}
To prevent unwished behavior on ListView with checkboxes my solution is:
private void lvMembers_MouseClick(object sender, MouseEventArgs e)
{
for (int itemIndex = 0; itemIndex < lvMembers.Items.Count; itemIndex++)
{
ListViewItem item = lvMembers.Items[itemIndex];
Rectangle itemRect = item.GetBounds(ItemBoundsPortion.Label);
if (itemRect.Contains(e.Location))
{
item.Checked = !item.Checked;
break;
}
}
}
If you want to select listview item on mouse click over it try this.
private void timeTable_listView_MouseUp(object sender, MouseEventArgs e)
{
Point mousePos = timeTable_listView.PointToClient(Control.MousePosition);
ListViewHitTestInfo hitTest = timeTable_listView.HitTest(mousePos);
try
{
int columnIndex = hitTest.Item.SubItems.IndexOf(hitTest.SubItem);
edit_textBox.Text = timeTable_listView.SelectedItems[0].SubItems[columnIndex].Text;
}
catch(Exception)
{
}
}
I want to allow user to drag any item from MenuStrip to a ListBox.
I did it between to ListBoxes, but can not do it with MenuStrip.
Thanks a lot for your help.
I use WinForms, C#
For the destination ListBox I modified its property
this.listBox2.AllowDrop = true;
and created the following two events:
private void listBox2_DragOver(
object sender, System.Windows.Forms.DragEventArgs e)
{
e.Effect=DragDropEffects.All;
}
private void listBox2_DragDrop(
object sender, System.Windows.Forms.DragEventArgs e)
{
if(e.Data.GetDataPresent(DataFormats.StringFormat))
{
string str= (string)e.Data.GetData(
DataFormats.StringFormat);
listBox2.Items.Add(str);
}
}
What I need is what should be done to the source MenuStrip to allow drag items from it the ListBox, in over words how to make MenuStrip draggable.
Thanks to all for their help.
I found the solution:
The missing event is that I should add event to ToolStripMenuItem_MouseDown, I prefer to use right click instead of left click to avoid the conflict between ToolStripMenuItem_Click and the drag event, this the code:
AllowDrop = true;
private void tsmi_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
DoDragDrop(sender, System.Windows.Forms.DragDropEffects.Copy);
}
Add also this code to the ListView:
private void lvAllowDropListView_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
System.Windows.Forms.ToolStripMenuItem button = e.Data.GetData(typeof(System.Windows.Forms.ToolStripMenuItem))
as System.Windows.Forms.ToolStripMenuItem;
if (button != null)
{
try
{
SmallImageList = sysIcons.SmallIconsImageList;
LargeImageList = sysIcons.LargeIconsImageList;
System.Windows.Forms.ToolStripMenuItem item = e.Data.GetData(typeof(System.Windows.Forms.ToolStripMenuItem))
as System.Windows.Forms.ToolStripMenuItem;
if (item != null)
{
AddToolStripMenuItem(item.Text, item.Name);
}
}
catch { }
}
}
private void AddToolStripMenuItem(string name, string tag)
{
System.Windows.Forms.ListViewItem item = new System.Windows.Forms.ListViewItem(name);
int Index = -1;
for (int i = 0; i < Items.Count;i++ )
if(Items[i].Tag.ToString() == tag)
{
Index = i;
break;
}
if (Index == -1)
{
item.Tag = tag;
Items.Add(item);
}
}
Drag Menu Strip Item is the same like ListBox item.
Check your code...
I just implemented a drag drop reordering function for a CheckedListBox. Now I want it to scroll down if dragging outside the bottom and vice versa at the top (a normal dragdrop autoscroll)
I've found loads of WPF info, but I don't see how I can apply those solutions to my winform ChekedListBox.
Here's my code:
private void myListBox_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
Point point = myListBox.PointToClient(new Point(e.X, e.Y));
int index = myListBox.IndexFromPoint(point);
int selectedIndex = myListBox.SelectedIndex;
if (index < 0)
{
index = selectedIndex;
}
if (index != selectedIndex)
{
myListBox.SwapItems(selectedIndex, index);
myListBox.SelectedIndex = index;
}
}
You can update the CheckedListBox.TopIndex property within the Timer.Tick event handler to implement the auto-scroll feature. To start and stop the timer, use the CheckedListBox.DragLeave and CheckedListBox.DragEnter events. Here is a code snippet:
private void checkedListBox1_DragEnter(object sender, DragEventArgs e) {
scrollTimer.Stop();
}
private void checkedListBox1_DragLeave(object sender, EventArgs e) {
scrollTimer.Start();
}
private void scrollTimer_Tick(object sender, EventArgs e) {
Point cursor = PointToClient(MousePosition);
if (cursor.Y < checkedListBox1.Bounds.Top)
checkedListBox1.TopIndex -= 1;
else if (cursor.Y > checkedListBox1.Bounds.Bottom)
checkedListBox1.TopIndex += 1;
}
I actually ended up adding this to my DragOver event handler instead. Maybe not as slick, but it works a bit better for me.
private void myListBox_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
Point point = myListBox.PointToClient(new Point(e.X, e.Y));
int index = myListBox.IndexFromPoint(point);
int selectedIndex = myListBox.SelectedIndex;
if (index < 0)
{
index = selectedIndex;
}
if (index != selectedIndex)
{
myListBox.SwapItems(selectedIndex, index);
myListBox.SelectedIndex = index;
}
if (point.Y <= (Font.Height*2))
{
myListBox.TopIndex -= 1;
}
else if (point.Y >= myListBox.Height - (Font.Height*2))
{
myListBox.TopIndex += 1;
}
}
Is it possible to reorder the tabs in the WinForms TabControl at run-time like IE or Firefox?
Links like this don't give me much hope.
Sure, it's possible! You're most likely trying to overcomplicate the solution. Essentially, all you have to do is subclass the standard TabControl and add some logic to the mouse event handlers. You'll just need to check which form the user is currently dragging and reorder it in the TabPages collection.
There are a couple of complete solutions available online:
Reordering TabPages inside TabControl
Drag and Drop Tab Control
Reposition TabItems at runtime
I found the solution originally posted by #Cody Gray to be mostly what I wanted, but I didn't see the need for it to be so complicated.
This is my simplification, implemented by deriving from TabControl:
public class DraggableTabControl : TabControl
{
private TabPage m_DraggedTab;
public DraggableTabControl()
{
MouseDown += OnMouseDown;
MouseMove += OnMouseMove;
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
m_DraggedTab = TabAt(e.Location);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || m_DraggedTab == null)
{
return;
}
TabPage tab = TabAt(e.Location);
if (tab == null || tab == m_DraggedTab)
{
return;
}
Swap(m_DraggedTab, tab);
SelectedTab = m_DraggedTab;
}
private TabPage TabAt(Point position)
{
int count = TabCount;
for (int i = 0; i < count; i++)
{
if (GetTabRect(i).Contains(position))
{
return TabPages[i];
}
}
return null;
}
private void Swap(TabPage a, TabPage b)
{
int i = TabPages.IndexOf(a);
int j = TabPages.IndexOf(b);
TabPages[i] = b;
TabPages[j] = a;
}
}
The drag and drop APIs are really intended for dragging stuff between separate applications, or at the very least, separate controls. Using them in this case is overkill.
Make sure you upvote Cody's answer too if you upvote mine, as it is based on his.
reordering TabPages with drag and drop - by Ludwig B.
inspired by http://dotnetrix.co.uk/tabcontrol.htm#tip7
private void tc_MouseDown(object sender, MouseEventArgs e)
{
// store clicked tab
TabControl tc = (TabControl)sender;
int hover_index = this.getHoverTabIndex(tc);
if (hover_index >= 0) { tc.Tag = tc.TabPages[hover_index]; }
}
private void tc_MouseUp(object sender, MouseEventArgs e)
{
// clear stored tab
TabControl tc = (TabControl)sender;
tc.Tag = null;
}
private void tc_MouseMove(object sender, MouseEventArgs e)
{
// mouse button down? tab was clicked?
TabControl tc = (TabControl)sender;
if ((e.Button != MouseButtons.Left) || (tc.Tag == null)) return;
TabPage clickedTab = (TabPage)tc.Tag;
int clicked_index = tc.TabPages.IndexOf(clickedTab);
// start drag n drop
tc.DoDragDrop(clickedTab, DragDropEffects.All);
}
private void tc_DragOver(object sender, DragEventArgs e)
{
TabControl tc = (TabControl)sender;
// a tab is draged?
if (e.Data.GetData(typeof(TabPage)) == null) return;
TabPage dragTab = (TabPage)e.Data.GetData(typeof(TabPage));
int dragTab_index = tc.TabPages.IndexOf(dragTab);
// hover over a tab?
int hoverTab_index = this.getHoverTabIndex(tc);
if (hoverTab_index < 0) { e.Effect = DragDropEffects.None; return; }
TabPage hoverTab = tc.TabPages[hoverTab_index];
e.Effect = DragDropEffects.Move;
// start of drag?
if (dragTab == hoverTab) return;
// swap dragTab & hoverTab - avoids toggeling
Rectangle dragTabRect = tc.GetTabRect(dragTab_index);
Rectangle hoverTabRect = tc.GetTabRect(hoverTab_index);
if (dragTabRect.Width < hoverTabRect.Width)
{
Point tcLocation = tc.PointToScreen(tc.Location);
if (dragTab_index < hoverTab_index)
{
if ((e.X - tcLocation.X) > ((hoverTabRect.X + hoverTabRect.Width) - dragTabRect.Width))
this.swapTabPages(tc, dragTab, hoverTab);
}
else if (dragTab_index > hoverTab_index)
{
if ((e.X - tcLocation.X) < (hoverTabRect.X + dragTabRect.Width))
this.swapTabPages(tc, dragTab, hoverTab);
}
}
else this.swapTabPages(tc, dragTab, hoverTab);
// select new pos of dragTab
tc.SelectedIndex = tc.TabPages.IndexOf(dragTab);
}
private int getHoverTabIndex(TabControl tc)
{
for (int i = 0; i < tc.TabPages.Count; i++)
{
if (tc.GetTabRect(i).Contains(tc.PointToClient(Cursor.Position)))
return i;
}
return -1;
}
private void swapTabPages(TabControl tc, TabPage src, TabPage dst)
{
int index_src = tc.TabPages.IndexOf(src);
int index_dst = tc.TabPages.IndexOf(dst);
tc.TabPages[index_dst] = src;
tc.TabPages[index_src] = dst;
tc.Refresh();
}
I extended the answer of Jacob Stanley a bit. This way the swapping won't occur too often. This is especially helpful for tabs of different sizes in which case the previous solution would swap very often while dragging.
The difference in user experience is that you have to drag a bit further to actually move the tab. But this is similar to tab reordering in browsers.
Also I added a hand cursor while dragging and enabled double-buffering.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Controls
{
public class DraggableTabControl : TabControl
{
private TabPage draggedTab;
public DraggableTabControl()
{
this.MouseDown += OnMouseDown;
this.MouseMove += OnMouseMove;
this.Leave += new System.EventHandler(this.DraggableTabControl_Leave);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
draggedTab = TabAt(e.Location);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || draggedTab == null)
{
this.Cursor = this.DefaultCursor;
draggedTab = null;
return;
}
int index = TabPages.IndexOf(draggedTab);
int nextIndex = index + 1;
int prevIndex = index - 1;
int minXForNext = int.MaxValue;
int maxXForPrev = int.MinValue;
var tabRect = GetTabRect(index);
if (nextIndex < TabPages.Count)
{
var nextTabRect = GetTabRect(nextIndex);
minXForNext = tabRect.Left + nextTabRect.Width;
}
if (prevIndex >= 0)
{
var prevTabRect = GetTabRect(prevIndex);
maxXForPrev = prevTabRect.Left + tabRect.Width;
}
this.Cursor = Cursors.Hand;
if (e.Location.X > maxXForPrev && e.Location.X < minXForNext)
{
return;
}
TabPage tab = TabAt(e.Location);
if (tab == null || tab == draggedTab)
{
return;
}
Swap(draggedTab, tab);
SelectedTab = draggedTab;
}
private TabPage TabAt(Point position)
{
int count = TabCount;
for (int i = 0; i < count; i++)
{
if (GetTabRect(i).Contains(position))
{
return TabPages[i];
}
}
return null;
}
private void Swap(TabPage a, TabPage b)
{
int i = TabPages.IndexOf(a);
int j = TabPages.IndexOf(b);
TabPages[i] = b;
TabPages[j] = a;
}
private void DraggableTabControl_Leave(object sender, EventArgs e)
{
this.Cursor = this.DefaultCursor;
draggedTab = null;
}
}
}