WPF Sample Series – Expander Control With Popup Content

Collapsed

 ExpandersCollapsed

Expanded

 ExpandersExpanded

This is the next sample in the WPF Sample Applications Series. The purpose of the Sample Series is to provide concise code solutions for specific programming tasks. This sample provides a brief description of the problem, the solution and full source code.

Yesterday I was working on a Validation Summary control similar to the ASP.NET Validation Summary for my WPF business applications.  I wanted to use the Expander control to display the validation errors to the user.  So I dove in, and quickly realized that the Expander control doesn’t have built in behavior to overlay other controls when I expands. 

I searched the web, reviewed my WPF books, viewed example code for an hour but couldn’t find what I was looking for.  To make sure I wasn’t missing something I even rang up a great friend and WPF expert to confirm this is the standard Expander control behavior.

There are several solutions for this problem.  You could restyle other WPF controls that have dropdown overlay capabilities built in, could use a PopUp control or you could load up a ToolTip with the information.

I like the Expander control and wanted to use it.  It remains expanded until either closed by the user or programmatically and Vista users have seen and used this control (or similar control) and know what it does and how it works.

Note: The Expander control has some specific guidelines about setting the Height or Width properties that you should read at MSDN Expander.

The solution I came up with was to wrap the Expander control in a Canvas control.  The Canvas control’s Height was defined and limited by the Grid.Row Height.  I could have also set the Canvas Height property and set the Grid.Row Height to Auto.  I prefer the container controls to manage the layout when possible.  I have tried all the WPF container controls, but only the Canvas control allows this to work as described.

When the Expander control expands, the parent Canvas will not resize.  Since the Canvas control’s ClipToBounds property defaults to False, child controls are permitted to extend past the boundary of the Canvas control.

We still have one more detail to cover.  How to get the Expander’s Content to overlay the controls below it and appear on top.  By setting the Canvas control’s attached property, Panel.ZIndex to 99, the WPF layout system to render the Canvas and it’s child controls on top of other controls that have a lower Panel.ZIndex property values.  Presto, we have an Expander control that over lays controls below it.

Getting the Expander control Content to look like a real PopUp window or ToolTip is super easy in WPF.  I added a DropShadowBitmapEffect to Grid control that lays out the Expander control’s expanded content.

Downloads

After downloading, you must rename the following download by changing the file extension from .doc to .zip. This is a requirement of WordPress.com.

Solution

Here are the bullet points of this solution:

  • Wrap the Expander control inside a Canvas control.
  • Constrain the Canvas height to the desired height when the Expander control is collapsed.
  • Set the Canvas Panel.ZIndex higher than controls you want to overlay.
  • Style and design your Expander Content so that it looks like a PopUp.
<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Expender WPF Sample" Height="300" Width="400"
    >
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Grid Grid.Column="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Expander HorizontalAlignment="Left" Header="Standard Expander"
                  VerticalAlignment="Top" ExpandDirection="Down" Width="150">
            <TextBlock TextWrapping="Wrap" Background="AntiqueWhite">
                This is the standard expander
                behavior.  The expander opens
                and the controls below it
                move down.
            </TextBlock>
        </Expander>
        <StackPanel Grid.Row="1"  Margin="10,5,0,0">
            <RadioButton Content="Choice One"/>
            <RadioButton Content="Choice Two"/>
            <RadioButton Content="Choice Three"/>
        </StackPanel>
    </Grid>

    <Grid Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="33" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Canvas Panel.ZIndex="99">
            <Expander HorizontalAlignment="Left" Header="PopUp Window Expander"
                      VerticalAlignment="Top" ExpandDirection="Down" Width="175">
                <Grid Background="Cornsilk">
                    <Grid.BitmapEffect>
                        <DropShadowBitmapEffect />
                    </Grid.BitmapEffect>

                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <TextBlock Text="How Cool Is This!" FontWeight="Bold"
                               Margin="5"/>
                    <TextBlock Grid.Row="1" TextWrapping="Wrap" Margin="5">
                        This is the popup expander
                        behavior.  The expander opens
                        and overlays the controls
                        below it.
                    </TextBlock>
                </Grid>
            </Expander>
        </Canvas>
        <StackPanel Grid.Row="1" Margin="10,5,0,0">
            <RadioButton Content="Choice One"/>
            <RadioButton Content="Choice Two"/>
            <RadioButton Content="Choice Three"/>
        </StackPanel>
    </Grid>
</Grid>
</Window>

Hope you can learn just a little bit more about WPF from this article and the Sample Series.

Just a grain of sand on the worlds beaches.

13 Responses to “WPF Sample Series – Expander Control With Popup Content”

  1. Josh Smith Says:

    Great! I’m glad this worked out for you. Nice solution! Thanks for sharing it. :)

    Josh

  2. Karl Shifflett Says:

    Thanks Josh!

  3. Tanveer Badar Says:

    Great elaboration. But as I am always the one to point out things, so be it. :)

    1 – “and Vista users have seen and used this control (or similar control) and know what it does and how it works.”. Expander control is present in Windows XP too whether or not available through comctl32.dll, but there. If you are not using classic folders, the collapsable views on left are expander controls.

    2- “By setting the Canvas control’s attached property, Panel.ZIndex to 99,”. Any value larger than the maximum of controls to hide will do.

    3-

    Be really careful with this. Bitmap effects make their target render in S/W, no H/W acceleration whatsoever. Any fancy message and you wish you hadn’t done that in the first place.

  4. Karl Shifflett Says:

    The idea here is that he expander in this case, “DOES” overlay controls below it (as opposed to pushing them down) and that it looks like a popup window.

  5. treetopvt Says:

    Is this possible inside a itemscontrol template? I would like to show more data over other items in a lookless listbox when a user uses an expander.

  6. chaiguy1337 Says:

    Very cool Karl, however I was a bit disappointed it wasn’t implemented as an actual Popup class that would be able to display outside the bounds of the window. I guess this isn’t what you were going for though–neat nevertheless.

  7. Karl Shifflett Says:

    Chaiguy,

    Purpose was to enhance the Expander rather than write a new control. But this would be possible.

    Cheers,

    Karl

  8. markbjerke Says:

    Hi:

    This was very interesting thank you. One thing I have a hard time understanding is why the Expander’s Header is not raised off of the rest of the UI since it is in the Canvas along with the expanded content? The Expander is a child of the Canvas so I would think the Header would behave similiarly to the expanded content. Perhaps the Header sets a local Z-index value so it looks as thought it’s not part of the content ???

    So I”m a bit confused by this.

  9. Karl Shifflett Says:

    Mark,

    Good question. I’ve never thought about it. Just glad my solution works.

    Cheers,

    Karl

  10. matelich Says:

    Love this! Have a user who *hates* controls to move around on the screen, so this is perfect. Also, the first place I tried this out, the expander is in the same grid column as a much wider control, the popup is nicely sized to the contents rather than the column (I coulda played with the HorizontalAlignment, but the defaults with the popup were just much nicer)

    thanks.

  11. Karl Shifflett Says:

    Glad you like this one!

    Cheers,

    Karl

  12. bcs346 Says:

    I think this is a great example. One question though. Lets say that the expander is the descendant of several container controls, but ultimately a ScrollViewer. Lets say that the expander is in a location such that when it get’s expanded, it’s content extends out past that of the viewing area of the ScrollViewer and is clipped. Is there any way of notifying the ScrollViewer of this without changing the dimensions of the Canvas that this has occurred?

  13. Karl Shifflett Says:

    bcs346,

    There is a property called ClipToBounds. When set to true, it “clips” child element trying to extend past the parent.

    You may be able to set the property to False so that the popup would not get clipped.

    Cheers,

    Karl

Leave a Reply

You must be logged in to post a comment.