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

C#拖放:在拖动时显示拖动的项目,c#,C#,我正在用C#和Windows窗体构建一个桌面应用程序。我有一个自定义控件,我希望能够在我的应用程序中(而不是在外部)拖放它。现在,我正在用常用的DoDragDrop/OnDragOver/OnDragDrop方法实现这一点。有没有什么方法可以在控件被拖动时连续绘制它——就像JQuery的拖放一样?我希望实际控件保持在原位,但我希望在用户拖动它时绘制其外观的副本。理想情况下,副本甚至应该是半透明的,但这更像是一个“很好拥有” 我能想到的唯一方法是将绘制代码放在主窗体的OnPaint方法中,但这似乎

我正在用C#和Windows窗体构建一个桌面应用程序。我有一个自定义控件,我希望能够在我的应用程序中(而不是在外部)拖放它。现在,我正在用常用的DoDragDrop/OnDragOver/OnDragDrop方法实现这一点。有没有什么方法可以在控件被拖动时连续绘制它——就像JQuery的拖放一样?我希望实际控件保持在原位,但我希望在用户拖动它时绘制其外观的副本。理想情况下,副本甚至应该是半透明的,但这更像是一个“很好拥有”


我能想到的唯一方法是将绘制代码放在主窗体的OnPaint方法中,但这似乎是一个不雅观的解决方案。还有其他想法吗?如果控件将自身绘制为位图,事情会变得更简单吗?

这通常由
GiveFeedback
事件处理。

将图像列表拖到窗体上,然后使用移动它:

  • 创建控件大小的位图对象(但不超过256x256)
  • 将控件的图像复制到位图中: 使用(Graphics gfx=Graphics.frommage(bmp)){gfx.CopyFromScreen(…)}
  • 将其添加到您的图像列表中
  • 调用ImageList_BeginDrag()
  • 调用DoDragDrop()
  • 在OnFragmove处理程序中,调用ImageList_DragMove()将其作为鼠标移动 移动
  • 当DoDragDrop返回时,调用ImageList_DragLeave()

  • 我一直在调用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);