C# Drag-and-Drop: Show the dragged item while dragging

后端 未结 5 1721
星月不相逢
星月不相逢 2020-11-30 10:01

I\'m building a desktop app in C# with Windows Forms. I have a custom Control, and I\'d like to be able to drag and drop it within my application (not outside). Right now I\

相关标签:
5条回答
  • 2020-11-30 10:22

    Drag an ImageList onto your form and use the Image List functions to move it around:

    1. Create a Bitmap object of the size of your control (but not more than 256x256).
    2. Copy the image of your control into the Bitmap: using (Graphics gfx = Graphics.FromImage(bmp)) { gfx.CopyFromScreen(...) }
    3. Add it to your ImageList.
    4. Call ImageList_BeginDrag().
    5. Call DoDragDrop()
    6. In your OnDragMove handler, call ImageList_DragMove() to move it around as the mouse moves.
    7. When DoDragDrop returns, call ImageList_DragLeave().

    I have been calling ImageList_DragEnter() and ImageList_DragLeave(), which seems to convert the coordinates used by ImageList_DragMove() to client coordinates, but my reading of the documentation suggests it is not necessary.

    0 讨论(0)
  • 2020-11-30 10:25

    I thought I should come back and answer this myself, since I did get it working eventually.

    I created a CursorUtil class with these functions:

    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;
        }
    

    Then I wrote a Drag class (also not object-oriented, alas, but I figured you can only drag one thing at a time in a desktop app). Here is a bit of that code:

        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;
            }
        }
    

    You can use these methods when you set up your controls, for example in the constructor:

    GiveFeedback += new GiveFeedbackEventHandler(Drag.UpdateCursor);
    

    and in this method:

        protected override void OnMouseMove(MouseEventArgs mea) {
            if (Drag.IsDragging(mea)) {
                Drag.StartDragging(this);
                DragDropEffects dde = DoDragDrop(Plan, DragDropEffects.Move | DragDropEffects.Copy);
                Drag.StopDragging();
            }
        }
    
    0 讨论(0)
  • 2020-11-30 10:26

    Based on a previous answer:

    First define the method MouseDown (in my case i use a button, but is aplicable at other controls).

            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);
        }
    

    i am casting the sender because in my aplicattion the buttón is be generated in real time.

    Now define the method GiveFeedBack(this method occurs during a drag operation.)

        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;
    
        }
    

    To end dont forget suscribe the control to the methods

    btn.Click += new EventHandler(ClickButton);
    btn.MouseDown += new MouseEventHandler(btn_MouseDown);
    btn.GiveFeedback += new GiveFeedbackEventHandler(btn_GiveFeedback);
    
    0 讨论(0)
  • 2020-11-30 10:33

    this could be an option:

    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;            
    
        }
    

    the cursor's hotspot will be created at the middle of the image

    0 讨论(0)
  • 2020-11-30 10:41

    That's usually handled by the GiveFeedback event.

    0 讨论(0)
提交回复
热议问题