C # Drag-and-Drop: показ перетаскиваемого элемента при перетаскивании - PullRequest
19 голосов
/ 13 июля 2010

Я создаю настольное приложение на C # с Windows Forms. У меня есть пользовательский элемент управления, и я хотел бы иметь возможность перетаскивать его в моем приложении (а не за его пределами). Прямо сейчас я реализую это с помощью обычных методов DoDragDrop / OnDragOver / OnDragDrop. Есть ли способ непрерывно рисовать элемент управления по мере его перетаскивания - вроде того, что вы видите с помощью перетаскивания в JQuery? Я хочу, чтобы фактический элемент управления оставался на месте, но я хочу нарисовать копию его внешнего вида, когда пользователь перетаскивает его. В идеале копия была бы даже полупрозрачной, но это более «приятно иметь».

Единственный способ, которым я могу думать, это поместить код рисования в метод OnPaint основной формы, но это кажется неэффективным решением. Есть еще идеи? Что может быть проще, если элемент Control отображает себя как растровое изображение?

Ответы [ 4 ]

15 голосов
/ 29 июля 2011

Я подумал, что должен сам вернуться и ответить на этот вопрос, так как в конечном итоге я все заработал.

Я создал класс 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;
    }

Затем я написалКласс перетаскивания (увы, тоже не объектно-ориентированный, но я подумал, что вы можете перетаскивать только одну вещь за раз в настольном приложении).Вот фрагмент этого кода:

    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();
        }
    }
6 голосов
/ 09 июля 2014

это может быть вариант:

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 голосов
/ 13 июля 2010

Обычно это обрабатывается событием GiveFeedback.

1 голос
/ 08 сентября 2010

Перетащите ImageList на вашу форму и используйте функции Список изображений , чтобы переместить его:

  1. Создание растрового объекта размером с ваш элемент управления (но не более 256x256).
  2. Скопируйте изображение вашего элемента управления в растровое изображение: using (Graphics gfx = Graphics.FromImage (bmp)) {gfx.CopyFromScreen (...)}
  3. Добавьте его в свой список изображений.
  4. Вызовите ImageList_BeginDrag ().
  5. Call DoDragDrop ()
  6. В вашем обработчике OnDragMove вызовите ImageList_DragMove (), чтобы переместить его как мышь движется.
  7. Когда DoDragDrop возвращается, вызовите ImageList_DragLeave ().

Я вызывал ImageList_DragEnter () и ImageList_DragLeave (), которые, кажется, преобразовывают координаты, используемые ImageList_DragMove (), в клиентские координаты, но мое чтение документации показывает, что в этом нет необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...