Robert's blog
Robert Važan

Anchor WPF Popup exactly where you need it with PrecisePopup

PrecisePopup is a smart popup for WPF that can open in multiple relative positions depending on how much space there is around it on the screen.

Popup in WPF of course allows you to select one of the predefined locations relative to placement target. The trouble begins when your placement target is close to the edge of the screen. If the popup doesn't fit in the screen when positioned as specified, Popup control will automatically move it to ensure the whole popup is visible. This is okay for usual rectangular popups, but it's a headache for authors of "eared" or balloon popups that have tabpage-like ear or pointed anchor. PrecisePopup, part of my opensource JungleControls library, is designed to handle these scenarios.

Below you can see PrecisePopup in action. The window was moved so that default bottom-right placement would be obscured by taskbar. Instead of sliding the popup to the left like the built-in Popup does, PrecisePopup selected another placement (bottom-left) from its discrete list of allowed placements.

Demo showing behavior near screen edge
One of four possible placements is chosen to keep the popup fully visible.

Below you can see how the PrecisePopup was configured to yield the above behavior. Some details have been omitted for brevity.

<jc:PrecisePopup Name="MyPopup"
                 PlacementTarget="{Binding ElementName=MyButton}"
                 IsOpen="{Binding IsOpen}"
                 Background="#a0ffffff">
    <jc:PrecisePopup.Placements>
        <jc:PrecisePopupPlacement
            Tag="BottomRight" />
        <jc:PrecisePopupPlacement
            Tag="BottomLeft"
            HorizontalTargetAlignment="Right"
            HorizontalPopupAlignment="Right" />
        <jc:PrecisePopupPlacement
            Tag="TopRight"
            VerticalTargetAlignment="Bottom"
            VerticalPopupAlignment="Bottom" />
        <jc:PrecisePopupPlacement
            Tag="TopLeft"
            HorizontalTargetAlignment="Right"
            HorizontalPopupAlignment="Right" />
    </jc:PrecisePopup.Placements>
    <Border Width="200"
            Height="200"
            BorderBrush="Gray"
            BorderThickness="2">
        <TextBlock
            Text="{Binding SelectedPlacement.Tag,
                           ElementName=MyPopup}"
            HorizontalAlignment="Center"
            VerticalAlignment="Center" />
    </Border>
</jc:PrecisePopup>

PrecisePopup is configured with one or more PrecisePopupPlacement instances. Each PrecisePopupPlacement specifies location of popup relative to placement target. PrecisePopup never adjusts position specified in PrecisePopupPlacement. If the popup happens to fall off the screen, so be it.

The only thing that PrecisePopup does is to select one PrecisePopupPlacement from the list of possible alternatives. It will select the first PrecisePopupPlacement that fits on screen. If no one fits fully, PrecisePopupPlacement will select the one that has least area clipped behind screen edges. This allows you to define several layouts for your popup depending on where the popup is opened.

PrecisePopup auto-sizes to fit its contents. It is possible to limit popup size to screen bounds without moving the popup. Notice that the popup always grows in some direction to accommodate its content. For example, setting HorizontalPopupAlignment to Left causes the popup to grow to the right. Center alignment causes it to grow equally in both directions. If you set ClipToScreen on PrecisePopupPlacement, PrecisePopup will stop growing when it reaches screen boundary. For Center alignment, popup will stop growing when it hits screen boundary that is closer to popup center.

PrecisePopup properties:

PrecisePopupPlacement properties:

You can get PrecisePopup from NuGet as part of my opensource JungleControls library.

Comments

Nice control.
Any chance for a version with a dependency property that sets a margin to extend the screen nudge coordinates?

The reason is because I have a popup that has a rendered shadow, therefore it needs to extend the popup over its edges (using a negative margin of -32) to be able to render its shadow, but then the screen nudging takes this margin into account and nudges the shadow edge instead of the actual content.

So I think a popup property of Thickness OffsetScreenClip should be useful in this case (which I'd set to the same as the margin via binding).

Also, any chance for a StaysOpen?
xavi
Also I noticed it cannot be used as popup for menu styles, I just tried and it doesn't show unlike the regular popup :(
xavi
xavi, I no longer actively develop the library since I shifted my attention to web stack. You are however free to fork it and introduce the properties you need.
Robert
Comments are closed for this post.