2010-07-13 21 views
18

Estoy construyendo una aplicación de escritorio en C# con Windows Forms. Tengo un Control personalizado, y me gustaría poder arrastrarlo y soltarlo dentro de mi aplicación (no afuera). En este momento estoy implementando eso con los métodos habituales DoDragDrop/OnDragOver/OnDragDrop. ¿Hay alguna manera de pintar continuamente el control a medida que se arrastra, algo así como lo que ves con el arrastrar y soltar de JQuery? Quiero que el control real permanezca en su lugar, pero quiero pintar una copia de su apariencia a medida que el usuario lo arrastra. Idealmente, la copia sería incluso semitransparente, pero eso es más un "agradable tener".C# Arrastrar y colocar: mostrar el elemento arrastrado mientras arrastra

La única forma en que puedo pensar para hacer esto es poner el código de pintura en el método OnPaint de la forma principal, pero parece una solución poco elegante. ¿Alguna otra idea? ¿Son las cosas más fáciles si el Control se pinta solo como un mapa de bits?

Respuesta

14

Pensé que debería volver y responder a esto por mi cuenta, ya que eventualmente lo hice funcionar.

creé una clase CursorUtil con estas funciones:

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

Entonces escribí una clase Arrastre (que tampoco está orientado a objetos, por desgracia, pero pensé que sólo se puede arrastrar una cosa a la vez en un escritorio aplicación).Aquí es un poco de ese código:

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

Puede utilizar estos métodos al configurar sus controles, por ejemplo en el constructor:

GiveFeedback += new GiveFeedbackEventHandler(Drag.UpdateCursor); 

y en este método:

protected override void OnMouseMove(MouseEventArgs mea) { 
     if (Drag.IsDragging(mea)) { 
      Drag.StartDragging(this); 
      DragDropEffects dde = DoDragDrop(Plan, DragDropEffects.Move | DragDropEffects.Copy); 
      Drag.StopDragging(); 
     } 
    } 
+8

¿Hay alguna posibilidad de ver el código completo para la clase Drag? TIA, Johnny J. –

2

Eso generalmente se maneja mediante el evento GiveFeedback.

+1

Sé que es ahí donde puedo obtener devoluciones de llamadas a medida que se mueve el elemento arrastrado, pero ¿está diciendo que debería obtener un objeto Graphics y poner comandos de dibujo en ese método? –

+0

Cuando uso GiveFeedback, el cursor parpadea cuando dejo de mover el mouse. Pero mientras el mouse se mueve, la pantalla está bien. –

1

Arrastre un ImageList en su forma y uso del Image List functions para moverla por:

  1. crear un objeto de mapa de bits del tamaño de su control (pero no más de 256x256).
  2. Copiar la imagen de su control en el mapa de bits: usando (Gráficos GFX = Graphics.FromImage (BMP)) {gfx.CopyFromScreen (...)}
  3. añadirlo a su ImageList.
  4. Llamada ImageList_BeginDrag().
  5. llamada DoDragDrop()
  6. En el controlador de OnDragMove, llame ImageList_DragMove() para moverlo alrededor mientras el ratón se mueve .
  7. Cuando DoDragDrop regresa, llama a ImageList_DragLeave().

He estado llamando a ImageList_DragEnter() y ImageList_DragLeave(), que parece convertir las coordenadas utilizadas por ImageList_DragMove() a las coordenadas de cliente, pero mi lectura de la documentación sugiere que no es necesario.

+0

Estoy bastante confundido aquí. La pregunta es por C#, pero el enlace de MSDN que proporciona es para C++ no administrado. Hay una clase ImageList en .Net, pero no tiene los métodos BeginDrag, DragMove y DragLeave que diga que deberían llamarse. – RenniePet

6

esto podría ser una opción:

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;    

    } 

el punto de acceso del cursor se creará en el centro de la imagen

+0

El cursor parece volver a la configuración predeterminada una vez que el método finaliza. – helrich

+0

Sí, pero ¿ese es un behivator deseado o me falta algo? Quiero decir, después de que la resistencia se haya detenido o se haya realizado una cancelación – kaytes

+0

Me encanta eso ... Bastante simple. –

Cuestiones relacionadas