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

时间:2010-07-13 19:16:19

标签: c#

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

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

5 个答案:

答案 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移动它:

  1. 创建一个控件大小的Bitmap对象(但不超过256x256)。
  2. 将控件的图像复制到位图中: 使用(Graphics gfx = Graphics.FromImage(bmp)){gfx.CopyFromScreen(...)}
  3. 将其添加到您的ImageList。
  4. 调用ImageList_BeginDrag()。
  5. 调用DoDragDrop()
  6. 在OnDragMove处理程序中,调用ImageList_DragMove()将其作为鼠标移动 移动。
  7. 当DoDragDrop返回时,调用ImageList_DragLeave()。
  8. 我一直在调用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);