C#拖放:在拖动时显示拖动的项目
我正在用C#和Windows窗体构建一个桌面应用程序。我有一个自定义控件,我希望能够在我的应用程序中(而不是在外部)拖放它。现在,我正在用常用的DoDragDrop/OnDragOver/OnDragDrop方法实现这一点。有没有什么方法可以在控件被拖动时连续绘制它——就像JQuery的拖放一样?我希望实际控件保持在原位,但我希望在用户拖动它时绘制其外观的副本。理想情况下,副本甚至应该是半透明的,但这更像是一个“很好拥有”C#拖放:在拖动时显示拖动的项目,c#,C#,我正在用C#和Windows窗体构建一个桌面应用程序。我有一个自定义控件,我希望能够在我的应用程序中(而不是在外部)拖放它。现在,我正在用常用的DoDragDrop/OnDragOver/OnDragDrop方法实现这一点。有没有什么方法可以在控件被拖动时连续绘制它——就像JQuery的拖放一样?我希望实际控件保持在原位,但我希望在用户拖动它时绘制其外观的副本。理想情况下,副本甚至应该是半透明的,但这更像是一个“很好拥有” 我能想到的唯一方法是将绘制代码放在主窗体的OnPaint方法中,但这似乎
我能想到的唯一方法是将绘制代码放在主窗体的OnPaint方法中,但这似乎是一个不雅观的解决方案。还有其他想法吗?如果控件将自身绘制为位图,事情会变得更简单吗?这通常由
GiveFeedback
事件处理。将图像列表拖到窗体上,然后使用移动它:
我一直在调用ImageList_DragEnter()和ImageList_DragLeave(),这似乎可以将ImageList_DragMove()使用的坐标转换为客户坐标,但我阅读文档后认为这是不必要的。我想我应该自己回来回答这个问题,因为我最终还是让它工作了 我使用以下函数创建了一个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;
}
光标的热点将根据前面的回答在图像的中间创建: 首先定义MouseDown方法(在我的例子中,我使用一个按钮,但适用于其他控件) 我铸造发送者是因为在我的应用中,Button是实时生成的 现在定义GiveFeedBack方法(此方法发生在拖动操作期间) 要结束,请不要忘记将控件复制到方法
btn.Click += new EventHandler(ClickButton);
btn.MouseDown += new MouseEventHandler(btn_MouseDown);
btn.GiveFeedback += new GiveFeedbackEventHandler(btn_GiveFeedback);
我知道当拖动的项目移动时,我可以在那里得到回调,但是你是说我应该获得一个图形对象并在该方法中放置绘图命令吗?当我使用GiveFeedback时,当我停止移动鼠标时,光标闪烁。但是当鼠标移动时,显示正常。这不是一个真正的答案。有没有可能看到Drag类的完整代码?蒂亚,约翰尼,我有点困惑。问题是C++,但是您提供的MSDN链接是非托管C++。Net中有一个ImageList类,但它没有您说应该调用的BeginDrag、DragMove和DragLeave方法。该方法完成后,光标似乎会恢复为默认值。是的,但这是一个需要的后遗症,还是我遗漏了什么?我的意思是,在拖拉结束或取消后,爱。。。非常简单。是的,这是一个比主要解决方案简单得多的解决方案,但它是不完整的。光标变回原来的位置,因为这只处理MouseDown事件。一旦移动鼠标,光标将变回原来的位置。因此,要处理这个问题,您必须使用以下行将GiveFeedback事件添加到相应的控件:e.UseDefaultCursors=False
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);