DataStateBehavior for Enum instead of bool? String?
Asked Answered
O

4

6

Is there an easy way in WPF to bind VisualStates to enum values? Kinda like DataStateBehavior, but for an Enum?

Overbold answered 14/6, 2010 at 23:58 Comment(0)
S
4

The best way is to just go ahead and implement a Behavior that does just that -

public class EnumStateBehavior : Behavior<FrameworkElement>
{
    public object EnumProperty
    {
        get { return (object)GetValue(EnumPropertyProperty); }
        set { SetValue(EnumPropertyProperty, value); }
    }

    // Using a DependencyProperty as the backing store for EnumProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty EnumPropertyProperty =
        DependencyProperty.Register("EnumProperty", typeof(object), typeof(EnumStateBehavior), new UIPropertyMetadata(null, EnumPropertyChanged));

    static void EnumPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == null) return;

        EnumStateBehavior eb = sender as EnumStateBehavior;

        VisualStateManager.GoToElementState(eb.AssociatedObject, e.NewValue.ToString(), true);
    }

}

The usage is extremely simple - use as follows:

<i:Interaction.Behaviors>
        <local:EnumStateBehavior EnumProperty="{Binding MyEnumProperty}" />
</i:Interaction.Behaviors>
Suspensor answered 26/2, 2011 at 0:22 Comment(3)
I think you can leave out the override... and essentially this wouldn't even need to be a BehaviourPhilipphilipa
@Markus as to the override you're right. as to the behavior - I guess this could be an attached property, but this way it's - (A) usable from Blend, and (B) can be enforced to be put only on FrameworkElement.Suspensor
Either check for null or change it to EnumStateBehavior eb = (EnumStateBehavior)sender; otherwise you could get a non-informative NullReferenceException.Fitzpatrick
G
3

You can do it in pure xaml by using a DataTrigger per possible enum value with each trigger calling GoToStateAction with a different state. See the example below. For more details take a look at Enum driving a Visual State change via the ViewModel.

    <i:Interaction.Triggers>
        <ei:DataTrigger Binding="{Binding ConfirmedAnswerStatus}" Value="Unanswered">
            <ei:GoToStateAction StateName="UnansweredState" UseTransitions="False" />
        </ei:DataTrigger>
        <ei:DataTrigger Binding="{Binding ConfirmedAnswerStatus}" Value="Correct">
            <ei:GoToStateAction StateName="CorrectlyAnsweredState" UseTransitions="True" />
        </ei:DataTrigger>
        <ei:DataTrigger Binding="{Binding ConfirmedAnswerStatus}" Value="Incorrect">
            <ei:GoToStateAction StateName="IncorrectlyAnsweredState" UseTransitions="True" />
        </ei:DataTrigger>
    </i:Interaction.Triggers>
Graphemics answered 9/2, 2012 at 18:46 Comment(1)
This is actually what I ended up doing for most things.Overbold
O
1

There is a DataStateSwitchBehavior in SL that could be ported to WPF: Anyone have a DataStateSwitchBehavior for WPF4?

the syntax is pretty straightforward:

 <is:DataStateSwitchBehavior Binding="{Binding Orientation}">
     <is:DataStateSwitchCase Value="Left" State="LeftState"/>
     <is:DataStateSwitchCase Value="Right" State="RightState"/>
     <is:DataStateSwitchCase Value="Down" State="DownState"/>
     <is:DataStateSwitchCase Value="Up" State="UpState"/>
 <is:DataStateSwitchCase/>
Orbital answered 25/3, 2011 at 16:49 Comment(0)
S
0

I was having issues with the above EnumStateBehavior answer.

The PropertyChanged handler will first trigger when the AssociatedObject is null (since the binding has been set up but the Behavior hasn't been attached yet). Also, even when the behavior is first attached, the target elements of the VisualState animation may not yet exist since the behavior may have been attached before other child visual trees.

The solution was to use the Loaded event on the associated object to ensure the binding's initial state is set.

public class EnumStateBehavior : Behavior<FrameworkElement>
{
    public static readonly DependencyProperty BindingProperty =
        DependencyProperty.Register(nameof(Binding), typeof(object), typeof(EnumStateBehavior), new UIPropertyMetadata(null, BindingPropertyChanged));

    public object Binding
    {
        get { return (object)GetValue(BindingProperty); }
        set { SetValue(BindingProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        this.AssociatedObject.Loaded += AssociatedObject_Loaded;
    }

    protected override void OnDetaching()
    {
        this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
        base.OnDetaching();
    }

    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        if (Binding != null)
            GoToState();
    }

    private void GoToState()
    {
        VisualStateManager.GoToElementState(this.AssociatedObject, Binding.ToString(), true);
    }

    private static void BindingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var eb = (EnumStateBehavior)sender;

        if (e.NewValue == null || eb.AssociatedObject == null || !eb.AssociatedObject.IsLoaded)
            return;

        eb.GoToState();
    }
}
Sweettempered answered 2/12, 2016 at 22:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.