Reputation: 85
I want to animate a color change of a button (based on its IsEnabled
property) in two ways:
I am trying to use EventTrigger
with RoutedEvent
, but there is only one event named Button.IsEnabledChanged
.
I have a button that its IsEnabled
property is changed based on a logic state.
The state machine is working, but I want to change the color (using ColorAnimation
) of the button when it becomes enabled or disabled.
What is the best way to do so?
Upvotes: 1
Views: 7101
Reputation: 306
I wish you could have used an EventTrigger, but they only work for routed events. The event you would have used, "IsEnabledChanged", is not a routed event, so you can't use an EventTrigger. However, there is another method, where you use the VisualStateManager and provide your own animation for the Disabled and Normal states.
Instead of using the VisualStateManager, you could have performed your animation in your code-behind, and possibly you could have declared your own custom RoutedEvent. My solution, using the VisualStateManager, has the advantage that it is performed entirely in XAML.
To use VisualStateManager, you will create a style with a ControlTemplate, my research has not uncovered any other way. That requires you to build most of your button's display features. Here is a screen shot of the enabled and disabled states of my sample:
To mimic the normal button appearance, I used two rectangles, one on above the other, for the button's appearance. Look on the left above: where you see the dark gray top of the button, that comes from one of my rectangles, and the black bottom part comes from my other rectangle. The disabled appearance on the right uses the same two rectangles, but the animation changes the colors to White and WhiteSmoke. I supply a ColorAnimation for each rectangle.
Here is the style that uses the VisualStateManager:
<Window.Resources>
<Style TargetType="Button" x:Key="AnimatedStyle" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<!-- Define our button's border. Note that
the text color is inherited by child
elements, that is we give it a name, so
we can target it with the animation -->
<Border BorderBrush="Black" BorderThickness="1"
CornerRadius="2"
TextBlock.Foreground="WhiteSmoke"
Name="theBorder">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- To match the appearance of a typical button,
we use two rectangles -->
<Rectangle Name="topBackground" Fill="DarkGray"/>
<Rectangle Grid.Row="1" Name="bottomBackground"
Fill="Black" />
<!-- The content presenter shows the
button's text -->
<ContentPresenter Grid.RowSpan="2"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<!-- Here is where we define
the disable animation -->
<VisualState Name="Disabled">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="topBackground"
Storyboard.TargetProperty="(Rectangle.Fill).(Color)"
To="White" Duration="0:0:.5" />
<ColorAnimation
Storyboard.TargetName="bottomBackground"
Storyboard.TargetProperty="(Rectangle.Fill).(Color)"
To="WhiteSmoke" Duration="0:0:0.5" />
<ColorAnimation
Storyboard.TargetName="theBorder"
Storyboard.TargetProperty="(TextBlock.Foreground).(Color)"
To="Gray" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
<!-- Here is where the enabled animation
is defined -->
<VisualState Name="Normal">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="topBackground"
Storyboard.TargetProperty="(Rectangle.Fill).Color"
To="DarkGray" Duration="0:0:0.5" />
<ColorAnimation
Storyboard.TargetName="bottomBackground"
Storyboard.TargetProperty="(Rectangle.Fill).(Color)"
To="Black" Duration="0:0:0.5" />
<ColorAnimation
Storyboard.TargetName="theBorder"
Storyboard.TargetProperty="(TextBlock.Foreground).Color"
To="WhiteSmoke" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Here is some XAML to utilize the style and demonstrate the animation in action:
<Grid>
<!--The button uses the style from above. Note that we control
whether it is enabled by binding the Enabled property to the checkbox's
property 'IsChecked', that way we don't need
any code-behind -->
<Button Content="Test Button" Name="btnTest"
IsEnabled="{Binding ElementName=chkEnabled,Path=IsChecked}"
Height="30" Margin="5"
Click="btnTest_Click"
Style="{StaticResource AnimatedStyle}" />
<CheckBox Grid.Column="1" Content="Enable Button"
Name="chkEnabled" IsChecked="True" VerticalAlignment="Center" />
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
</Grid>
Finally, just to prove that the button really is disabled/enabled, here is the entire code-behind (other than boiler-plate code), which is fired by clicking the button, but only when it is enabled:
private void btnTest_Click(object sender, RoutedEventArgs e) {
MessageBox.Show("You clicked the button, it must be enabled.");
}
Upvotes: 0
Reputation: 3110
Define a style such as the following:
<Style x:Key="ButtonEnabledStyle" TargetType="Button">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false" >
<Setter Property="Background" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
Then assign it to your button(s) like this:
<Button Content="I'm a button" Style="{StaticResource ButtonEnabledStyle}" />
Upvotes: 0