Search code examples
wpfwpf-controls

How do I set a draggable wpf popup to its original location once it is closed?


I used code found here on Stack Overflow to make a WPF popup draggable using attached behavors. This code and behavior works as expected. The popup will remain at its dragged position until the user moves it again.

What I want to do now is make the popup appear at its original placement target location once it is closed and reopened. How do I accomplish this task?

Original post: A draggable popup control in wpf

Answer code written by Rick Sladkey: https://stackoverflow.com/a/4784977/1286413

Here is the XAML for the Popup:

<Grid>
   <StackPanel>
       <TextBox x:Name="textBox1" Width="200" Height="20"/>
   </StackPanel>
   <Popup PlacementTarget="{Binding ElementName=textBox1}" IsOpen="{Binding IsKeyboardFocused, ElementName=textBox1, Mode=OneWay}">
       <i:Interaction.Behaviors>
           <local:MouseDragPopupBehavior/>
       </i:Interaction.Behaviors>
       <TextBlock Background="White">
           <TextBlock.Text>Sample Popup content.</TextBlock.Text>
       </TextBlock>
   </Popup>
</Grid>

Here is the AttachedBehavior he wrote:

public class MouseDragPopupBehavior : Behavior<Popup>
{
   private bool mouseDown;
   private Point oldMousePosition;

   protected override void OnAttached()
   {
       AssociatedObject.MouseLeftButtonDown += (s, e) =>
       {
           mouseDown = true;
           oldMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
           AssociatedObject.Child.CaptureMouse();
       };
       AssociatedObject.MouseMove += (s, e) =>
       {
           if (!mouseDown) return;
           var newMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
           var offset = newMousePosition - oldMousePosition;
           oldMousePosition = newMousePosition;
           AssociatedObject.HorizontalOffset += offset.X;
           AssociatedObject.VerticalOffset += offset.Y;
       };
       AssociatedObject.MouseLeftButtonUp += (s, e) =>
       {
           mouseDown = false;
           AssociatedObject.Child.ReleaseMouseCapture();
       };
   }
}

Thanks in advance for the help!


Solution

  • In OnAttached, add a handler to Closed that will save the popup's position, and another handler to Opened that will move the popup back to that position.

    public class MouseDragPopupBehavior : Behavior<Popup>
    {
        private bool mouseDown;
        private Point oldMousePosition;
    
        private bool useSavedPosition;
        private Point savedPosition;
    
        protected override void OnAttached()
        {
            AssociatedObject.MouseLeftButtonDown += (s, e) =>
            {
                mouseDown = true;
                oldMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
                AssociatedObject.Child.CaptureMouse();
            };
            AssociatedObject.MouseMove += (s, e) =>
            {
                if (!mouseDown) return;
                var newMousePosition = AssociatedObject.PointToScreen(e.GetPosition(AssociatedObject));
                var offset = newMousePosition - oldMousePosition;
                oldMousePosition = newMousePosition;
                AssociatedObject.HorizontalOffset += offset.X;
                AssociatedObject.VerticalOffset += offset.Y;
            };
            AssociatedObject.MouseLeftButtonUp += (s, e) =>
            {
                mouseDown = false;
                AssociatedObject.Child.ReleaseMouseCapture();
            };
    
            AssociatedObject.Opened += (s, e) =>
            {
                if (!useSavedPosition) return;
                AssociatedObject.HorizontalOffset = savedPosition.X;
                AssociatedObject.VerticalOffset = savedPosition.Y;
            };
    
            AssociatedObject.Loaded += (s, e) =>
            {
                savedPosition = new Point(AssociatedObject.HorizontalOffset, AssociatedObject.VerticalOffset);
                useSavedPosition = true;
            };
        }
    }