WPF and the expander control using the visual state manager

Apr 16, 2009 at 10:04 PM

I created an accordian type control in Silverlight and am trying to port the control over to WPF.  The Expand state never seems to fire.

The nuts and bolts of it are I have an Exander Control with a style that has a Visual State Manager on it.  I'm using Expersion Blend 2 with SP 1, Silverlight 2.0.40115 and the WPF Toolkit from March.

the expander control has a style on it that uses a static resource:

 

<controls:Expander Grid.Row="1" ExpandDirection="Down" Header="Search" Style="{StaticResource ExpanderNoButtonStyle}" Grid.ColumnSpan="2" IsEnabled="True" >
<Button Content
="&lt;Expanded Content&gt;"/>

which ultimately should fire this:

 

<vsm:VisualState x:Name="Expanded">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="Visibility">

 

<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>

 

</ObjectAnimationUsingKeyFrames>

 

<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpanderButton" Storyboard.TargetProperty="Foreground">

 

<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>

 

</ObjectAnimationUsingKeyFrames>

 

</Storyboard>

 

</vsm:VisualState>

 

 

</controls:Expander>

Coordinator
Apr 21, 2009 at 2:28 PM
The WPFToolkit doesn't actual have the ExpanderBehavior there to glue the GoToState calls together.  This is in dev10 but obviously that is going to be awhile from now.  Here is the implementation of the ExpanderBehavior:

    /// <summary>
    ///     Provides VisualStateManager behavior for Expander controls.
    /// </summary>
    public class ExpanderBehavior : ControlBehavior
    {
        /// <summary>
        ///     Attaches to property changes and events.
        /// </summary>
        /// <param name="control">An instance of the control.</param>
        protected override void OnAttach(Control control)
        {
            base.OnAttach(control);

            Expander expander = (Expander)control;
            Type targetType = typeof(Expander);
            EventHandler handler = delegate { UpdateState(expander, true); };

            AddValueChanged(Expander.IsMouseOverProperty, targetType, expander, UpdateStateHandler);
            AddValueChanged(Expander.IsEnabledProperty, targetType, expander, UpdateStateHandler);
            AddValueChanged(Expander.IsExpandedProperty, targetType, expander, UpdateStateHandler);
            AddValueChanged(Expander.ExpandDirectionProperty, targetType, expander, UpdateStateHandler);
        }

        /// <summary>
        /// Detaches property changes and events.
        /// </summary>
        /// <param name="control">The control</param>
        protected override void OnDetach(Control control)
        {
            base.OnDetach(control);

            Expander expander = (Expander)control;
            Type targetType = typeof(Expander);
            EventHandler handler = delegate { UpdateState(expander, true); };

            RemoveValueChanged(Expander.IsMouseOverProperty, targetType, expander, UpdateStateHandler);
            RemoveValueChanged(Expander.IsEnabledProperty, targetType, expander, UpdateStateHandler);
            RemoveValueChanged(Expander.IsExpandedProperty, targetType, expander, UpdateStateHandler);
            RemoveValueChanged(Expander.ExpandDirectionProperty, targetType, expander, UpdateStateHandler);
        }

        /// <summary>
        ///     Called to update the control's visual state.
        /// </summary>
        /// <param name="control">The instance of the control being updated.</param>
        /// <param name="useTransitions">Whether to use transitions or not.</param>
        protected override void UpdateState(Control control, bool useTransitions)
        {
            Expander expander = (Expander)control;

            if (!expander.IsEnabled)
            {
                VisualStateManager.GoToState(expander, "Disabled", useTransitions);
            }
            else if (expander.IsMouseOver)
            {
                VisualStateManager.GoToState(expander, "MouseOver", useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(expander, "Normal", useTransitions);
            }

            if (expander.IsExpanded)
            {
                VisualStateManager.GoToState(expander, "Expanded", useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(expander, "Collapsed", useTransitions);
            }

            switch (expander.ExpandDirection)
            {
                case ExpandDirection.Down:
                    VisualStateManager.GoToState(expander, "ExpandDown", useTransitions);
                    break;

                case ExpandDirection.Up:
                    VisualStateManager.GoToState(expander, "ExpandUp", useTransitions);
                    break;

                case ExpandDirection.Left:
                    VisualStateManager.GoToState(expander, "ExpandLeft", useTransitions);
                    break;

                default:
                    VisualStateManager.GoToState(expander, "ExpandRight", useTransitions);
                    break;
            }

            base.UpdateState(control, useTransitions);
        }
    }

To use it you need to add the attached behavior to the Expander style:

<local:ExpanderBehavior x:Key="ExpanderBehavior" />

<Style TargetType="{x:Type Expander}">
            <Setter Property="toolkit:VisualStateBehavior.VisualStateBehavior" Value="{StaticResource ExpanderBehavior}" />
</Style>