Automatically update a WPF Popup position

I wanted a means to automatically reposition a popup in relation to it’s placement target – for example I created a popup that was displayed to the right of another control (the placement target), but when the control’s parent window moved the popup (by default) does not move with it – I needed it to reposition in relation to the placement target.

So I’ve implemented the following behavior to automatically reposition the popup when the parent window is moved

public class AutoRepositionPopupBehavior : Behavior<Popup>
{
   private const int WM_MOVING = 0x0216;

   // should be moved to a helper class
   private DependencyObject GetTopmostParent(DependencyObject element)
   {
      var current = element;
      var result = element;

      while (current != null)
      {
         result = current;
         current = (current is Visual || current is Visual3D) ? 
            VisualTreeHelper.GetParent(current) :
            LogicalTreeHelper.GetParent(current);
      }
      return result;
   }

   protected override void OnAttached()
   {
      base.OnAttached();

      AssociatedObject.Loaded += (sender, e) =>
      {
         var root = GetTopmostParent(AssociatedObject.PlacementTarget) as Window;
         if (root != null)
         {
            var helper = new WindowInteropHelper(root);
            var hwndSource = HwndSource.FromHwnd(helper.Handle);
            if (hwndSource != null)
            {
               hwndSource.AddHook(HwndMessageHook);
            }
         }
      };
   }

   private IntPtr HwndMessageHook(IntPtr hWnd, 
           int msg, IntPtr wParam, 
           IntPtr lParam, ref bool bHandled)
   {
      if (msg == WM_MOVING)
      {
         Update();				
      }
      return IntPtr.Zero;
   }

   public void Update()
   {
      // force the popup to update it's position
      var mode = AssociatedObject.Placement;
      AssociatedObject.Placement = PlacementMode.Relative;
      AssociatedObject.Placement = mode;
   }
}

So to use the above code we simple use the following XAML within a popup element

<i:Interaction.Behaviors>
   <behaviors:AutoRepositionPopupBehavior />
</i:Interaction.Behaviors>

When the AssociatedObject is loaded we locate the topmost window hosting the AssociatedObject and then we hook into the message queue watching for the Window WM_MOVEING message. On each WM_MOVEING message, we update the placement of the popup. To get the Popup to recalculate its position we need to change the placement to something other than what it’s set as, i.e. it needs to change. Then when we reset the placement to our original placement, the Popup position is recalculated.

This code could be improved by checking the Placement and ensuring it changes – in the code above I simply set to PlacementMode.Relative because I know my code a different PlacementMode. Also the above code does not handle the Popup repositioning in the PlacementRectangle is used. Also my requirement doesn’t include the resizing of the PlacementTarget.

I’ll leave those changes until I need them…

Update

Seems I did need to handle resizing of the PlacementTarget so I’ve now added the following to the OnAttached method

AssociatedObject.LayoutUpdated += (sender, e) => Update();

and that appears to solve that problem.