Positioning Silverlight Popups Relative to a Control

Its important that when you think in terms of Silverlight controls, you always think in terms of XAML and that means thinking in terms of a Visual Tree.  Elements in the tree have parents and elements in the tree have children.  A popup control is no  different. Although it appears to look like this disassociated control floating around, it really is just another element in the Visual Tree (Unbeknownst to the naked eye).

In order to position a popup relative to an existing control, you should make it a child of the control.   If the control does not allow children, then put the control in a Grid and you can have all the children you want.  Here is an example.

  private void OnHoverTab(object sender, MouseEventArgs e)

  {

  var p = new Popup();

  p.DataContext = ((sender as FrameworkElement).Parent as  
            FrameworkElement).DataContext;

  p.Child = new UnderlyingUserControl();  // puts a user control in the popup

  p.VerticalOffset = 25; // this will offset us slightly from the
                          // parent

  p.HorizontalOffset = 0;

  p.IsOpen = true;

   // this is where we add the popup to a Grid we can position
   // against

  ((sender as FrameworkElement).Parent as Grid).Children.Add(p);

  }



Now once we dismiss our popup, it's important to remove it from the Grid, otherwise we'll end up with lots and lots of dead popups in our Grid tree.

I've added an OK and Cancel onto my Underlying user control to dismiss the popup, here is the code behind for the Child of the popup:

  private void OnOK(object sender, RoutedEventArgs e)

    {

      ((MyViewModel) DataContext).Name = NameInput.Text;

// Hide the popup and remove from the parent

      (this.Parent as Popup).IsOpen = false;

      ((this.Parent as Popup).Parent as  Grid).Children.Remove(this.Parent as Popup);

    }

 

    private void OnCancel(object sender, RoutedEventArgs e)

    {

    // Hide the popup and remove from the parent

      (this.Parent as Popup).IsOpen = false;

      ((this.Parent as Popup).Parent as Grid).Children.Remove(this.Parent as Popup);

    }