13

Is there any way to stop a WPF Popup from repositioning itself when it goes off-screen?

I found this old question, but it didn't get a proper answer to it. Is there any way to do this? I'm willing to subclass it if necessary. Thanks.

5 Answers 5

6

As Andrei points out, this behavior is deep inside the Popup control and hard to overcome. If you are willing to do some work it can be done by resizing and translating the content of the popup when it reaches the screen edges. For the purposes of the demonstration, we'll focus on the left edge of the screen.

If we have some XAML like this:

<Window ...
        LocationChanged="Window_LocationChanged"
        SizeChanged="Window_SizeChanged"
        >
    <Grid>
        <Rectangle Name="rectangle1" Width="100" Height="100" Fill="Blue"/>
        <Popup Name="popup1" PlacementTarget="{Binding ElementName=rectangle1}" IsOpen="True" Width="100" Height="100">
            <TextBlock Background="White" TextWrapping="Wrap" Width="100" Height="100">
                <TextBlock.Text>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</TextBlock.Text>
            </TextBlock>
        </Popup>
    </Grid>
</Window>

and code-behind like this:

private void Window_LocationChanged(object sender, EventArgs e)
{
    RefreshPopupPosition();
}

private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    RefreshPopupPosition();
}

private void RefreshPopupPosition()
{
    var upperLeft = rectangle1.PointToScreen(new Point(0, 100));
    var xOffset = Math.Min(0, upperLeft.X);
    popup1.Width = xOffset + 100;
    (popup1.Child as FrameworkElement).Margin = new Thickness(xOffset, 0, 0, 0);
    popup1.HorizontalOffset += 1;
    popup1.HorizontalOffset -= 1;
}

then by calculating that the Popup would be off-screen, we can reduce the width of the content and give it a negative margin so that the portion that is on-screen is clipped to what would have appeared if the Popup were to allow this.

This would have to be extended to deal with all four edges of the screen and the possibility of multiple screens, but it demonstrates that the approach is workable.

2

I don't think there is a way to do this. At least a clean way. If you peek to the Popup class with the Reflector, you'll find an UpdatePosition method that will adjust the location of the popup so that it stays between screen bounds. This method is called from several places, from On***Changed callbacks (OnPlacementChanged, OnVerticalOffsetChanged, etc).

If you're really really want to get this functionality, you might have a small chance to extend the Popup class, override some of the metadata on the properties that registered with the On***Changed callbacks so that UpdatePosition never gets called, however most people will consider doing this to be pure evil, I don't recommend it either.

2

there is no way on wpf framework to put Popup out of screen , but you can force popup position by calling "SetWindowPos" via p/invoke:

#region P/Invoke imports & definitions

        private const int HWND_TOPMOST = -1;
        private const int HWND_NOTOPMOST = -2;
        private const int HWND_BOTTOM = 1;
        private const int SWP_NOMOVE = 0x0002;
        private const int SWP_NOSIZE = 0x0001;
        private const int SWP_NOACTIVATE = 0x0010;
        private const int GW_HWNDPREV = 3;
        private const int GW_HWNDLAST = 1;


  [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
        private static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags); 

 #endregion

 private void updateposition(Item item)
        {
            if (item.IsOpen)
            {
                IntPtr hwnd = ((HwndSource)PresentationSource.FromVisual(item.popup.Child)).Handle;
                SetWindowPos(hwnd, 0, (int)item.WorkPoint.X, (int)item.WorkPoint.Y, (int)item.popup.Width, (int)item.popup.Height, SWP_NOSIZE | SWP_NOACTIVATE);

            }


        }
0

You can use PrecisePopup control from my opensource JungleControls library. It will position the popup exactly as specified even if that means the popup is partially off-screen.

It can automatically select the most suitable placement from among the placements you define, but if you just need one exact popup placement that corresponds to "Bottom" placement of the built-in Popup, you can do it like this:

<jc:PrecisePopup PlacementTarget="{Binding ElementName=SomeTarget}"
                 IsOpen="{Binding IsOpen}">
    <jc:PrecisePopup.Placements>
        <jc:PrecisePopupPlacement VerticalTargetAlignment="Bottom" />
    </jc:PrecisePopup.Placements>
    <!-- Popup content here... -->
</jc:PrecisePopup>
-1

You have to place the popup and its target inside the Canvas panel so that it wont reposition due to the screen edge problem in popup control.

          <Canvas>
            <Rectangle Name="rectPopUp" Width="30" Height="30"/>
            <Button Width="20" Height="20" Name="btnAppLauncher" Click="btnAppLauncher_Click" Margin="-5,-2,0,0">
                <Button.Content>
                    <Path Fill="White" Stretch="Uniform" Data="M16,20H20V16H16M16,14H20V10H16M10,8H14V4H10M16,8H20V4H16M10,14H14V10H10M4,14H8V10H4M4,20H8V16H4M10,20H14V16H10M4,8H8V4H4V8Z"></Path>
                </Button.Content>
            </Button>
            <Popup Name="pnlAppLauncherPopup"  PlacementRectangle="0,25,0,0" PlacementTarget="{Binding ElementName=btnAppLauncher}" Placement="Bottom" AllowsTransparency="True" PopupAnimation="Slide">
                <UniformGrid Name="pnlAppLauncher">
                </UniformGrid>
            </Popup>
        </Canvas>

Since the whole unit is inside canvas panel and placement rectangle is set the popup will show below the rectangle.

1
  • This solution does not work for me. When moving my popup near the screen edge it repositions itself accordingly regardless of the Canvas container. I tried a peer placement target as well as using the default parent.
    – Brannon
    Commented Oct 20, 2016 at 21:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.