Autoscroll CheckedListBox with DragDrop reordering - c#

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;
}
}

Related

C# Drag and Drop in Listview doesn't work with Groups

I have a Listview with Drag and Drop codes working well.
However, after i add groups to it, the Drag and Drop codes are run, but the outcome is not what the codes used to behave.
Dropped items were only added to the tail of its group.
Why? and how to get it work properly with groups?
The Drag and Drop procedures are given herebelow:
private void lvQ_DragDrop(object sender, DragEventArgs e)
{
int targetIndex = lvQ.InsertionMark.Index;
if (targetIndex == -1)
return;
if (lvQ.InsertionMark.AppearsAfterItem)
targetIndex++;
ListViewItem draggedItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
if (targetIndex == 0 && lvQ.Items.OfType<ListViewItem>().FirstOrDefault()?.SubItems[1].Text == "FF")
{
lvQ.InsertionMark.Index = -1;
}
else
{
lvQ.BeginUpdate();
lvQ.Items.Insert(targetIndex, (ListViewItem)draggedItem.Clone());
lvQ.Items.Remove(draggedItem);
lvQ.EndUpdate();
ShowTXFrameStr();
}
}
private void lvQ_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void lvQ_DragLeave(object sender, EventArgs e)
{
lvQ.InsertionMark.Index = -1;
}
private void lvQ_DragOver(object sender, DragEventArgs e)
{
Point ptScreen = new(e.X, e.Y);
Point pt = lvQ.PointToClient(ptScreen);
int targetIndex = lvQ.InsertionMark.NearestIndex(pt);
if (targetIndex > -1)
{
Rectangle itemBounds = lvQ.GetItemRect(targetIndex);
lvQ.InsertionMark.AppearsAfterItem = pt.Y > itemBounds.Top + (itemBounds.Height / 2);
}
lvQ.InsertionMark.Index = targetIndex;
}
private void lvQ_ItemDrag(object sender, ItemDragEventArgs e)
{
lvQ.DoDragDrop(e.Item, DragDropEffects.Move);
}

How to display the ContextMenuStrip when item is selected in a list box c# .net

I'm trying select an item from list box when is right clicked and show the ContextMenuStrip to display my options available, but when I click everywhere in the control (list box) is showing the ContextMenuStrip.
This is what I have in code:
private void lbSMTPEmails_MouseDown(object sender, MouseEventArgs e)
{
int SelectedIndex = lbSMTPEmails.IndexFromPoint(e.X, e.Y);
if (SelectedIndex == -1)
lbSMTPEmails.ContextMenuStrip.Hide();
else
{
lbSMTPEmails.SelectedIndex = SelectedIndex;
lbSMTPEmails.ContextMenuStrip.Show();
}
}
do you have any idea how to solve this?
Use opening event of ContextMenuStrip
void cms_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
int SelectedIndex = lbSMTPEmails.IndexFromPoint( lbSMTPEmails.PointToClient(Cursor.Position) );
if (SelectedIndex == -1)
e.Cancel = true;
else
{
lbSMTPEmails.SelectedIndex = SelectedIndex;
}
}
I did by this way and it worked!
private void listbox_MouseDown(object sender, MouseEventArgs e)
{
ShowMenuStrip = listbox.IndexFromPoint(e.Location) >= 0; //This is a global bool variable
if (ShowMenuStrip)
listbox.SelectedIndex = listbox.IndexFromPoint(e.Location);
else
listbox.SelectedIndex = -1;
}
private void ContextMenuStrip_Opening(object sender, CancelEventArgs e)
{
e.Cancel = !ShowMenuStrip;
}

C# - ListView : How to handle the mouse click event on a listViewItem?

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)
{
}
}

Is it possible to make the WinForms Tab Control be able to do tab reordering like IE or Firefox?

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;
}
}
}

C# Drag & drop from listbox to treeview

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.

Categories

Resources