Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Full-featured XP Style Collapsible Panel

0.00/5 (No votes)
1 Nov 2004 176  
Source and demo application for a full-featured XP style collapsible panel.

Updates

Version 1.2 provides the ability to change the LinearGradientMode of the Caption and Panel gradients, and includes a few small bug fixes. The implementation now overrides DisplayRectangle which allows Docking and Anchoring of child controls to work as expected.

Version 1.1 provides two new layout controls and a small number of new features that simplify the creation of more sophisticated layouts and effects. The additions and changes for version 1.1 are listed at the end of the article.

Introduction

After reviewing quite a few of the collapsible panel style controls (both Open Source and commercial), I decided to write XPPanel to achieve a high quality, feature rich control that provided reasonably tight integration with the Visual Studio IDE.

The primary features I need from a Collapsible panel are:

  • Animated Expand/Collapse w/ Transparency
  • Gradient colors for both caption and panel
  • Customizable radius for caption rounded corners
  • Multi-line caption text
  • Support for images on both the left and right sides of the caption
  • Support for four image states (Normal, Highlight, Pressed, and Disabled)
  • Image transparency
  • Stand-alone panel (not part of a panel group)
  • A Panel Group to contain/manage individual panels
  • Fixed size and non-collapsible panels
  • Intelligent gray-scale rendering when disabled
  • Good integration with the Visual Studio IDE
  • Resource efficient
  • TextLayoutPanel and ItemLayoutPanel controls that dynamically resize based on content

Background

The real compelling reason for writing XPPanel was that I just needed to write some code. After weeks of writing software requirements, I was definitely having withdrawal. As I had spent a fair amount of time away from UI code, I thought it would be fun to get back into the swing of things by writing a new control. Unlike most of you, my experience with Windows XP has been limited, but by coincidence, I recently moved my primary development system from Windows 2000 Server to Windows XP Professional. Of course, I was bombarded with UI elements containing collapsible panels, and ergo, I figured why not?

Of course, I did my homework on the currently available implementations of collapsible panels, and I found two that were both high quality and had reasonable feature sets. I am happy to say that both projects are hosted on CodeProject and can be found at the following links:

Still, although I was impressed with various aspects of both, neither really fit the set of requirements that I wanted for my implementation. I actually thought about merging the two different projects to try and get a better control, but in the end, I decided to write my own implementation and borrow good techniques from those articles (as well as a few other CodeProject based articles!).

Even with good starting points for my control, I faced a bit of an uphill battle with getting the level of IDE designer support I desired, and although I struggled to get it all to work, in the end, I am moderately happy with the results.

Using the code

Like any other .NET Control, for use in the IDE, you should add the XPPanel controls to a Toolbox panel. This can be accomplished by right-clicking on a Toolbox tab and selecting "add/remove items...", browsing to the XPPanel assembly, and selecting it. This will add all the XPPanel controls to the Toolbox so they can be dragged/dropped to a Windows Form or User Control. For XPPanel, it is important to add the Debug version of the assembly for efficiency/size reasons, the designers are not included in the Release (redistributable) version.

There are two primary controls in the XPPanel assembly:

  • XPPanel
  • XPPanelGroup

An XPPanel is the control that defines a caption and panel (container). An XPPanel can be used standalone but is generally contained by an XPPanelGroup. An XPPanelGroup is a container for a collection of XPPanel controls, and provides automatic placement and relocation as they expand/collapse.

Generally speaking, create a new Windows Form, drag and drop an XPPanelGroup to the form, and if desired, dock the XPPanelGroup to the right or left side of the form. If the XPPanelGroup default gradient color is not appropriate, change the start and end colors for the gradient using the PanelGradient property. Now, drag and drop one or more XPPanel controls into the XPPanelGroup. Resize each XPPanel as appropriate. Notice that the XPPanelGroup manages the placement of each contained XPPanel, and has the properties BorderMargins and Spacing to provide control over the left/right and top margins of the panels in relationship to the container (XPPanelGroup). The Spacing property defines the distance between the bottom of one panel, and the top of the next.

One thing that the XPPanelGroup does not provide is default/ambient properties that can be inherited by contained XPPanels. This is a bit inconvenient since it means that each XPPanel has to have the appropriate properties set. This could be a laboriously manual process, but the IDE makes it relatively simple. Simply select each XPPanel that should have the same properties, and define those properties so that each selected XPPanel receives the appropriate property changes.

Once the basic properties (generally the color scheme) of the panels are set, it is now time to define the caption text and select any images and/or glyphs for the panels. Although this part is very simple, it is probably a slight deviation from what you are normally used to. For example, when you want to add images to a Toolbar or Button, you probably create an ImageList, add some images, then specify the image index as appropriate. You follow the exact same pattern here, but instead of using an ImageList, you will use an ImageSet which is a Component provided with XPPanel. The primary reason for using an ImageSet instead of an ImageList is that the ImageList contains a now infamous bug regarding 32-bit images and alpha transparency. Although there are code-level work-arounds, they are generally run-time corrections and hence are not applicable at design time.

ImageSet Component

To end my frustration with ImageList, I wrote ImageSet as an ImageList replacement, and overall, it has the exact same type of functionality. Drag and drop an ImageSet from the Toolbox, and it appears in the component tray. Select its properties, and it provides the same overall properties as ImageList: Size, Image collection, and TransparentColor. To add images to the ImageSet, click on the Image collection property and use the same ImageEditor provided for ImageList. The only real difference you should see is that the ImageSet doesn't obliterate your 32-bit images that contain alpha channels!

Once you have defined one or more ImageSet components, you can instruct an XPPanel to use that ImageSet by modifying one of the three image related properties:

  • ImageImages
  • ExpandedGlyphs
  • CollapsedGlyphs

The ImageItems property of XPPanel defines the images that appear on the left side of the caption and are generally indicative of the panel's purpose, while the glyph properties are the button-like images that appear on the right side of the caption. There are two sets of glyphs, one for when the panel is expanded (generally showing a collapse glyph), and one when the panel is collapsed (generally showing an expand glyph.)

The aforementioned properties are not actually of type ImageSet, they are of type StateImageItems which contains a reference to an ImageSet. The StateImageItems class also provides a property for each supported image state, Normal, Highlight, Pressed, and Disabled. Each of these properties is an integer index that maps into the specified ImageSet. My primary reason for doing this is that I was able to collapse five properties into a single top-level property on XPPanel. Considering that there are three different StateImageItems, that is a total of fifteen properties reduced to three top-level properties. In my opinion, this makes managing XPPanel properties a lot easier.

There is no requirement that you define any images for an XPPanel, and if you do use images, there is no requirement that you have a distinct image for each state. The XPPanel code will select the Normal image if a particular state is undefined. You can leave a state image index undefined by leaving it at its default value of -1. Also, if you don't want to provide custom glyph images, you can set the XPPanelStyle property to WindowsXP and the standard XP style glyphs are automatically used. There is the limitation that the Windows XP style glyphs are designed for a blue color-scheme, but this is a simple way to automatically get glyph images.

As far as Disabled state images go, there is no need to provide one as the drawing code uses smart gray scale color transformations when an XPPanel control is disabled, effectively rendering any image as a reasonable disabled version.

Custom Popup Image Editor

One nice feature that I provide is a custom UITypeEditor for selecting state images from an ImageSet. Each of the StateImageItems properties (once again, Normal, Highlight, Pressed, and Disabled) has a drop-down image picker that displays a panel with each of the images contained in the referenced ImageSet. The image popup editor uses another control that I provide with XPPanel (although it is only used by the popup image editor) called ImagePanel. ImagePanel is loosely based on another CodeProject article which you can find at the following URL:

Here is a sample of the ImagePanel in action in the VS.NET 2003 property grid.

Another thing you might notice as you set XPPanel properties is that I generally group related Color properties into meta-classes. Once again, this reduces the number of top-level properties, and groups related items together into simple drill-down types. For gradients, I defined GradientColor which defines Start and End properties. For pairs of colors (e.g., foreground/background), I defined ColorPair. Both classes provide a custom UITypeEditor so that the property grid contains a preview of the color combination as well as standard color pickers for browsing/defining colors.

Documentation

An NDoc configuration file is included in case you want to build the XPPanel documentation, or you can just download the pre-compiled version from CodeProject.

Overall Design/Implementation Strategy

My overall choices for the design of the XPPanel components were nearly identical to 90% of similar implementations. The collapsible panel acts as a single control with one area of the control dedicated to the caption, and the remaining area for the panel itself. Although, I think this is the best choice because it provides a loose coupling with the XPPanelGroup which is a custom container control for XPPanel instances.

Unfortunately, it does introduce some serious limitations with regard to placement of child controls on the panel itself. This is generally only a problem when the width or height of the caption area may change dynamically. If the caption is generally non-dynamic, you won't have any problems with the positioning of controls, otherwise you may need to adjust both the X and Y offset of controls (on the fly) on the panel to maintain constant spacing of the controls in the panel. Also, when in an XPPanelGroup, the width of the contained XPPanel controls may change as a vertical scrollbar is added/removed (AutoScroll is set to true). In order to address some of these issues, I have included two specialized panels that are designed to help manage layout of controls and text within the panels (more on these later.)

The most common alternative to this strategy is that the panel is the panel and nothing but the panel. The caption of the panel is actually a separate (non-control) entity. To use this strategy, the container (XPPanelGroup) needs to be more tightly coupled because it must determine the placement of the caption and panel, and is responsible for drawing the caption. When using this technique, the height of the panel, when collapsed, is zero (0), and you can use docking/anchoring with children of the panel. Of course, the XPPanel is no longer a stand-alone control because it is dependent upon its container to layout and draw the caption. This is probably not a huge limitation given that you will usually want the XPPanel to be managed by a container anyway. Still, I wanted any XPPanel to be a stand alone control and not require the use of an XPPanelGroup.

The second major design/implementation decision was to pre-calculate the layout and cache all the GDI+ drawing objects. This makes the drawing code simpler, and avoids a lot of redundant GDI+ object creation. The downside is that we hold onto GDI+ resources for longer periods of time. Given the small number of brushes and pens used to draw the XPPanel, this seems like a reasonable choice and should increase overall drawing performance. Each cached GDI+ object has a private property used to get/set the object. The get accessor automatically creates the object if it doesn't exist, and the set accessor intelligently disposes of any existing object.

The third major design/implementation area was events. Specifically, property change events and panel state change events. Property change events are important because they play a major part in determining when a cached GDI+ object is no longer useful, as well as detecting that the panel layout is no longer up to date and must be recalculated. Although the .NET Framework provides a standard PropertyChange event mechanism, I chose to use a proprietary form that provides the property that changed as an enumeration (rather than the string name). Overall, this is faster, and the events are only intended for internal use or by a derived class. For the most part, external entities do not need to hook property change events of XPPanel.

For panel state change events, I decided that this would be a more generic event that simply lets listeners know that a major change has/is occurring with the XPPanel. Examples of major changes are that the panel is expanded, collapsed, or when animation is used in the process of expanding/collapsing. It is also used when major control events occur, for example, OnSizeChanged, OnVisibilityChanged, and OnEnabledChanged. Any time one of these events occur, listeners are notified so they can take appropriate action. Of course, the primary listener is an XPPanelGroup that is managing one or more XPPanel instances and needs to reposition other instances based on the change.

In version 1.1, I have also added high-level events for when a panel is Collapsing, Collapsed, Expanding, and Expanded. This makes it a little simpler if you need to react to changes in panels.

The final area affecting the design/implementation was good integration of the control into the IDE designer. Generally speaking, this is relatively easy when we are exposing/hiding basic control properties. Difficulties arise when adding custom type converters, type editors, and dealing with special cases for designer serialization. I won't go over all the various attributes and techniques for integrating a control into the IDE as many other CodeProject articles do a great job explaining the basics, but it is all in the source code, so feel free to take a look.

Of special note was yet another CodeProject article that got me started in the right direction on how to create the type editor for StateImageItems, which required a drop-down style editor with a custom control: MenuItem Extender - add images and font support to your menu by Eugene Pankov.

Points of Interest

As far as the two main controls, XPPanel and XPPanelGroup go, XPPanel is where all the action really happens. While XPPanel is moderately large as far as source files go, all the core logic happens in a very small number of methods.

Calculating the Caption Layout

Key to drawing the caption of the XPPanel is determining its overall layout. The caption area of the XPPanel is made up of three primary parts: Image, Caption text, and Glyph. Technically, all three components are optional, but if you don't specify at least one, your XPPanel isn't going to be very interesting!

Once defined, the Image and Glyph are basically fixed sized (as defined by the appropriate ImageSet), so calculating the rectangles for them is relatively straightforward and is a prerequisite to calculating the bounding rectangle for the text. The primary reason for this is that the Caption text appears between the Image and the Glyph, and hence its bounding rectangle is a function of the overall widths and inter-item spacing.

Calculating the bounds of the Caption text

Because the length of the caption text can be quite arbitrary, we need to calculate its bounding rectangle within fixed horizontal extents (between the right edge of the Image and the left edge of the Glyph), but variable height extents as we want to allow the text to flow onto multiple lines if necessary. To measure the text extents, I use the MeasureString method of the GDI+ Graphics object, passing in the caption text, the caption font, and the available horizontal extents once the image, glyph, margins, and spacing are accounted for. Also included is a StringFormat object which specifies various options for text formatting. In this case, we are primarily interested in specifying the user selected vertical and horizontal alignment of the text. (See the VertAlignment and HorzAlignment properties.)

Calculating the bounding rectangle is a two-step process. The first pass is to determine how tall the bounding rectangle of the Caption text needs to be to avoid clipping. This value will be used as part of the process for calculating the best height of the XPPanel caption. Once the final dimensions of the caption have been calculated, the bounding rectangle for the Caption text is set to the largest available rectangle. This is important for proper alignment of the text.

Selecting the Best Height for the Caption

The height of the XPPanel caption is dependent on the height of the three caption items: Image, Caption text, and Glyph. It is also constrained to a reasonable minimum/maximum height. Although you might think that the height of the caption is equivalent to the tallest item that is part of the caption (plus spacing), I actually didn't want to take the image height into account (unless the FitToImage property is true) so that the top of the image could appear to extend outside the caption. What this means is that the caption is more a function of the text and glyph. In the case where the caption is shorter than its accompanying image, then the caption origin is offset, in the Y direction, an amount equal to Image.Height - Caption.Height.

The following sample images show XPPanel captions where the top of the image extends above the caption frame, and when the FitToImage property is applied to the caption.

As I mentioned previously, after the actual caption height and offset are determined, we have to recalculate the bounding rectangle for the text as it's possible the vertical extents of the caption may have grown. This is important because the user may specify both horizontal and vertical alignment, and the bounding rectangle of the text needs to reflect this in order to draw correctly.

Drawing the XPPanel

Caption Background

Drawing the caption background is mostly straightforward although handling the GradientOffset property (which specifies at what point the gradient actually starts) requires the use of a ColorBlend object (as follows):

if ((GradientOffset > 0.0) && (GradientOffset < 1.0))
{
    ColorBlend colorBlend = new ColorBlend() ;
    colorBlend.Colors = new Color [] { captionBrush.LinearColors[0], 
           captionBrush.LinearColors[0],captionBrush.LinearColors[1] } ;

    colorBlend.Positions = new float [] { 0.0f, (float) GradientOffset, 1.0f } ;
    captionBrush.InterpolationColors = colorBlend ;
}

Caption

Drawing the actual caption items is very straightforward. The basic process is to determine the appropriate image state based upon whether the mouse is over the caption, or the mouse button is down over the caption, or the XPPanel is Enabled/Disabled. When the XPPanel is disabled, we need to draw the images in a disabled state, and because we do not rely on having an appropriate image for the Disabled state, we use a color transformation matrix that converts the image to an appropriate gray scale rendering. Currently, to keep the image selection and drawing logic as simple as possible, we apply the gray scale color transformation even if an image is provided for the Disabled state. You might want to change this if your disabled images are superior to the images that result from the color transformation.

As far as the color transformation matrix goes, this is the most basic plain vanilla transformation, but it does a good job. If you don't like the results, you should be able to find alternative matrix definitions on the Internet. Specifically, the very popular RegionMaster controls by Mike Harsh includes a gray scale transformation designed to emulate Microsoft Outlook 2003. My impression from the comments is that the brightness of the colors of an image will be more faithfully reproduced than when using the generic transformation.

Finally, in order to draw the Caption text in a "disabled" state, I use the ControlPaint.DrawStringDisabled method (from the System.Windows.Forms namespace).

Panel

Drawing the panel is trivial. Essentially it is a gradient fill. The only special logic that is required is to ensure that the panel is actually visible (i.e., the XPPanel is not collapsed), and if necessary, draw the outline on the left, right, and bottom sides. In the case where the Start and End Color values for the PanelGraident property are Color.Transparent, we don't actually draw the panel area at all.

Managing the ExpandedHeight of an XPPanel

One interesting problem to solve was how to distinguish between, the user intentionally setting a new height value for an XPPanel, and when the change was programmatic due to collapsing/expanding. A second trick was to prevent the user from being able to resize the XPPanel while in the designer, when the XPPanel was collapsed. Along the same lines, when the XPPanel is collapsed in the designer, the serialized code sets the Width and Height accordingly. In essence, the expanded height of the XPPanel is lost.

As a result of these issues, the code detects Height modifications and makes various checks to determine if the action is a result of user code, XPPanel itself, or the designer. Depending on the specific circumstances, the internal property ExpandedHeight is recorded for future use. For example, when expanding the XPPanel, the final height is the ExpandedHeight.

Of course, the ExpandedHeight property needs to be serialized so that when the control is created, we know what the value of the ExpandedHeight property should be. Although I originally had the property serialized by the designer, I was unhappy with that, and decideded to provide a TypeConverter for XPPanel that would allow the designer to use a form of constructor that accepted the ExpandedHeight property as a parameter.

The following code shows the main logic of the XPPanelTypeConverter:

public override object ConvertTo(
   ITypeDescriptorContext context,
   System.Globalization.CultureInfo culture,
   object value,
   Type destinationType
   )
{

    // the designer wants an InstanceDescriptor

    if ((destinationType == typeof(InstanceDescriptor)) && 
                   (value is XPPanel)) {
        XPPanel xpPanel = value as XPPanel ;
        // Get our XPPanel(int) constructor

        ConstructorInfo ctorInfo = 
          typeof(XPPanel).GetConstructor(new Type [] { typeof(int) }) ;

        if (ctorInfo != null) {
            // use this constructor and pass in the ExpandedHeight

            value. Use false to say that

            // initialization is NOT complete

            return new InstanceDescriptor(ctorInfo, 
               new object [] { xpPanel.ExpandedHeight },false) ;

        }
    }

    return base.ConvertTo (context, culture, value, destinationType);
}

Animation

The final, big piece of the puzzle is the animation code. Of critical importance to me is that it provides transparency ala Windows XP. Fortunately for me, Daren May solved the basic problem in his CodeProject article, Just Another C# Collapsing Group Control, so all I really needed to do was incorporate it. Daren's algorithm was a little over-simplified, so I added a few things, rearranged the code a bit to suit my personal style, changed a few variable names; but at the end of the day, the basic mechanism is all Daren's.

One thing I am contemplating is taking the animation off a timer, and driving it in a tight loop using Thread.Sleep and Application.DoEvents. One of the primary reasons I think this is a good idea is that it can all be done in its own scope, so there would be no need for instance members to track things such as the current animation rate, opacity adjustments, or base control visibility.

XPPanelGroup - The XPPanel Container

XPPanelGroup is a special purpose container (derived from System.Windows.Forms.Panel) designed to contain and manage XPPanel controls. It provides automatic positioning of each contained XPPanel, and handles PanelStateChange events to react as individual XPPanel controls expand and collapse.

The constructor for XPPanelGroup sets the following control styles:

// Use these control styles for smoother drawing and transparency support


SetStyle(ControlStyles.ResizeRedraw, true);

SetStyle(ControlStyles.AllPaintingInWmPaint, true);

SetStyle(ControlStyles.UserPaint, true);

SetStyle(ControlStyles.DoubleBuffer, true);

SetStyle(ControlStyles.SupportsTransparentBackColor, true);

SetStyle(ControlStyles.ContainerControl, true);

By default, XPPanelGroup sets the AutoScroll property to true so that a vertical scrollbar will appear as necessary, and also sets the BackColor property to Color.Transparent. Both of these properties are overridden, and I use the false)> attribute to prevent them from appearing in the designer.

Managing controls

Management of contained XPPanel controls is handled by overriding the OnControlAdded and OnControlRemoved methods. In addition, an EventHandler for XPPanel.PanelStateChange provides additional support for dynamic changes, such as expanding/collapsing, or changes in visibility.

Internally, an ArrayList of contained XPPanel controls is maintained, as controls are added and removed. In addition, the ISupportInitialize interface is supported for two reasons:

  • Designer code serialization adds controls in reverse order (last to first)
  • Avoiding unnecessary XPPanel layout operations at initialization

The implementation of ISupportInitialize does nothing more then set a boolean flag saying whether we are currently initializing. If we are, we correct for designer control ordering, and we suppress layout until initialization is complete.

XPPanel Updates

The final piece of the puzzle are two routines, UpdatePanels and UpdatePanelsAfter which do the actual (re)sizing and placement of each XPPanel control. The routines are pretty straightforward, and take into account whether a control is visible or not.

One final thing to remember is that although XPPanelGroup is a general purpose container, it is only cognizant of XPPanel controls and does not provide automatic placement of other control types.

TextLayoutPanel

One of the most common uses for an XPPanel is to show a 'detail' panel for the current application context. A common example is selecting a file or folder in Windows Explorer. Depending on the type of the file, the panel might only show a few core bits of information such as 'date modified' and 'size', while for other file types such as audio or video files, the available information is much richer. While this extra level of context sensitive information is great for users, making visually appealing panels can be very time consuming. This is especially true if you need to layout controls within the panel via the designer and then implement a lot of run-time code to dynamically re-layout the information as the context changes. Another problem is getting variable length text elements to layout properly using existing controls.

I am not saying it is impossible, but why bother with all the hassles when you can use the TextLayoutPanel to create good visuals (in the designer) with very little effort, and a minimum of runtime code is required to update/add/remove information from the panel? And what if I told, you could define 'Style' elements that could be applied to text elements within the panel (or all panels across the application) and the entire look/color scheme can be changed instantly by changing the style? 'Pretty interesting...', you say, but you are a hard sell, so what about custom images for the text elements? And how about intelligent support for kerning so that related elements automatically group closer together while unrelated items have distinct separation? OK, you get the picture...

TextLayoutPanel was my solution to needing to build nice detail panels without a lot of wasted time and code. Personally, I do not have a lot of time to waste with design-time control placement, formatting, and especially runtime control placement as detail items change. So, in about an hour, I wrote TextLayoutPanel which uses light-weight TextElements to describe the panel's contents.

A TextElement is very much like a label, but is not a control (which is why I say that a TextElement is lightweight.) A TextElement defines its content (i.e., the display text) but a majority of the information related to its visualization (such as Font, ForeColor, and BackColor) are defined by its associated TextStyle. A TextStyle is a Component which defines visual information that can be used by any number of TextElements on the Form. The properties of the TextStyle do not need to be explicitly specified, and instead, the "default" values for those properties are determined by querying the parent TextLayoutPanel. This allows a TextStyle to modify only a few aspects of the visualization and leave the other properties to act as if they were ambient.

TextElements are stored in the Elements collection of the TextLayoutPanel. The overall layout of the TextLayoutPanel is determined by calculating the bounding rectangle of the TextElement, given its Font, Image, and textual content. Each TextElement is associated with a TextStyle which defines a 'Spacing' adjustment between a TextElement, the previous TextElement, and the TextElement that follows. In the example image, you will notice that the 'MPEG Layer 3 Audio' TextElement is much closer to the proceeding TextElement than it is to the succeeding TextElement. This is the effect of the TextStyle.SpacingAdjustment property.

TextElements can be hidden/shown so that items not relevant to the current context are not visible and do not effect the overall layout.

TextLayoutPanel is a stand-alone control and can be used in non-XPPanel situations, but because it is explicitly designed to work with XPPanel, it can be made to dynamically resize the XPPanel as the content changes. This is exactly how Windows XP Detail panels work.

The demo application contains an example of using the TextLayoutPanel and a quick perusing of the implementation, and HTML documentation should make it all pretty clear.

ItemLayoutPanel

Of course, TextLayoutPanel is great, but what about when you need real controls that can be clicked and disabled? To solve this common problem, I wrote ItemLayoutPanel which provides automatic layout of controls within a panel. The actual layout algorithm is called via an interface so that you can write your own customized layout engine. Of course, I provide a default implementation that does a simple left-alignment of all the controls, taking into account the border and spacing properties of the ItemLayoutPanel.

ItemLayoutPanel (like TextLayoutPanel) is a stand alone control, but can be configured to dynamically resize its parent XPPanel based on visible content. Also, like TextLayoutPanel, a key strategy is to place a collection of controls onto the panel but only make visible those that are relevant to the current context. This minimizes the amount of work you need to do to dynamically change the visuals.

Because the order that controls are added to the panel (in the designer) is not necessarily the order in which they should appear, ItemLayoutPanel provides the ability to move/reorder controls on-the-fly.

The demo has two panels which use ItemLayoutPanel. The one shown in the example image also includes programmatic animation of the XPPanel when you click one of the LinkLabels.

Conclusion

Writing this control was a lot of fun and a great experience. It is really amazing to me what an incredible resource CodeProject is. Regardless of the specific problem or area of interest, I was always able to find some relevant information or code to get me going in the right direction.

Hopefully, this article and implementation is a good example of what it takes to write a high-quality .NET control, and I hope you have a great experience using and extending the controls and components in XPPanel. If you are already using version 1.0 and want to upgrade to version 1.1, be aware that the assembly is signed, and no attempt has been made at maintaining backward compatibility with serialized information. Not much has changed, but the designer will be looking for version 1.0.0.0, and should you recompile version 1.1.0.0 as version 1.0.0.0, you are likely to encounter minor problems in the designer due to small, unavoidable changes.

I did my best to write this article with Lutz Roeders Writer, which is a nifty little HTML based editor. It is still very immature (it is currently a .4 release), but I really found it to be terribly useful and I hope that it continues to evolve and grow.

The icons I used were created by Farkus and can be obtained from FarkusXP Icon Set.

Version 1.2 Updates

The following improvements were made:

XPPanel

  • Overrode DisplayRectangle so that Docking and Anchoring of child controls work properly.
  • Added CaptionGradientMode and PanelGradientMode properties to control corresponding LinearGradientMode.
  • Added CaptionCornerType to allow custom corners for the caption.
  • Added support for negative values for CaptionGradientOffset (inverts gradient colors in ColorBlend).
  • Minor changes to ItemLayoutPanel, mostly related to the designer.

The following bug fixes were added for Version 1.2:

  • Fixed reported problem with PanelState where Expand code did not work correctly at design-time.
  • To avoid issues, OnVisibleChange no longer generates PanelStateChange events. Just hook VisibleChange directly.
  • ItemLayoutPanel.OnLayout now calls base.OnLayout().

Version 1.1 Updates

The following improvements were added for Version 1.1:

New Controls

  • TextLayoutPanel - Provides easy creation of sophisticated 'Detail' panels which dynamically auto-size to fit their contents.
  • ItemLayoutPanel - Provides easy creation/layout of control based panels which dynamically auto-size to fit their contents.

XPPanel

  • PanelHeight property - Allows the size of the panel area to be explicitly specified.
  • TogglePanelState() - Allows the Expand/Collapse state of an XPPanel to be programmatically toggled (including animation).
  • Ability to specify completely transparent panel area.
  • GradientOffset property is now handled with a single brush/path and a ColorBlend.
  • Events - Collapsed, Collapsing, Expanded, Expanding.

XPPanelGroup

  • MovePanel - Method to reorder/reposition XPPanel's within the group.
  • EnsureVisible - Method to ensure that the maximum possible area of an XPPanel is visible.

The following bug fixes were added for Version 1.1:

  • ImageCollection - In one constructor, the MakeTransparent() call proceeded the image actually being drawn in the Bitmap (oops!).
  • XPPanelGroup - The UpdatePanelsAfter(XPPanel) method contained an egregious 'cut & paste' error that caused layout issues when the update was invoked due to a panel changing visibility.

Many of these fixes and improvements came from you, and I appreciate everyone's comments and suggestions. If you have any ideas on how I can improve these controls (sans Theming Support) or make using them easier, please drop me an e-mail.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here