Frage

I'm trying to find out how to implement what in WPF is called (I believe so--no experience with WPF yet) "drag adorners." I've been searching and trying different proposed solutions for a couple of days now, but none of them look quite like what I'm after. I want to stay as close to Windows' native look and feel as possible.

It's the semi-transparent image that we see slightly above and to the left of the mouse pointer when we drag the address bar globe in Firefox or the BigBlueE™ in IE, etc.

Double-click and drag a word on this page and you'll see what I mean. The cursor remains a Cursors.No until a TextBox is encountered--at that point we see a graphical representation of the text being dragged. The standard Move/Copy visual indicators of the cursor are shown (small gray square at the bottom right; plus sign when copying).

Sheridan says here that he's licked this problem; I'm hoping to hear from him with a tip.

Either a VB or C# suggestion will be fine, but WinForms is a must.

If I can just get pointed in the right direction, I can take it from there.

Thanks.


UPDATE: I've settled on my preferred solution, here. I know I was originally asking for the cursor's native Move/Copy indicator to stay put, but the SetDropDescription() feature here is so nice that I've changed my mind and decided to go with it instead. Here's what the WPF sample looks like when run:

DragDropSample

That's pretty nice. It's all Interop, so it should translate right over to WinForms.

War es hilfreich?

Lösung

It's possible to achieve this effect with a custom cursor. You can create the cursor as described in this answer. When you start the drag operation, create the cursor, and in the GiveFeedback event, set Cursor.Current to that cursor and e.UseDefaultCursors to false.

Here's a full example (using LINQPad):

https://gist.github.com/thomaslevesque/837d1a8295b33be2b404

Andere Tipps

More research: as it turns out, we can't safely use the Shell Extensions solution. Shell and CLR still don't mix, even after all this time. See here.

So I've decided to draw an image that looks like the Shell Icons and use a custom cursor to display them. Tedious, yes, but it must be done.

Here's the code, made into extension methods and tuned up a bit for memory leaks.

Public Module Gdi
  <Extension()>
  Public Function ToCursor(Bitmap As Bitmap, HotSpot As Point) As Cursor
    Dim oInfo As Interop.Structures.IconInfo
    Dim hIcon As IntPtr

    oInfo = New Interop.Structures.IconInfo
    hIcon = Bitmap.GetHicon

    Interop.Functions.GetIconInfo(hIcon, oInfo)

    oInfo.HotSpotX = HotSpot.X
    oInfo.HotSpotY = HotSpot.Y
    oInfo.IsIcon = False

    ToCursor = New Cursor(Interop.Functions.CreateIconIndirect(oInfo))

    If oInfo.Color <> IntPtr.Zero Then Interop.Functions.DeleteObject(oInfo.Color)
    If oInfo.Mask <> IntPtr.Zero Then Interop.Functions.DeleteObject(oInfo.Mask)
    If hIcon <> IntPtr.Zero Then Interop.Functions.DestroyIcon(hIcon)
  End Function


  <Extension()>
  Public Function ToBitmap(Cursor As Cursor) As Bitmap
    Dim oInfo As Interop.Structures.IconInfo
    Dim oData As BitmapData
    Dim hIcon As IntPtr

    oInfo = New Interop.Structures.IconInfo
    hIcon = Cursor.Handle

    Interop.Functions.GetIconInfo(hIcon, oInfo)

    Using oBitmap As Bitmap = Bitmap.FromHbitmap(oInfo.Color)
      Interop.Functions.DeleteObject(oInfo.Color)
      Interop.Functions.DeleteObject(oInfo.Mask)

      oData = oBitmap.LockBits(New Rectangle(0, 0, oBitmap.Width, oBitmap.Height), ImageLockMode.ReadOnly, oBitmap.PixelFormat)
      ToBitmap = New Bitmap(oData.Width, oData.Height, oData.Stride, PixelFormat.Format32bppArgb, oData.Scan0)
      oBitmap.UnlockBits(oData)
    End Using
  End Function
End Module

Public Class Functions
  <DllImport("User32")> _
  Public Shared Function CreateIconIndirect(ByRef Icon As Structures.IconInfo) As IntPtr
  End Function

  <DllImport("User32")> _
  Public Shared Function GetIconInfo(Icon As IntPtr, ByRef Info As Structures.IconInfo) As <MarshalAs(UnmanagedType.Bool)> Boolean
  End Function

  <DllImport("Gdi32")> _
  Public Shared Function DeleteObject(Handle As IntPtr) As Boolean
  End Function

  <DllImport("User32", CharSet:=CharSet.Auto)> _
  Public Shared Function DestroyIcon(Handle As IntPtr) As Boolean
  End Function
End Class

Namespace Structures
  Public Structure IconInfo
    Public IsIcon As Boolean
    Public HotSpotX As Integer
    Public HotSpotY As Integer
    Public Mask As IntPtr
    Public Color As IntPtr
  End Structure
End Namespace

Unfortunately ToBitMap() doesn't work for low-res cursors, such as Default. With a little more effort, though, it can be made to turn out a decent image. Perhaps oBitmap = Icon.FromHandle(Cursor).ToBitmap would suffice, but the resulting image quality leaves it an individual choice.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top