Drag WPF Popup control
Solution 1
Here's a simple solution using a Thumb.
- Subclass Popup in XAML and codebehind
- Add a Thumb with width/height set to 0 (this could also be done in XAML)
- Listen for MouseDown events on the Popup and raise the same event on the Thumb
- Move popup on DragDelta
XAML:
<Popup x:Class="PopupTest.DraggablePopup" ...>
<Canvas x:Name="ContentCanvas">
</Canvas>
</Popup>
C#:
public partial class DraggablePopup : Popup
{
public DraggablePopup()
{
var thumb = new Thumb
{
Width = 0,
Height = 0,
};
ContentCanvas.Children.Add(thumb);
MouseDown += (sender, e) =>
{
thumb.RaiseEvent(e);
};
thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
}
Solution 2
There is no DragMove for PopUp. Just a small work around, there is lot of improvements you can add to this.
<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint" Width="200">
<Rectangle Stretch="Fill" Fill="Red"/>
</Popup>
In the code behind , add this mousemove event
pop.MouseMove += new MouseEventHandler(pop_MouseMove);
void pop_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
e.GetPosition(this).Y),new Point(200,200));
}
}
Solution 3
Building off of Jobi Joy's answer, I found a re-useable solution that allows you to add as a control within xaml of an existing control/page. Which was not possible adding as Xaml with a Name since it has a different scope.
[ContentProperty("Child")]
[DefaultEvent("Opened")]
[DefaultProperty("Child")]
[Localizability(LocalizationCategory.None)]
public class DraggablePopup : Popup
{
public DraggablePopup()
{
MouseDown += (sender, e) =>
{
Thumb.RaiseEvent(e);
};
Thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
/// <summary>
/// The original child added via Xaml
/// </summary>
public UIElement TrueChild { get; private set; }
public Thumb Thumb { get; private set; } = new Thumb
{
Width = 0,
Height = 0,
};
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
TrueChild = Child;
var surrogateChild = new StackPanel();
RemoveLogicalChild(TrueChild);
surrogateChild.Children.Add(Thumb);
surrogateChild.Children.Add(TrueChild);
AddLogicalChild(surrogateChild);
Child = surrogateChild;
}
}
Solution 4
The issue with loosing the mouse when moving too fast, could be resolved
This is taken from msdn:
The new window contains the Child content of Popup.
The Popup control maintains a reference to its Child content as a logical child. When the new window is created, the content of Popup becomes a visual child of the window and remains the logical child of Popup. Conversely, Popup remains the logical parent of its Child content.
In the other words, the child of the popup is displayed in standalone window.
So when trying to the following:
Popup.CaptureMouse()
is capturing the wrapper window and not the popup itself. Instead using Popup.Child.CaptureMouse()
captures the actual popup.
And all other events should be registered using Popup.Child
.
Like Popup.Child.MouseMove
, Popup.Child.LostCapture
and so on
This has been tested and works perfectly fine
Solution 5
Another way of achieving this is to set your Popup's placement to MousePoint. This makes the popup initially appear at the position of the mouse cursor.
Then you can either use a Thumb or MouseMove event to set the Popup's HorizontalOffset & VerticalOffset. These properties shift the Popup away from its original position as the user drags it.
Remember to reset HorizontalOffset and VerticalOffset back to zero for the next use of the popup!
Joachim Kerschbaumer
Updated on July 31, 2022Comments
-
Joachim Kerschbaumer almost 2 years
the WPF Popup control is nice, but somewhat limited in my opinion. is there a way to "drag" a popup around when it is opened (like with the DragMove() method of windows)?
can this be done without big problems or do i have to write a substitute for the popup class myself? thanks
-
RedGlyph over 13 years
-
Jobi Joy over 13 yearsI agree , that solution I have given is just a hack, As you said DragDelta is more performant than MoveMove.
-
Joachim Kerschbaumer about 13 yearsyou could capture the mouse to avoid the problem you mentioned when the mouse gets out of the window
-
Joachim Kerschbaumer almost 13 yearsBut what if you dont't wanna misuse a popup as a thumb (which i guess i meant in RedGlyphs comment), but want to move a more complex popup (e.g. video overlay). using an explicit window is not option in my scenario.
-
Alan over 11 yearsYes, capturing mouse input is the proper way to handle dragging. All of this e.Leftbutton == Pressed is a bit lazy and causes some side-effects.
-
Vassi over 11 yearsPerhaps i'm missing something simple that a seasoned WPF developer would know, but how is this re-usable? When I plant the control in a different XAML context (like trying to reuse it in a window) any content I specify overrides the Canvas element to which the thumb is bound.
-
Shankar Raju about 10 yearsI have tried this solution. Works great! Thank you Jacob. I am facing only one issue: I have a view inside this Thumb and I am able to drag the entire view fine, however if that view has a scrollbar, then dragging the scrollbar is causing the dragging the thumb, and hence the entire view. Is there a way to avoid this?