wpfxamlcontentcontrol

pop up not showing up


Recently we moved our WPF Application on dot net 6, and we discovered that one of the one of the control is not working. It kind of a custom combo box control that work as a popup to a button, so whenever we click on the button a popup is shown, but after migration that pop up is not visible.

// this is the control that is supposed to popup on button click
public class BalonCtrl : ContentControl
{
  static BalonCtrl()
  {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(BalonCtrl), 
    new FrameworkPropertyMetadata(typeof(BalonCtrl)));
  }

  // some properties like 

  public void Show()
  {
    var popup = new PopUp()
    {
      RenderTransform = new ScaleTransform(App.Window.ApplicationScale, App.Window.ApplicationScale),
      Focusable = true,
      MinWidth = MinWidth,
      PlacementTarget = Target as FrameworkElement,
      Child = this, // <== Delegate the Content to the Popup so that it can render it
      AllowsTransparency = true,
      StaysOpen = false,
      Placement = Placement,
      PlacementRectangle = PlacementRectangle,
      PopupAnimation = PopupAnimation.Slide
    };

     popup.isOpen = true;
  }

}

// This is how the above piece of code is being called

onButtonclick()
{
 
 var list = new windows.ComboBoxPopup();

 var balloon = new windows.BalonControl()
 {
   Content = list,
 };

ballon.show();


}



When I remove the Content, which a content propert in ContentControl class, from the balloon object and set listbox as child directly in BallononControl show method, the pop up works, just styling is the issue.

I'm mostly concerned with why this behavior is happening is there something I can do to fix it.


Solution

  • It doesn't look like it has anything to do with .NET 6. It rather looks like a refactoring error.

    The point is that BalloonControl is a ContentControl. In order to make it show it must be a child of a visual tree (which BalloonControl isn't). The only chance to display any content is to delegate it to the Popup that BalloonControl creates and shows. But you never do this. Instead, you set the Popup.Child to the BalloonControl instance itself. Just fix the Popup initialization.

    Important!
    You are currently creating a potential memory leak for the BalloonControl. If you retain a reference to a BalloonControl instance and call Show again, the old Popup won't get garbage collected because the event handlers of BalloonControl are keeping it alive. To be on the safe side for the future (when you forgot about those details or when other developers that don't know the implementation details use that code) you should a) use the WeakEventManager to listen to the Popup events or b) unregister all event handlers from the Popup.Closed event or c) reuse the Popup and only update the Popup.Child property (lifetime of Popup and BalloonControl is now the same).

    The fixes applied to your example:

    public class BalloonControl : ContentControl
    {
      static BalloonControl()
      {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(BalloonControl), new FrameworkPropertyMetadata(typeof(BalloonControl)));
      }
    
      // some properties like 
    
      public void Show()
      {
        var popupContent = this.Content;
        this. Content = null;
        _Popup = new Popup()
        {
          RenderTransform = new ScaleTransform(App.Window.ApplicationScale, App.Window.ApplicationScale),
          Focusable = true,
          MinWidth = MinWidth,
          PlacementTarget = Target as FrameworkElement,
          Child = popupContent, // <== Delegate the Content to the Popup so that it can render it
          AllowsTransparency = true,
          StaysOpen = false,
          Placement = Placement,
          PlacementRectangle = PlacementRectangle,
          PopupAnimation = PopupAnimation.Slide
        };
    
        this.SizeChanged += OnSizeChanged;
        _Popup.Opened += OnPopupOpened;
        _Popup.Closed += OnPopupClosed;
        _Popup.IsOpen = true;
      }
    
      private void OnPopupOpened(object sender, EventArgs e)
      {
        Mouse.Capture(_Popup.Child, CaptureMode.SubTree);
        // right now up or down only
        if (PointerOrientation == Common.PointerOrientation.Bottom)
        {
          var target = Target as FrameworkElement;
          if (target != null)
          {
            Point pt = target.TranslatePoint(PlacementRectangle.TopLeft, Content as FrameworkElement);
            if (pt.Y > 0)
            {
              PointerOrientation = Common.PointerOrientation.Top;
            }
          }
        }
      }
    
      private void OnPopupClosed(object sender, EventArgs e)
      {
        this.SizeChanged -= OnSizeChanged;
        _Popup.Opened -= OnPopupOpened;
        _Popup.Closed -= OnPopupClosed;
    
        if (IsCancelled)
        {
          if (OnCancel != null) OnCancel(this, new EventArgs());
        }
        else
        {
          if (OnApply != null) OnApply(this, new EventArgs());
        }
      }
    
      private void OnSizeChanged(object sender, SizeChangedEventArgs e) 
      {
        if (Placement == PlacementMode.Top || Placement == PlacementMode.Bottom)
        {
          if (UseCanvasScale)
          {
            Graphic.GraphicCanvas canvas = Graphic.GraphicCanvas.FocusedCanvas;
            _Popup.HorizontalOffset = (-e.NewSize.Width / 2) / canvas.CanvasScale.ScaleX;
          }
          else
          {
            _Popup.HorizontalOffset = -e.NewSize.Width / 2;
          }
        }
      }
    }