A ways is with the Drag List Box feature from Common Controls
A quick test with a ListBox listBox1, to be improved, for example with a User Control :
public partial class Form1 : Form
{
[DllImport("Comctl32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool MakeDragList(IntPtr hLB);
[DllImport("Comctl32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern void DrawInsert(IntPtr handParent, IntPtr hLB, int nItem);
[DllImport("Comctl32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int LBItemFromPt(IntPtr hLB, POINT pt, bool bAutoScroll);
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint RegisterWindowMessage(string lpString);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DRAGLISTINFO
{
public uint uNotification;
public IntPtr hWnd;
public POINT ptCursor;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
public const int WM_USER = 0x400;
public const int DL_BEGINDRAG = (WM_USER + 133);
public const int DL_DRAGGING = (WM_USER + 134);
public const int DL_DROPPED = (WM_USER + 135);
public const int DL_CANCELDRAG = (WM_USER + 136);
public const int DL_CURSORSET = 0;
public const int DL_STOPCURSOR = 1;
public const int DL_COPYCURSOR = 2;
public const int DL_MOVECURSOR = 3;
uint nMessage = 0;
int nDragItem = -1;
string sDragItem = null;
public Form1()
{
InitializeComponent();
bool bRet = MakeDragList(listBox1.Handle);
if (bRet)
{
nMessage = RegisterWindowMessage("commctrl_DragListMsg");
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == nMessage)
{
DRAGLISTINFO dli = (DRAGLISTINFO)Marshal.PtrToStructure(m.LParam, typeof(DRAGLISTINFO));
if (dli.uNotification == DL_BEGINDRAG)
{
nDragItem = LBItemFromPt(listBox1.Handle, dli.ptCursor, true);
sDragItem = listBox1.Items[nDragItem].ToString();
m.Result = (IntPtr)1;
}
else if (dli.uNotification == DL_DRAGGING)
{
int nItem = LBItemFromPt(listBox1.Handle, dli.ptCursor, true);
if (nItem != -1)
{
DrawInsert(m.HWnd, listBox1.Handle, nItem);
}
if (nItem != -1)
m.Result = (IntPtr)DL_MOVECURSOR;
else
m.Result = (IntPtr)DL_STOPCURSOR;
}
else if (dli.uNotification == DL_CANCELDRAG)
DrawInsert(m.HWnd, listBox1.Handle, -1);
else if (dli.uNotification == DL_DROPPED)
{
int nItem = LBItemFromPt(listBox1.Handle, dli.ptCursor, true);
if (nItem != -1)
{
DrawInsert(m.HWnd, listBox1.Handle, -1);
if (nDragItem != nItem)
{
if (nItem >= nDragItem)
{
listBox1.Items.RemoveAt(nDragItem);
listBox1.Items.Insert(nItem - 1, sDragItem);
listBox1.SelectedIndex = nItem - 1;
}
else
{
listBox1.Items.RemoveAt(nDragItem);
listBox1.Items.Insert(nItem, sDragItem);
listBox1.SelectedIndex = nItem;
}
}
}
nDragItem = -1;
DrawInsert(m.HWnd, listBox1.Handle, -1);
m.Result = (IntPtr)DL_CURSORSET;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
for (int x = 1; x <= 50; x++)
{
listBox1.Items.Add("Item " + x.ToString());
}
}
}