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 containingWindowin order to know how much the grid should move.Try
e.GetPosition(null)(that is null instead of this) in both_MouseLeftButtonDownand_MouseMoveto calculate correctdeltas.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
RowDefinitions (and let WPF handle widths and heights):In the
..ButtonDown/..ButtonUphandlers, 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():