2010-07-13 4 views
18

Windows Forms을 사용하여 C#으로 데스크톱 응용 프로그램을 작성합니다. 나는 사용자 정의 컨트롤이 있고, 내 응용 프로그램 (외부가 아닌) 내에서 끌어서 놓을 수 있기를 원합니다. 지금 당장은 일반적인 DoDragDrop/OnDragOver/OnDragDrop 메서드로 구현하고 있습니다. JQuery 드래그 앤 드롭으로 볼 수있는 일종의 드래그 앤드 드래그를 통해 컨트롤을 계속 페인트 할 수있는 방법이 있습니까? 실제 컨트롤을 그대로두고 싶지만 사용자가 드래그 할 때 모양의 복사본을 그려야합니다. 이상적으로 사본은 심지어 반투명 한 것일지도 모르지만 그것은 더 가지고있다 "가지고있는 것이 좋다.C# 드래그 앤 드롭 : 드래그하는 동안 드래그 한 항목 표시

내가 이것을 할 수있는 유일한 방법은 기본 폼의 OnPaint 메서드에 페인트 코드를 넣는 것입니다.하지만 이는 비 숙련 된 솔루션처럼 보입니다. 다른 아이디어? 컨트롤이 비트 맵으로 그려지면 모든 것이 더 쉬워 질까요?

답변

14

결국 돌아와서 대답해야한다고 생각 했었습니다. 나는 드래그 클래스 (또한 객체 지향하지, 슬프게도을 쓴 다음

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

,하지만 난 당신이 단지 바탕 화면에 한 번에 한 가지를 드래그 할 수 있습니다 생각 :

나는 이러한 기능을 가진 CursorUtil 클래스를 생성 앱).여기에 그 코드의 비트는 다음과 같습니다

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(); 
     } 
    } 
+8

Drag 클래스의 전체 코드를 볼 기회가 있습니까? TIA, Johnny J. –

2

일반적으로 GiveFeedback 이벤트가 처리합니다.

+1

드래그 한 항목이 이동하면 콜백을받을 수있는 곳을 알지만 그래픽 객체를 가져 와서 해당 메서드에 드로잉 명령을 넣어야한다고 말하고 있습니까? –

+0

GiveFeedback을 사용할 때 마우스 이동을 멈 추면 커서가 깜박입니다. 그러나 마우스가 움직이는 동안 디스플레이는 괜찮습니다. –

1

에서 마우스를 끌어 당신의 폼으로의 ImageList하고 주위를 이동 Image List functions를 사용

  1. 이 컨트롤의 크기의 비트 맵 객체를 생성합니다 (그러나 더 256 * 256 이하).
  2. 복사 비트 맵에 컨트롤의 이미지 : 사용 (그래픽 GFX = Graphics.FromImage (BMP)) {gfx.CopyFromScreen (...)}
  3. 은 당신의 ImageList에 추가합니다.
  4. ImageList_BeginDrag()를 호출하십시오.
  5. DoDragDrop()을 호출하십시오.
  6. OnDragMove 핸들러에서 ImageList_DragMove()를 호출하여 마우스를 움직이면 이 움직입니다.
  7. DoDragDrop이 반환되면 ImageList_DragLeave()를 호출하십시오.

나는 클라이언트 좌표 ImageList_DragMove (가 사용하는 좌표)로 변환 보인다, ImageList_DragEnter()와 ImageList_DragLeave()를 호출되었지만, 문서의 내 독서는 필요하지 않습니다 제안합니다.

+0

나는 오히려 여기 혼란스러워. 질문은 C#이지만 사용자가 제공하는 MSDN 링크는 관리되지 않는 C++ 용입니다. .NET에는 ImageList 클래스가 있지만 BeginDrag, DragMove 및 DragLeave 메서드는 호출하지 않아도됩니다. – RenniePet

6

이것은 옵션 일 수 있습니다.

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;    

    } 

커서의 핫스팟이 이미지 중간에 생성됩니다

+0

메서드가 끝난 후 커서가 기본값으로 되돌아가는 것처럼 보입니다. – helrich

+0

그래,하지만 그게 원하는 behivator 또는 뭔가를 놓치지? 내 말은, 끌기가 끝나거나 취소가 수행 된 후 – kaytes

+0

Love that ... 아주 간단합니다. –