C#拖放:拖动时显示拖动的项目

我正在使用Windows Forms在C#中构建桌面应用程序。 我有一个自定义控件,我希望能够在我的应用程序中拖放它(不在外面)。 现在我正在使用通常的DoDragDrop / OnDragOver / OnDragDrop方法实现它。 是否有任何方法可以连续绘制控件,因为它被拖动 – 有点你用JQuery的拖放看到的? 我希望实际控件保持原位,但我想在用户拖动时绘制其外观的副本。 理想情况下,副本甚至可以是半透明的,但这更像是“很高兴”。

我能想到的唯一方法是将绘图代码放在主窗体的OnPaint方法中,但这似乎是一个不优雅的解决方案。 还有其他想法吗? 如果Control将自己描绘成一个Bitmap,事情会变得更容易吗?

我以为我应该回来自己回答这个问题,因为我最终确实得到了它。

我用这些函数创建了一个CursorUtil类:

public struct IconInfo { public bool fIcon; public int xHotspot; public int yHotspot; public IntPtr hbmMask; public IntPtr hbmColor; } public class CursorUtil { [DllImport("user32.dll")] public static extern IntPtr CreateIconIndirect(ref IconInfo icon); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo); [DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr handle); [DllImport("user32.dll", CharSet = CharSet.Auto)] extern static bool DestroyIcon(IntPtr handle); // Based on the article and comments here: // http://www.switchonthecode.com/tutorials/csharp-tutorial-how-to-use-custom-cursors // Note that the returned Cursor must be disposed of after use, or you'll leak memory! public static Cursor CreateCursor(Bitmap bm, int xHotspot, int yHotspot) { IntPtr cursorPtr; IntPtr ptr = bm.GetHicon(); IconInfo tmp = new IconInfo(); GetIconInfo(ptr, ref tmp); tmp.xHotspot = xHotspot; tmp.yHotspot = yHotspot; tmp.fIcon = false; cursorPtr = CreateIconIndirect(ref tmp); if (tmp.hbmColor != IntPtr.Zero) DeleteObject(tmp.hbmColor); if (tmp.hbmMask != IntPtr.Zero) DeleteObject(tmp.hbmMask); if (ptr != IntPtr.Zero) DestroyIcon(ptr); return new Cursor(cursorPtr); } public static Bitmap AsBitmap(Control c) { Bitmap bm = new Bitmap(c.Width, c.Height); c.DrawToBitmap(bm, new Rectangle(0, 0, c.Width, c.Height)); return bm; } 

然后我写了一个Drag类(也不是面向对象的,唉,但我认为你只能在桌面应用程序中一次拖动一件事)。 这是一些代码:

  public static void StartDragging(Control c) { Dragged = c; DisposeOldCursors(); Bitmap bm = CursorUtil.AsBitmap(c); DragCursorMove = CursorUtil.CreateCursor((Bitmap)bm.Clone(), DragStart.X, DragStart.Y); DragCursorLink = CursorUtil.CreateCursor((Bitmap)bm.Clone(), DragStart.X, DragStart.Y); DragCursorCopy = CursorUtil.CreateCursor(CursorUtil.AddCopySymbol(bm), DragStart.X, DragStart.Y); DragCursorNo = CursorUtil.CreateCursor(CursorUtil.AddNoSymbol(bm), DragStart.X, DragStart.Y); //Debug.WriteLine("Starting drag"); } // This gets called once when we move over a new control, // or continuously if that control supports dropping. public static void UpdateCursor(object sender, GiveFeedbackEventArgs fea) { //Debug.WriteLine(MainForm.MousePosition); fea.UseDefaultCursors = false; //Debug.WriteLine("effect = " + fea.Effect); if (fea.Effect == DragDropEffects.Move) { Cursor.Current = DragCursorMove; } else if (fea.Effect == DragDropEffects.Copy) { Cursor.Current = DragCursorCopy; } else if (fea.Effect == DragDropEffects.None) { Cursor.Current = DragCursorNo; } else if (fea.Effect == DragDropEffects.Link) { Cursor.Current = DragCursorLink; } else { Cursor.Current = DragCursorMove; } } 

您可以在设置控件时使用这些方法,例如在构造函数中:

 GiveFeedback += new GiveFeedbackEventHandler(Drag.UpdateCursor); 

并在这种方法:

  protected override void OnMouseMove(MouseEventArgs mea) { if (Drag.IsDragging(mea)) { Drag.StartDragging(this); DragDropEffects dde = DoDragDrop(Plan, DragDropEffects.Move | DragDropEffects.Copy); Drag.StopDragging(); } } 

这可能是一个选择:

 private void btntarget_MouseDown(object sender, MouseEventArgs e) { Bitmap bmp = new Bitmap(btntarget.Width, btntarget.Height); btntarget.DrawToBitmap(bmp, new Rectangle(Point.Empty, bmp.Size)); //optionally define a transparent color bmp.MakeTransparent(Color.White); Cursor cur = new Cursor(bmp.GetHicon()); Cursor.Current = cur; } 

光标的热点将在图像中间创建

这通常由GiveFeedback事件处理。

将ImageList拖到表单上并使用图像列表function移动它:

  1. 创建一个控件大小的Bitmap对象(但不超过256×256)。
  2. 将控件的图像复制到Bitmap中:using(Graphics gfx = Graphics.FromImage(bmp)){gfx.CopyFromScreen(…)}
  3. 将其添加到您的ImageList。
  4. 调用ImageList_BeginDrag()。
  5. 调用DoDragDrop()
  6. 在OnDragMove处理程序中,调用ImageList_DragMove()以在鼠标移动时移动它。
  7. 当DoDragDrop返回时,调用ImageList_DragLeave()。

我一直在调用ImageList_DragEnter()和ImageList_DragLeave(),它似乎将ImageList_DragMove()使用的坐标转换为客户端坐标,但我阅读文档表明没有必要。