问题
I was wondering if there is a way (there is always a way) or what are best approach you have to show either of the borders of my controls or an image of what user is currently draging and dropping? I only drag from within my own app so not from desktop.
I would like to have somethings similar to :
I used to try to manually draw lines to "fake" the fact that this is my control's borders and It works fine... but I had hard time showing them over others form's controls as this is a huge form with multilevel controls.
I can't really show code I currently have as this is too much huge. But, maybe if you knows some good examples to perform this on what I can base my searches on. Because so far, I can't find any way to shows a Controls Border or a Image of control when drag and dropping.
回答1:
It is, as you noted, possible, but not with one or two lines..
Here is an example, that uses a Panel dragFrame, in which an Image of the dragged control is shown.
Is is moving along with the cursor, but should not slide under it, so the DragEnter event still recognizes the Cursor coming in.
I prepare the form by setting up two list of controls: Those that can be dragged and those that can be dropped on. But for the eample I only code for one target..
We can't use a MouseMove event during the Dragging so we need a workaround with a Timer. In its Tick we either follow with the Panel or, if by now the mouse button has been released, we abort everything.
First we create the Panel and hide it. The Timer should be fast..
public Form1()
{
InitializeComponent();
dragFrame.Visible = false;
dragFrame.BorderStyle = BorderStyle.FixedSingle;
Controls.Add(dragFrame);
timer1.Interval = 20;
}
Now we load the two lists:
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control ctl in Controls) if (ctl != dropPanel) draggables.Add(ctl);
makeDraggable(draggables);
dragTargets.Add(dropPanel);
}
List<Control> dragTargets = new List<Control>();
List<Control> draggables = new List<Control>();
Panel dragFrame = new Panel();
Point mDown = Point.Empty;
This function adds sa few handlers to the draggable controls. The most involved it the Mousedown. In addition to starting the Draggin we create a new BackgroundImage for the Panel and size and show it. We also start the timer.
void makeDraggable(List<Control> draggables)
{
foreach (Control ctl in draggables)
{
ctl.MouseDown += (s, e) =>
{
mDown = e.Location;
timer1.Start();
dragFrame.Size = ctl.Size;
if (dragFrame.BackgroundImage != null)
dragFrame.BackgroundImage.Dispose();
Bitmap bmp = new Bitmap(dragFrame.ClientSize.Width,
dragFrame.ClientSize.Height);
ctl.DrawToBitmap(bmp, dragFrame.ClientRectangle);
dragFrame.BackgroundImage = bmp;
dragFrame.BringToFront();
dragFrame.Show();
ctl.DoDragDrop(ctl.Text, DragDropEffects.Copy | DragDropEffects.Move);
};
ctl.MouseUp += (s, e) =>
{
dragFrame.Hide();
timer1.Stop();
};
ctl.Leave += (s, e) =>
{
dragFrame.Hide();
timer1.Stop();
};
}
}
These are the events for one drop target. You will want to code the actual drop code, where you process the data..
private void dropPanel_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Text))
{ e.Effect = DragDropEffects.Copy; }
else { e.Effect = DragDropEffects.None; }
}
private void dropPanel_DragDrop(object sender, DragEventArgs e)
{
dragFrame.Hide();
timer1.Stop();
}
In the timer.Tick we do the moving and, when necessary abort the action:
private void timer1_Tick(object sender, EventArgs e)
{
if ( (Control.MouseButtons & MouseButtons.Left) == MouseButtons.None)
{
dragFrame.Hide();
timer1.Stop();
}
if (dragFrame.Visible)
{
Point pt = this.PointToClient(Cursor.Position);
dragFrame.Location = new Point(pt.X - mDown.X,
pt.Y - dragFrame.Height);
foreach( Control ctl in dragTargets)
if (ctl.ClientRectangle.Contains(pt ) )
{
dragFrame.Hide();
}
}
}
As you see it is involved and probably has a few quirks.
Still sure you need this?
回答2:
Finally, here is how we finally make it work as we want;
public Sub CreateCustomCursor(ByVal e As MouseEventArgs)
Dim bmp As New Bitmap(Me.Width, Me.Height)
' ME IS A USERCONTROL '
Me.DrawToBitmap(bmp, New Rectangle(New Point(0, 0), Me.Size))
' MAKE A CURSOR WITH AN IMAGE OF ME, WHICH IS A USERCONTROL '
Dim cur As Cursor = Me.CreateCursor(bmp, e.X, e.Y)
Cursor.Current = cur
Me.IsCursorSet = True
End Sub
Public Structure IconInfo
Public fIcon As Boolean
Public xHotspot As Integer
Public yHotspot As Integer
Public hbmMask As IntPtr
Public hbmColor As IntPtr
End Structure
<DllImport("user32.dll")> _
Public Shared Function GetIconInfo(ByVal hIcon As IntPtr, ByRef pIconInfo As IconInfo) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll")> _
Public Shared Function CreateIconIndirect(ByRef icon As IconInfo) As IntPtr
End Function
private Function CreateCursor(ByVal bmp As Bitmap, ByVal xHotSpot As Integer, ByVal yHotSpot As Integer) As Cursor
Dim ptr As IntPtr = bmp.GetHicon()
Dim tmp As New IconInfo()
GetIconInfo(ptr, tmp)
tmp.xHotspot = xHotSpot
tmp.yHotspot = yHotSpot
tmp.fIcon = False
ptr = CreateIconIndirect(tmp)
Return New Cursor(ptr)
End Function
来源:https://stackoverflow.com/questions/31193787/net-drag-and-drop-show-dragged-borders-or-image-such-as-windows-do