Click here to Skip to main content
15,893,161 members
Articles / Desktop Programming / WPF

XPlorerBar: A WPF Windows XP Style Explorer Bar Control

Rate me:
Please Sign up or sign in to vote.
4.95/5 (168 votes)
9 Dec 2008CPOL11 min read 344.7K   9.3K   408  
A fully customizable WPF implementation of the left side pane that was introduced in Windows XP's Explorer.
#region [       Copyright © 2008, Zona-Tools, all rights reserved.       ]
/*
 * 
    This source code is licensed under the Code Project Open License (CPOL).
    Check out http://www.codeproject.com/info/cpol10.aspx for further details.
 * 
*/
#endregion


#region [       Using namespaces       ]

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;

#endregion


namespace ZonaTools.XPlorerBar
{
    /// <summary>
    /// Represents an expandable/collapsible container in which any content can 
    /// be placed. 
    /// </summary>
    [TemplatePart(Name = "PART_HeaderSite", Type = typeof(ToggleButton))]
    [TemplatePart(Name = "PART_ContentSite", Type = typeof(XPandableDecorator))]
    public class XPlorerSection : HeaderedContentControl
    {
        #region [       Constructor       ]

        //===========================================================================
        /// <summary>
        /// Static constructor used to override the dependency properties (if
        /// needed) and define the default style for the control.
        /// </summary>
        //===========================================================================
        static XPlorerSection()
        {
            //Provides a new default style for the XPlorerSection element
            DefaultStyleKeyProperty.OverrideMetadata(typeof(XPlorerSection), 
                new FrameworkPropertyMetadata(typeof(XPlorerSection)));
        }

        #endregion


        #region [       Dependency properties       ]

        #region IsExpanded property

        //===========================================================================
        /// <summary>
        /// Gets or sets a value indicating whether the content window is visible. 
        /// This is a dependency property.
        /// </summary>
        /// <remarks>
        /// The default value is <c>false</c>.
        /// </remarks>
        //===========================================================================
        [Category(XPlorerBar.CATEGORYNAME), Browsable(true)]
        public bool IsExpanded
        {
            get { return (bool)GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="IsExpanded"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded",
            typeof(bool), typeof(XPlorerSection), 
            new PropertyMetadata(false, new PropertyChangedCallback(OnIsExpandedChanged)));

        /// <summary>
        /// Invoked whenever the <c>IsExpanded</c> dependency property value
        /// has been updated.
        /// </summary>
        /// <param name="sender">The <c>DependencyObject</c> on which the property
        /// has changed value.</param>
        /// <param name="e">Event data that is issued by any event that tracks changes 
        /// to the effective value of this property. </param>
        private static void OnIsExpandedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            //Gets the XPlorerSection instance who sent the event
            XPlorerSection section = sender as XPlorerSection;
            if ((section == null) || (section.Template == null))
                return;

           //Gets the new value of the 'IsExpanded' DP
            bool newIsExpandedValue = (bool)e.NewValue;

            if (newIsExpandedValue)
                section.RaiseEvent(new RoutedEventArgs(XPlorerSection.ExpandedEvent, section));
        }

        #endregion

        #region IsPrimary property

        //===========================================================================
        /// <summary>
        /// Gets or sets a value indicating whether the element has a
        /// different look and feel. This is a dependency property.
        /// </summary>
        /// <remarks>
        /// The default value is <c>false</c>.
        /// </remarks>
        //===========================================================================
        [Category(XPlorerBar.CATEGORYNAME), Browsable(true)]
        public bool IsPrimary
        {
            get { return (bool)GetValue(XPlorerSection.IsPrimaryProperty); }
            set { SetValue(XPlorerSection.IsPrimaryProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="IsPrimary"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsPrimaryProperty =
            DependencyProperty.Register("IsPrimary", 
            typeof(bool), typeof(XPlorerSection),
            new FrameworkPropertyMetadata(false));

        #endregion

        #region HeaderImage property

        //===========================================================================
        /// <summary>
        /// Gets or sets the image source of the image to associate with the text in 
        /// the header of an XPlorerSection. This is a dependency property.
        /// </summary>
        /// <remarks>
        /// The default value is <c>null</c>.
        /// </remarks>
        //===========================================================================
        [Category(XPlorerBar.CATEGORYNAME), Browsable(true)]
        public ImageSource HeaderImage
        {
            get { return (ImageSource)GetValue(HeaderImageProperty); }
            set { SetValue(HeaderImageProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="HeaderImage"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty HeaderImageProperty =
            DependencyProperty.Register("HeaderImage",
            typeof(ImageSource), typeof(XPlorerSection),
            new PropertyMetadata(null));

        #endregion

        #region ExpandCollapseDuration property

        //===========================================================================
        /// <summary>
        /// Gets or sets the duration (in milliseconds) of the expanding 
        /// (or collapsing) animation. This is a dependency property.
        /// </summary>
        /// <remarks>
        /// The default value is 1000 milliseconds.
        /// </remarks>
        //===========================================================================
        [Category(XPlorerBar.CATEGORYNAME), Browsable(true)]
        public Double ExpandOrCollapseDuration
        {
            get { return (Double)GetValue(XPlorerSection.ExpandOrCollapseDurationProperty); }
            set { SetValue(XPlorerSection.ExpandOrCollapseDurationProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="ExpandOrCollapseDuration"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ExpandOrCollapseDurationProperty =
            DependencyProperty.Register("ExpandOrCollapseDuration",
            typeof(Double), typeof(XPlorerSection),
            new FrameworkPropertyMetadata(1000.0));

        #endregion

        #endregion


        #region [       Routed events       ]

        #region Expanded event

        //===========================================================================
        /// <summary>
        /// Occurs when the content window of an XPlorerSection opens to display 
        /// both its header and content. This is a bubbling event.
        /// </summary>
        //===========================================================================
        public event RoutedEventHandler Expanded
        {
            add { AddHandler(ExpandedEvent, value); }
            remove { RemoveHandler(ExpandedEvent, value); }
        }
        /// <summary>
        /// Identifies the <see cref="Expanded"/> routed event.
        /// </summary>
        public static readonly RoutedEvent ExpandedEvent =
            EventManager.RegisterRoutedEvent("Expanded", 
            RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), typeof(XPlorerSection));

        #endregion

        #endregion


        #region [       Template customization       ]

        //===========================================================================
        /// <summary>
        /// Defines the <c>PART_HeaderSite</c> and <c>PART_ContentSite</c>
        /// behaviors.
        /// </summary>
        //===========================================================================
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            //Gets the header part
            ToggleButton header = GetTemplateChild("PART_HeaderSite") as ToggleButton;
            if (header != null)
            {
                //Binds the ToggleButton's IsChecked property to the IsExpanded property 
                Binding bindIsChecked = new Binding();
                bindIsChecked.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent); 
                bindIsChecked.Path = new PropertyPath("IsExpanded");
                bindIsChecked.Mode = BindingMode.TwoWay;
                header.SetBinding(ToggleButton.IsCheckedProperty, bindIsChecked);
            }

            //Gets the content part
            XPandableDecorator content = GetTemplateChild("PART_ContentSite") as XPandableDecorator;
            if (content != null)
            {
                //Binds the XPandableDecorator's IsExpanded property to the IsExpanded property
                Binding bindIsExpanded = new Binding();
                bindIsExpanded.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
                bindIsExpanded.Path = new PropertyPath("IsExpanded");
                content.SetBinding(XPandableDecorator.IsExpandedProperty, bindIsExpanded);

                //Binds the XPandableDecorator's ExpandOrCollapseDuration property to the XPlorerSection's one
                Binding bindDuration = new Binding();
                bindDuration.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
                bindDuration.Path = new PropertyPath("ExpandOrCollapseDuration");
                content.SetBinding(XPandableDecorator.ExpandOrCollapseDurationProperty, bindDuration);
            }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
France France
I have been developing and managing projects for real-time embedded softwares for eight years. Then, I moved from Paris to the south of France and began to lead a team who was developping Java applications.

My main occupation right now is to continue my journey in the WPF world.

You can check out my blog here. [^]

Comments and Discussions