I'm basically writing a simple floating panel whose location can be changed via dragging its title bar (which is a grid itself). But I can't get it working! It seems MouseEventArgs.GetPosition
returns a wrong point. What am I missing here?
public class FloatingPanel : Grid
{
Grid gridTitle;
bool dragging = false;
Point lastPos;
public FloatingPanel(UserControl gadget)
{
this.MouseMove += FloatingPanel_MouseMove;
gridTitle = new Grid();
gridTitle.Height = 25;
gridTitle.VerticalAlignment = System.Windows.VerticalAlignment.Top;
gridTitle.Background = Brushes.Cyan;
gridTitle.MouseLeftButtonDown += gridTitle_MouseLeftButtonDown;
gridTitle.MouseLeftButtonUp += gridTitle_MouseLeftButtonUp;
this.Children.Add(gridTitle);
this.Height = gadget.Height + 25;
this.Width = gadget.Width;
gadget.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
this.Children.Add(gadget);
}
void gridTitle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
dragging = false;
}
void gridTitle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
lastPos = Mouse.GetPosition(this);
dragging = true;
}
void FloatingPanel_MouseMove(object sender, MouseEventArgs e)
{
if(dragging)
{
Vector delta = e.GetPosition(this) - lastPos;
this.Margin = new Thickness(this.Margin.Left + delta.X, this.Margin.Top + delta.Y, this.Margin.Right, this.Margin.Bottom);
lastPos = e.GetPosition(this);
}
}
}
I've also tried using System.Windows.Forms.Cursor.Position
and System.Windows.Forms.Control.MousePosition
which give the position on the screen. But no luck.
Solution: The problem was solved by 3 modifications(as Sphinxxx pointed out):
- Using MouseEventArgs.GetPosition(null)
instead of MouseEventArgs.GetPosition(this)
- Capturing and releasing the mouse in mousedown
and mouseup
events using Mouse.Capture(gridTitle)
and Mouse.Capture(null)
- Setting the grid's horizontal and vertical alignment. (This seems odd to me. Why does nod setting the alignment cause a problem?)
In
_MouseMove
, you're trying to calculate the movement usinge.GetPosition(this)
, but that only gets the mouse pointer position relative to yourGrid
. You need to find the position relative to some other UI element, e.g. the containingWindow
in order to know how much the grid should move.Try
e.GetPosition(null)
(that is null instead of this) in both_MouseLeftButtonDown
and_MouseMove
to calculate correctdelta
s.This article illustrates the difference: Getting the Mouse Position Relative to a Specific Element
EDIT: More robust
FloatingPanel
:In the constructor, avoid having a gadget that may end up on top of the title bar by putting them in two separate
RowDefinition
s (and let WPF handle widths and heights):In the
..ButtonDown
/..ButtonUp
handlers, make the title bar "capture" (and release) the mouse movements so the mouse pointer doesn't "slide off" when moving too fast:EDIT 2: Alternative without
Mouse.Capture()
: