Click here to Skip to main content
Email Password   helpLost your password?

XP_Metallic.pngOdyssey

Introduction

This is a WPF Explorer Bar similar to the Explorer bar in Windows XP. An explorer bar usually contains one ore more collapsible panels, as shown above.

Using the code

WPF offers very nice animation features to allow almost anything you can imagine. Unfortunately, for a generic panel that supports animation while expanding/collapsing, it's not just as simple as defining DoubleAnimation to the Height property. Although it would work for ScaleTransform.ScaleY, the effect would be different to what we see in XP. Therefore, I use a custom Decorator control, which, in a few words, is a panel that can contain only one child. The AnimationDecorator has an IsExpanded property that specifies whether the decorator is expanded or collapsed. To perform the animation, I added a helper property named YOffset that will be animated. YOffset has a range from 0 to the ActualHeight of the decorator, and is used at ArrangeOverride and MeasureOverride to perform the animation. The code looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Odyssey.Controls
{

public class AnimationDecorator : Decorator
{

    static AnimationDecorator()
    {
    }

    public AnimationDecorator()
    : base()
    {
        ClipToBounds = true;
    }

    /// <summary>
    /// Specify whether to apply opactiy animation.
    /// </summary>
    public bool OpacityAnimation
    {
        get { return (bool)GetValue(OpacityAnimationProperty); }
        set { SetValue(OpacityAnimationProperty, value); }
    }

    public static readonly DependencyProperty OpacityAnimationProperty =
            DependencyProperty.Register("OpacityAnimation", 
            typeof(bool), 
            typeof(AnimationDecorator),
            new UIPropertyMetadata(true));

    /// <summary>
    /// Gets or sets whether the decorator is expanded or collapsed.
    /// </summary>
    public bool IsExpanded
    {
        get { return (bool)GetValue(IsExpandedProperty); }
        set { SetValue(IsExpandedProperty, value); }
    }

    public static readonly DependencyProperty IsExpandedProperty =
        DependencyProperty.Register("IsExpanded", 
        typeof(bool), 
        typeof(AnimationDecorator),
        new PropertyMetadata(true,IsExpandedChanged));
    
    public static void IsExpandedChanged(DependencyObject d, 
           DependencyPropertyChangedEventArgs e)
    {
        AnimationDecorator expander = d as AnimationDecorator;
        bool expanded = (bool)e.NewValue;
        expander.DoAnimate(expanded);
    }

    /// <summary>
    /// Specify whether to apply animation when IsExpanded is changed.
    /// </summary>

    public DoubleAnimation HeightAnimation
    {
        get { return (DoubleAnimation)GetValue(HeightAnimationProperty); }
        set { SetValue(HeightAnimationProperty, value); }
    }

    public static readonly DependencyProperty HeightAnimationProperty =
        DependencyProperty.Register("HeightAnimation", 
        typeof(DoubleAnimation), 
        typeof(AnimationDecorator), 
        new UIPropertyMetadata(null));

    /// <summary>
    /// Gets or sets the duration for the animation.
    /// </summary>
    public Duration Duration
    {
        get { return (Duration)GetValue(DurationProperty); }
        set { SetValue(DurationProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Duration.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DurationProperty =
        DependencyProperty.Register("Duration", typeof(Duration), 
        typeof(AnimationDecorator), 
        new UIPropertyMetadata(new Duration(new TimeSpan(0,0,0,400))));

    /// <summary>
    /// Perform the animation.
    /// </summary>
    /// <param name="expanded"></param>
    private void DoAnimate(bool expanded)
    {
        if (Child != null)
        {
            if (YOffset > 0) YOffset = 0;

            if (-YOffset > Child.DesiredSize.Height)
                YOffset = -Child.DesiredSize.Height;

            DoubleAnimation animation = HeightAnimation;
            if (animation == null)
            {
                animation = new DoubleAnimation();
                animation.DecelerationRatio = 0.9;
                animation.Duration = Duration;
            }
            animation.From = null;
            animation.To = expanded ? 0 : -Child.DesiredSize.Height;
            this.BeginAnimation(AnimationDecorator.YOffsetProperty, animation);

            if (OpacityAnimation)
            {
                animation.From = null;
                animation.To = expanded ? 1 : 0;
                this.BeginAnimation(Control.OpacityProperty, animation);
            }
        }
        else
        {
            YOffset = int.MinValue;
        }
    }

    protected void SetYOffset(bool expanded)
    {
        YOffset = expanded ? 0 : -Child.DesiredSize.Height;
    }

    /// <summary>
    /// A helper value for the current state while in animation.
    /// </summary>
    internal Double YOffset
    {
        get { return (Double)GetValue(YOffsetProperty); }
        set { SetValue(YOffsetProperty, value); }
    }

    public static readonly DependencyProperty YOffsetProperty =
        DependencyProperty.Register("YOffset", 
        typeof(Double), typeof(AnimationDecorator),
        new FrameworkPropertyMetadata(0.0,
        FrameworkPropertyMetadataOptions.AffectsRender | 
        FrameworkPropertyMetadataOptions.AffectsArrange
        | FrameworkPropertyMetadataOptions.AffectsMeasure));

    /// <summary>
    /// Measures the child element of a
    /// <see cref="T:System.Windows.Controls.Decorator"/>
    /// to prepare for arranging it during the <see
    /// cref="M:System.Windows.Controls.Decorator.
    ///       ArrangeOverride(System.Windows.Size)"/> pass.
    /// </summary>
    /// <param name="constraint">An upper limit
    /// <see cref="T:System.Windows.Size"/> that should not be exceeded.</param>
    /// <returns>
    /// The target <see cref="T:System.Windows.Size"/> of the element.
    /// </returns>

    protected override Size MeasureOverride(Size constraint)
    {
        if (Child == null) return new Size(0, 0);
            Child.Measure(new Size(Double.PositiveInfinity, 
                                   Double.PositiveInfinity));
        Size size = new Size();
        size.Width = DesiredSize.Width;
        size.Height = Child.DesiredSize.Height;
        Double h = size.Height + YOffset;
        if (h < 0) h = 0;
        size.Height = h;
        if (Child != null) Child.IsEnabled = h > 0;
        return size;
    }

    /// <summary>
    /// Arranges the content of a <see cref="T:System.Windows.Controls.Decorator"/> element.
    /// </summary>
    /// <param name="arrangeSize">The <see cref="T:System.Windows.Size"/>
    /// this element uses to arrange its child content.</param>
    /// <returns>
    /// The <see cref="T:System.Windows.Size"/> that represents the arranged size
    /// of this <see cref="T:System.Windows.Controls.Decorator"/> element and its child.
    /// </returns>

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        if (Child == null) return arrangeSize;
            Size size = new Size();
        size.Width = arrangeSize.Width;
        size.Height = Child.DesiredSize.Height;
        Point p = new Point(0, YOffset);
        Child.Arrange(new Rect(p, size));
        Double h = Child.DesiredSize.Height + YOffset;

        if (h < 0) h = 0;
            size.Height = h;
        return size;
    }
}

}

The OdcExpander itself contains various properties to customize the skin of the control. Actually, I intended to keep some properties internal, such as MouseOverHeaderForeground that specifies the foreground color of the header on mouse-over, or PressedHeaderBackground, etc., but this wouldn't allow you to easily apply custom skins since you would have to completely describe the control template instead of just modifying some properties.

/// <summary>
/// An Expander with animation.
/// </summary>

public class OdcExpander : HeaderedContentControl
{

    static OdcExpander()
    {
        MarginProperty.OverrideMetadata(
            typeof(OdcExpander),
            new FrameworkPropertyMetadata(new Thickness(10, 10, 10, 2)));
            FocusableProperty.OverrideMetadata(typeof(OdcExpander),
            new FrameworkPropertyMetadata(false));

        DefaultStyleKeyProperty.OverrideMetadata(typeof(OdcExpander),
            new FrameworkPropertyMetadata(typeof(OdcExpander)));
    }

    /// <summary>
    /// Gets or sets the custom skin for the control.
    /// </summary>
    public static string Skin { get; set; }

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);
        ApplySkin();
    }

    public void ApplySkin()
    {
        if (!string.IsNullOrEmpty(Skin))
        {
            Uri uri = new Uri(Skin, UriKind.Absolute);
            ResourceDictionary skin = new ResourceDictionary();
            skin.Source = uri;
            this.Resources = skin;
        }
    }

    public Brush HeaderBorderBrush
    {
        get { return (Brush)GetValue(HeaderBorderBrushProperty); }
        set { SetValue(HeaderBorderBrushProperty, value); }
    }

    public static readonly DependencyProperty HeaderBorderBrushProperty =
        DependencyProperty.Register("HeaderBorderBrush",
        typeof(Brush), typeof(OdcExpander), 
        new UIPropertyMetadata(Brushes.Gray));

    public Brush HeaderBackground
    {
        get { return (Brush)GetValue(HeaderBackgroundProperty); }
        set { SetValue(HeaderBackgroundProperty, value); }
    }

    public static readonly DependencyProperty HeaderBackgroundProperty =
        DependencyProperty.Register("HeaderBackground",
        typeof(Brush), typeof(OdcExpander), 
        new UIPropertyMetadata(Brushes.Silver));

    public bool IsMinimized
    {
        get { return (bool)GetValue(MinimizedProperty); }
        set { SetValue(MinimizedProperty, value); }
    }

    public static readonly DependencyProperty MinimizedProperty =
        DependencyProperty.Register("IsMinimized",
        typeof(bool), typeof(OdcExpander),
        new UIPropertyMetadata(false, IsMinimizedChanged));

    public static void IsMinimizedChanged(DependencyObject d, 
                       DependencyPropertyChangedEventArgs e)
    {
        OdcExpander expander = d as OdcExpander;
        RoutedEventArgs args = new RoutedEventArgs((bool)e.NewValue ? 
                               MinimizedEvent : MaximizedEvent);
        expander.RaiseEvent(args);
    }

    /// <summary>
    /// Gets or sets the ImageSource for the image in the header.
    /// </summary>
    public ImageSource Image
    {
        get { return (ImageSource)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, value); }
    }

    public static readonly DependencyProperty ImageProperty =
        DependencyProperty.Register("Image", 
        typeof(ImageSource), typeof(OdcExpander), 
        new UIPropertyMetadata(null));

    public bool IsExpanded
    {
        get { return (bool)GetValue(IsExpandedProperty); }
        set { SetValue(IsExpandedProperty, value); }
    }

    public event RoutedEventHandler Expanded
    {
        add { AddHandler(ExpandedEvent, value); }
        remove { RemoveHandler(ExpandedEvent, value); }
    }

    public event RoutedEventHandler Collapsed
    {
        add { AddHandler(CollapsedEvent, value); }
        remove { RemoveHandler(CollapsedEvent, value); }
    }

    public event RoutedEventHandler Minimized
    {
        add { AddHandler(MinimizedEvent, value); }
        remove { RemoveHandler(MinimizedEvent, value); }
    }

    public event RoutedEventHandler Maximized
    {
        add { AddHandler(MaximizedEvent, value); }
        remove { RemoveHandler(MaximizedEvent, value); }
    }

#region dependency properties and routed events definition

    public static readonly DependencyProperty IsExpandedProperty =
        DependencyProperty.Register(
        "IsExpanded",
        typeof(bool),
        typeof(OdcExpander),
        new UIPropertyMetadata(true, IsExpandedChanged));

    public static void IsExpandedChanged(DependencyObject d, 
                    DependencyPropertyChangedEventArgs e)

    {
        OdcExpander expander = d as OdcExpander;
        RoutedEventArgs args = new RoutedEventArgs((bool)e.NewValue ? 
                               ExpandedEvent : CollapsedEvent);
        expander.RaiseEvent(args);
    }

    public static readonly RoutedEvent ExpandedEvent = 
           EventManager.RegisterRoutedEvent(
            "ExpandedEvent",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(OdcExpander));

    public static readonly RoutedEvent CollapsedEvent = 
           EventManager.RegisterRoutedEvent(
            "CollapsedEvent",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(OdcExpander));

    public static readonly RoutedEvent MinimizedEvent = 
           EventManager.RegisterRoutedEvent(
            "MinimizedEvent",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(OdcExpander));

    public static readonly RoutedEvent MaximizedEvent = 
           EventManager.RegisterRoutedEvent(
            "MaximizedEvent",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(OdcExpander));

#endregion

/// <summary>
/// Gets or sets the corner radius for the header.
/// </summary>

public CornerRadius CornerRadius
{
    get { return (CornerRadius)GetValue(CornerRadiusProperty); }
    set { SetValue(CornerRadiusProperty, value); }
}

public static readonly DependencyProperty CornerRadiusProperty =
    DependencyProperty.Register("CornerRadius", typeof(CornerRadius), 
    typeof(OdcExpander), new UIPropertyMetadata(null));

/// <summary>
/// Gets or sets the background color of the header on mouse over.
/// </summary>

public Brush MouseOverHeaderBackground
{
    get { return (Brush)GetValue(MouseOverHeaderBackgroundProperty); }
    set { SetValue(MouseOverHeaderBackgroundProperty, value); }
}

public static readonly DependencyProperty MouseOverHeaderBackgroundProperty =
    DependencyProperty.Register("MouseOverHeaderBackground", 
    typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(null));

/// <summary>
/// Gets whether the PressedBackground is not null.
/// </summary>

public bool HasPressedBackground
{
    get { return (bool)GetValue(HasPressedBackgroundProperty); }
    set { SetValue(HasPressedBackgroundProperty, value); }
}

public static readonly DependencyProperty HasPressedBackgroundProperty =
    DependencyProperty.Register("HasPressedBackground", 
    typeof(bool), typeof(OdcExpander), new UIPropertyMetadata(false));

/// <summary>
/// Gets or sets the background color of the header in pressed mode.
/// </summary>

public Brush PressedHeaderBackground
{
    get { return (Brush)GetValue(PressedHeaderBackgroundProperty); }
    set { SetValue(PressedHeaderBackgroundProperty, value); }
}

public static readonly DependencyProperty PressedHeaderBackgroundProperty =
    DependencyProperty.Register("PressedHeaderBackground", 
    typeof(Brush), typeof(OdcExpander), 
    new UIPropertyMetadata(null, PressedHeaderBackgroundPropertyChangedCallback));

public static void PressedHeaderBackgroundPropertyChangedCallback(DependencyObject d, 
                   DependencyPropertyChangedEventArgs e)
{
    OdcExpander expander = (OdcExpander)d;
    expander.HasPressedBackground = e.NewValue != null;
}

public Thickness HeaderBorderThickness
{
    get { return (Thickness)GetValue(HeaderBorderThicknessProperty); }
    set { SetValue(HeaderBorderThicknessProperty, value); }
}

// Using a DependencyProperty as the backing store
// for HeaderBorderThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderBorderThicknessProperty =
  DependencyProperty.Register("HeaderBorderThickness", 
  typeof(Thickness), typeof(OdcExpander), new UIPropertyMetadata(null));


/// <summary>
/// Gets or sets the foreground color of the header on mouse over.
/// </summary>
public Brush MouseOverHeaderForeground
{
    get { return (Brush)GetValue(MouseOverHeaderForegroundProperty); }
    set { SetValue(MouseOverHeaderForegroundProperty, value); }
}

public static readonly DependencyProperty MouseOverHeaderForegroundProperty =
    DependencyProperty.Register("MouseOverHeaderForeground", 
    typeof(Brush), typeof(OdcExpander), new UIPropertyMetadata(null));

/// <summary>
/// Specifies whether to show a elipse with the expanded/collapsed image.
/// </summary>

public bool ShowEllipse
{
    get { return (bool)GetValue(ShowEllipseProperty); }
    set { SetValue(ShowEllipseProperty, value); }
}

public static readonly DependencyProperty ShowEllipseProperty =
    DependencyProperty.Register("ShowEllipse", typeof(bool), 
    typeof(OdcExpander), new UIPropertyMetadata(false));

}

To simplify the design of the header of the OdcExpander, I wrote a helper control named OdcExpanderHeader, and added some properties for skinning:

/// <summary>
/// A helper class to specify the header of an OdcExpander.
/// </summary>

internal class OdcExpanderHeader : ToggleButton
{
    static OdcExpanderHeader()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(OdcExpanderHeader), 
          new FrameworkPropertyMetadata(typeof(OdcExpanderHeader)));
    }

    /// <summary>
    /// Gets whether the expand geometry is not null.
    /// </summary>
    public bool HasExpandGeometry
    {
        get { return (bool)GetValue(HasExpandGeometryProperty); }
        set { SetValue(HasExpandGeometryProperty, value); }
    }

    public static readonly DependencyProperty HasExpandGeometryProperty =
        DependencyProperty.Register("HasExpandGeometry", typeof(bool), 
        typeof(OdcExpanderHeader), new UIPropertyMetadata(false));


    /// <summary>
    /// Gets or sets the geometry for the collapse symbol.
    /// </summary>
    public Geometry CollapseGeometry
    {
        get { return (Geometry)GetValue(CollapseGeometryProperty); }
        set { SetValue(CollapseGeometryProperty, value); }
    }

    public static readonly DependencyProperty CollapseGeometryProperty =
        DependencyProperty.Register("CollapseGeometry", 
        typeof(Geometry), typeof(OdcExpanderHeader), 
        new UIPropertyMetadata(null));

    public static void CollapseGeometryChangedCallback(DependencyObject d, 
                  DependencyPropertyChangedEventArgs e)
    {
        OdcExpanderHeader eh = d as OdcExpanderHeader;
        eh.HasExpandGeometry = e.NewValue != null;
    }

    /// <summary>
    /// Gets or sets the geometry for the expand symbol.
    /// </summary>
    public Geometry ExpandGeometry
    {
        get { return (Geometry)GetValue(ExpandGeometryProperty); }
        set { SetValue(ExpandGeometryProperty, value); }
    }

    public static readonly DependencyProperty ExpandGeometryProperty =
        DependencyProperty.Register("ExpandGeometry", typeof(Geometry), 
        typeof(OdcExpanderHeader), new UIPropertyMetadata(null, 
                                   CollapseGeometryChangedCallback));

    /// <summary>
    /// Gets or sets the corner radius for the header.
    /// </summary>
    public CornerRadius CornerRadius
    {
        get { return (CornerRadius)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CornerRadius.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.Register("CornerRadius", typeof(CornerRadius), 
        typeof(OdcExpanderHeader), new UIPropertyMetadata(null));

    /// <summary>
    /// Gets or sets whether to display the ellipse
    /// around the collapse/expand symbol.
    /// </summary>
    public bool ShowEllipse
    {
        get { return (bool)GetValue(ShowEllipseProperty); }
        set { SetValue(ShowEllipseProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ShowEllipse.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ShowEllipseProperty =
        DependencyProperty.Register("ShowEllipse", typeof(bool), 
        typeof(OdcExpanderHeader), new UIPropertyMetadata(true));


    /// <summary>
    /// Gets or sets the Image to display on the header.
    /// </summary>
    public ImageSource Image
    {
        get { return (ImageSource)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Image.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ImageProperty =
        DependencyProperty.Register("Image", typeof(ImageSource), 
        typeof(OdcExpanderHeader), new UIPropertyMetadata(null));

}

Note that I added some properties like HasExpandGeometry. Such properties help to conditionaly define the XAML file, as follows:

<MultiTrigger>
 <MultiTrigger.Conditions>
  <Condition Property="IsChecked" Value="True"/>
  <Condition Property="HasExpandGeometry" Value="True"/>
 </MultiTrigger.Conditions>
 <Setter TargetName="path" Property="Data" 
  Value="{Binding RelativeSource={RelativeSource 
         TemplatedParent},Path=ExpandGeometry}"/>
</MultiTrigger>

In this case, the Multitrigger checks the IsChecked property of the OdcExpanderHelper (which is derived from ToggleButton) together with the HasExpandedGeometry, and only if both values are true, a setter changes the Data of the Path control.

Themes

The OdcExpander supports themes that depend on the current theme of the OS. Therefore, the OdcExpander looks different on Vista and XP.

VistaXP_Metallic.png

The theme is only different when a OdcExpander is inside a ExplorerBar; otherwise, it always uses the same generic style. For instance, the part for the XP metallic snippet looks like:

<Style TargetType="{x:Type local:ExplorerBar}">
 <Setter Property="Background" Value="#FFBDBAD6"/>
 <Setter Property="Focusable" Value="False"/>
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate>
 <Border Background="{TemplateBinding Background}">
 <ScrollViewer VerticalScrollBarVisibility="Auto">
  <ItemsPresenter/>
 </ScrollViewer>
 </Border>
 <ControlTemplate.Resources>
  <Style TargetType="{x:Type local:OdcExpander}">
  <Setter Property="HeaderBorderThickness" Value="0"/>
  <Setter Property="HeaderBackground" 
     Value="{StaticResource HeaderBackgroundBrush}"/>
  <Setter Property="Background" Value="{StaticResource ExpanderBg}"/>
  <Setter Property="MouseOverHeaderBackground" 
     Value="{StaticResource HeaderBackgroundBrush}"/>
  <Setter Property="BorderBrush" Value="White"/>
  <Setter Property="MouseOverHeaderForeground" 
     Value="{StaticResource HighlightHeaderTextBrush}"/>
  <Setter Property="CornerRadius" Value="6,6,0,0"/>
  <Setter Property="ShowEllipse" Value="True"/>
</Style>
 </ControlTemplate.Resources>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
</Style>

About Themes

To allow a control to have Windows based themes, you need to specify a custom theme for each Windows theme. Each theme must reside in the Themes folder of the source control as a ResourceDictionary. I implemented the following themes:

Each ResourceDictionary is optional. For instance, if you don't specify the Luna.Homestead.xaml, the style falls back to Classic.xaml, if available; otherwise, to Generic.xaml.

But, that's still not all. When you create your first themable control, you'll wonder why it is only using the generic.xaml and never the customized dictionaries. To finally enable theming, you need to modify the AssemblyInfo.cs as follows:

[assembly: ThemeInfo(
 ResourceDictionaryLocation.SourceAssembly,
 //where theme specific resource dictionaries are located
 //(used if a resource is not found in the page, 
 // or application resource dictionaries)

 ResourceDictionaryLocation.SourceAssembly
 //where the generic resource dictionary is located
 //(used if a resource is not found in the page, 
 // app, or any theme specific resource dictionaries)

)]

(For further information, please read the documentation for ThemeInfo.)

Drawback

When you create custom controls, Visual Studio automatically creates a default template for the new control in Generic.xaml. Thus, all styles for all controls of a control library would reside in one XAML. This can become very confusing. But fortunately, it is possible to merge various ResourceDictionarys together. So, I created a folder for each control that contains all the possible styles (generic, Classic, Luna, etc.). Each ResourceDictionary for each theme is now merged with the base ResourceDictionary, as follows:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary 
  Source="pack://application:,,,/Odyssey;Component/Themes/Expander/Luna.HomeStead.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

In Luna.Homestead.xaml, the Style for the Expander is added using MergedDictionaries. So, it is easy to add styles for other controls by adding its ResourceDictionary to the MergedDictionaries block.

History

The ExplorerBar and OdcExpander is part of the Odyssey class library that I'm currently developing for free. It also contains the BreadcrumbBar which was already introduced on CodeProject.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGrate
Mr_Coder
18:07 14 Jan '10  
to stady xaml
QuestionUsing only the Animation Control
Ilias Bogordos
0:35 29 Dec '09  
Congrats for the great work!
New to WPF stuff..and very curious for beautiful animations like this one!
I am wondering if i can use only the animation to my expanders and not the skining-styling controls..
Is AnimationDecorator enough as back code to achieve this?
I would be very gratefull if you answer me!
Thank you very much,
Ilias
GeneralBug fix?
Foxman2000
9:27 12 Aug '09  
First, thanks quite a lot for the initial work on this control. I have to admit to taking it and modifying it heavily (mostly in templates) but requiring the addition of fields in places.

To the point though, there is a scenario where two expanders are nested & both respond to the Expanded & Collapsed events. The problem came when the inner expander was causing the outer expander's events to fire. The solution to this problem was to change "RoutingStrategy.Bubble" to "RoutingStrategy.Direct" in OdcExpander.cs

If this is still maintained, you might consider making the change, I don't think it should break anything.
GeneralThank you
zhujinlong19840913
18:45 14 Apr '09  
Thank you
QuestionBinding
Steve Fenton
0:35 13 Feb '09  
This is a great control.

Do you know if there are any examples of using data and binding to dynamically build the expander bars?
AnswerRe: Binding
Steve Fenton
1:23 17 Feb '09  
Binding was much easier than I thought it would be - I'm dynamically creating the explorer bar in code, which supplies lots of control. You could perform simple bindings directly in the XAML, but in my implementation there's a vertical tab control with an explorer bar in each tab based on some data!

This is a great control - thanks.
GeneralNicely done
adityakakrania
7:50 23 Jan '09  
Stylish control and Well written. Thank you!
GeneralSlight problem re: text wrapping
chaiguy1337
20:20 17 Dec '08  
Hi Thomas, again I'm very thrilled with this but so far have just one minor quibble. I can't seem to get TextBlocks placed inside an Expander to text-wrap. Seems for some reason they extend and "stretch" the content presenter (including the border) beyond its natural size, which looks bad and is obviously not what I want.

I'm going to try fiddling around some more, but just wanted to ask if you have any idea why this is happening and what can be done about it. I think I'll call it a night for tonight and pick it up again tomorrow. Smile

chai

Sad but true: 4/3 of Americans have difficulty with simple fractions.

There are 10 types of people in this world: those who understand binary and those who don't.

{o,o}.oO( Check out my blog! )
|)””’)          http://pihole.org/ -”-”-

GeneralRe: Slight problem re: text wrapping
chaiguy1337
8:51 18 Dec '08  
Fixed it. It involved adding the line Width="{TemplateBinding ActualWidth}" to the Border inside AnimationDecorator in the Generic.Expander.xaml file. For some reason, adding it to the AnimationDecorator had no effect.

I'm still not sure why this was happening, and why it needed to be patched manually. It must have something to do with either the templating or with AnimationDecorator itself.

Sad but true: 4/3 of Americans have difficulty with simple fractions.

There are 10 types of people in this world: those who understand binary and those who don't.

{o,o}.oO( Check out my blog! )
|)””’)          http://pihole.org/ -”-”-

GeneralRe: Slight problem re: text wrapping
Thomas Gerber
13:06 14 Jan '09  
Thank you for your help, I added this fix in version 1.3.17 available on codeplex http://www.codeplex.com/odyssey[^]
GeneralRe: Slight problem re: text wrapping
DotNetProgramma
4:23 11 Feb '10  
Ive found a problem with this control that I have not been able to fix, related to wrapping.

This fix only work when not resizing the containing window
When resizing a window which contains this control, any contents are not wrapped correctly. Cry

I would be interested if you have a fix for this behaviour.

Regards
David
GeneralGreat work!
chaiguy1337
7:06 17 Dec '08  
And just what I needed. Love the animation.

Thanks!

Sad but true: 4/3 of Americans have difficulty with simple fractions.

There are 10 types of people in this world: those who understand binary and those who don't.

{o,o}.oO( Check out my blog! )
|)””’)          http://pihole.org/ -”-”-

GeneralExcellent work!
John Schroedl
10:03 8 Oct '08  
This is a fantastic control and a very nice bit of code to read. I learned a bit about Measure and Arrange debugging through this. Thanks for sharing!

John


Last Updated 5 Oct 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010