Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Draggable Popup

0.00/5 (No votes)
5 Jan 2017 1  
This is a simple popup that includes drag capability. The code should be helpful to anyone looking to create a control drag capability.

Introduction

I was asked to put up a dialog while the application was busy that could be dragged, allowing the user to move it out of the way so that he could see what was underneath.

Background

I had tried several things I found online but did not really like any of them, and some actually had issues.

The Code

The DraggablePopup derives from the Popup class. The overridden OnInitialized method checks that the Popup has content. This is because the Popup cannot be clicked on. The content, or Child, should contain a FrameworkElement that encompases any part of the Popup that the designer wants to allow the user to click and drag with. The OnInitialized method, after checking that there is content, then attaches handlers for the MouseLeftButtonDown, MouseLeftButtonUp and MouseMove events:

public class DraggablePopup : Popup
{
 Point _initialMousePosition;
 bool _isDragging ;

 protected override void OnInitialized(EventArgs e)
 {
  var contents = Child as FrameworkElement;
  Debug.Assert(contents != null, "DraggablePopup either has no content if content that " +
   "does not derive from FrameworkElement. Must be fixed for dragging to work.");
  contents.MouseLeftButtonDown += Child_MouseLeftButtonDown;
  contents.MouseLeftButtonUp += Child_MouseLeftButtonUp;
  contents.MouseMove += Child_MouseMove;
 }

 private void Child_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
  var element = sender as FrameworkElement;
  _initialMousePosition = e.GetPosition(null);
  element.CaptureMouse();
  _isDragging = true;
  e.Handled = true;
 }

 private void Child_MouseMove(object sender, MouseEventArgs e)
 {
  if (_isDragging)
  {
   var currentPoint = e.GetPosition(null);
   HorizontalOffset = HorizontalOffset + (currentPoint.X - _initialMousePosition.X);
   VerticalOffset = VerticalOffset + (currentPoint.Y - _initialMousePosition.Y);
  }
 }

 private void Child_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
 {
  if (_isDragging)
  {
   var element = sender as FrameworkElement;
   element.ReleaseMouseCapture();
   _isDragging = false;
   e.Handled = true;
  }
 }
}

There are two private fields: one to indicate that a drag operation is in progress, and the other to contain the initial location that dragging started. The MouseLeftButtonDown and MouseLeftButtonUp event handlers basically start and end the process by setting the bool IsDragging field, and the MouseLeftButtonDown also sets the initial location of the click. Then the MouseMove adjusts the location of the Popup.

Using the Code

I have used a Border as the content in the sample. There is a Button within the Border that allows the Popup to be closed, but is not shown in the code below. I have set the Placement as Center to the containing Window. From my experience, the way Placement works is different on a Window than other controls. In other controls, to get the Popup centered, I had to use the HorzontalOffset and VerticalOffset since the top left corner is what is centered.

<local:DraggablePopup Width="300"
                      Height="200"
                      IsOpen="{Binding ElementName=WindowToggleButton,
                                       Path=IsChecked}"
                      Placement="Center"
                      PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
                     >
 <Border Background="LightGray"
         BorderBrush="Black"
         BorderThickness="1" >
  </Border>
</local:DraggablePopup>

History

  • 2016/12/23: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here