2010-07-13 13 views
18

Sto costruendo un'app desktop in C# con Windows Form. Ho un controllo personalizzato e mi piacerebbe poterlo trascinare nella mia applicazione (non all'esterno). In questo momento lo sto implementando con i soliti metodi DoDragDrop/OnDragOver/OnDragDrop. C'è un modo per dipingere continuamente il controllo mentre viene trascinato in giro - una sorta di ciò che vedi con il drag-and-drop di JQuery? Voglio che il controllo effettivo rimanga sul posto, ma voglio dipingere una copia del suo aspetto mentre l'utente lo trascina. Idealmente la copia sarebbe anche semitrasparente, ma è più un "bello da avere".C# Drag-and-Drop: mostra l'oggetto trascinato mentre si trascina

L'unico modo che posso pensare di fare questo è inserire il codice di vernice nel metodo OnPaint del modulo principale, ma sembra una soluzione poco elegante. Altre idee? Le cose sono più facili se il controllo si dipinge da solo come una bitmap?

risposta

14

Pensavo di dover tornare e rispondere a me stesso, poiché alla fine l'ho fatto funzionare.

ho creato una classe CursorUtil con queste funzioni:

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

Poi ho scritto una classe di resistenza (anche non orientata agli oggetti, ahimè, ma ho pensato che è possibile trascinare una sola cosa alla volta in un desktop app).Ecco un po 'di quel codice:

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

È possibile utilizzare questi metodi quando si impostano i controlli, ad esempio nel costruttore:

GiveFeedback += new GiveFeedbackEventHandler(Drag.UpdateCursor); 

e in questo metodo:

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

Qualche possibilità di vedere il codice completo per la classe Drag? TIA, Johnny J. –

2

In genere viene gestito dall'evento GiveFeedback.

+1

So che è dove posso ottenere le richiamate mentre l'oggetto trascinato si sposta, ma stai dicendo che dovrei ottenere un oggetto Graphics e mettere i comandi di disegno in quel metodo? –

+0

Quando uso GiveFeedback, il cursore sfarfalla quando smetto di spostare il mouse. Ma mentre il mouse si muove, il display è ok. –

1

Trascinare un ImageList nel form e utilizzare il Image List functions per spostarla:

  1. Creare un oggetto bitmap delle dimensioni del vostro controllo (ma non più di 256x256).
  2. Copiare l'immagine del vostro controllo nel Bitmap: usando (Graphics GFX = Graphics.FromImage (BMP)) {gfx.CopyFromScreen (...)}
  3. aggiungerlo al tuo ImageList.
  4. Chiama ImageList_BeginDrag().
  5. chiamata DoDragDrop()
  6. Nel vostro gestore OnDragMove, chiamare ImageList_DragMove() per spostarsi in giro come il mouse si muove .
  7. Quando DoDragDrop restituisce, chiamare ImageList_DragLeave().

ho chiamato ImageList_DragEnter() e ImageList_DragLeave(), che sembra per convertire le coordinate utilizzate da ImageList_DragMove() in coordinate client, ma la mia lettura della documentazione suggerisce che non è necessario.

+0

Sono piuttosto confuso qui. La domanda è per C#, ma il link MSDN che fornisci è per C++ non gestito. Esiste una classe ImageList in .Net, ma non ha i metodi BeginDrag, DragMove e DragLeave che si dovrebbero chiamare. – RenniePet

6

questo potrebbe essere un'opzione:

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;    

    } 

l'hotspot del cursore verrà creato al centro dell'immagine

+0

Il cursore sembra ripristinare l'impostazione predefinita al termine del metodo. – helrich

+0

sì ma quello è un behivator desiderato o mi manca qualcosa? Voglio dire, dopo che il trascinamento è finito o viene eseguita una cancellazione – kaytes

+0

Amore che ... Abbastanza semplice. –

Problemi correlati