我正在使用Windows窗体在C#中构建桌面应用程序。我有一个自定义控件,我希望能够在我的应用程序中拖放它(不在外面)。现在我用通常的DoDragDrop / OnDragOver / OnDragDrop方法实现它。是否有任何方法可以连续绘制控件,因为它被拖动 - 有点你用JQuery的拖放看到的?我希望实际控件保持原位,但我想在用户拖动时绘制其外观的副本。理想情况下,副本甚至可以是半透明的,但这更像是“很高兴”。
我能想到的唯一方法是将绘图代码放在主窗体的OnPaint方法中,但这似乎是一个不优雅的解决方案。还有其他想法吗?如果Control将自己描绘成一个Bitmap,事情会变得更容易吗?
答案 0 :(得分:15)
我想我应该回来自己回答这个问题,因为我最终确实这样做了。
我用这些函数创建了一个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();
}
}
答案 1 :(得分:6)
这可能是一个选择:
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;
}
光标的热点将在图像中间创建
答案 2 :(得分:2)
这通常由GiveFeedback
事件处理。
答案 3 :(得分:1)
将ImageList拖到表单上,然后使用Image List functions移动它:
我一直在调用ImageList_DragEnter()和ImageList_DragLeave(),它似乎将ImageList_DragMove()使用的坐标转换为客户端坐标,但我对文档的阅读表明没有必要。
答案 4 :(得分:0)
基于先前的答案:
首先定义方法MouseDown(在我的情况下,我使用按钮,但适用于其他控件)。
private void btn_MouseDown(object sender, MouseEventArgs e)
{
//Cast the sender to control type youre using
Button send = (Button)sender;
//Copy the control in a bitmap
Bitmap bmp = new Bitmap(send.Width, send.Height);
send.DrawToBitmap(bmp, new Rectangle(Point.Empty, bmp.Size));
//In a variable save the cursor with the image of your controler
this.BitMapCursor = new Cursor(bmp.GetHicon());
send.DoDragDrop(send.Text, DragDropEffects.Move);
}
我正在强制发送者,因为在我的应用中,buttón是实时生成的。
现在定义方法GiveFeedBack(此方法在拖动操作期间发生。)
private void btn_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
//Deactivate the default cursor
e.UseDefaultCursors = false;
//Use the cursor created from the bitmap
Cursor.Current = this.BitMapCursor;
}
最后不要忘记将控件绑定到方法上
btn.Click += new EventHandler(ClickButton);
btn.MouseDown += new MouseEventHandler(btn_MouseDown);
btn.GiveFeedback += new GiveFeedbackEventHandler(btn_GiveFeedback);