3

I am trying to use the VisualStateManager inside my ControlTemplate for a ToggleButton. I want the ToggleButton to look one way when checked and another when unchecked. I also want the ToggleButton to look different when it's disabled. The issue I'm having is the Unchecked VisualState seems to be trumping the Disabled VisualState.

The documentation states that "Each VisualStateGroup contains a collection of VisualState objects that are mutually exclusive." That's nice, but what about mutual exclusivity between groups?

Anyhow, here's my ControlTemplate. How can I get the TextBlock to use different color for each of the three states; Checked, Unchecked and Disabled?

<Style x:Key="GraphToggleButtonStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Cursor" Value="Hand" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border Background="Transparent">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="Pink" Duration="0" />
                                </Storyboard>
                            </VisualState>
                       </VisualStateGroup>
                        <VisualStateGroup x:Name="CheckStates">
                            <VisualState x:Name="Checked">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="#3AA5DB" Duration="0" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unchecked">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="TextBlock" To="Green" Duration="0" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Indeterminate" />
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="Border" CornerRadius="4" Background="{TemplateBinding Background}">
                        <StackPanel>
                            <TextBlock Name="TextBlock" FontFamily="/Resources/#Entypo" Text="" FontSize="87" Foreground="#909090" HorizontalAlignment="Center" Margin="0,-25,0,0" />
                            <TextBlock FontFamily="Proxima Nova Rg" Text="Stimulator" FontSize="18" Foreground="{StaticResource BlackBrush}" HorizontalAlignment="Center" Margin="0,12,0,0" />
                        </StackPanel>
                    </Border>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Matt Becker
  • 2,338
  • 1
  • 28
  • 36

1 Answers1

4

Each VisualStateGroup contains a collection of VisualState objects that are mutually exclusive

This is actually true. However there is still one more requirement, that's each VisualState should not affect the same properties. In this case your Unchecked state and Disabled state affect the same property Foreground of the same element.

So I don't think we can have any elegant solution for this. We just have this work-around (this is in fact used commonly when styling element in WPF). We need some fake element called DisabledTextBlock, this should be placed in the same Grid with the original element TextBlock. Once the Disabled state comes, that fake element should be shown and hide the original one as well as hide all the effect of the Unchecked (or Checked) state and bring the effect of Disabled to the front. Here is the working code:

<ControlTemplate TargetType="{x:Type ToggleButton}">
   <Border Background="Transparent">
     <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CheckStates">
           <!-- unchanged -->
        </VisualStateGroup>
        <VisualStateGroup x:Name="CommonStates">                            
          <VisualState Name="Disabled">
            <Storyboard>
             <DoubleAnimation Storyboard.TargetProperty="Opacity" 
                       Storyboard.TargetName="DisabledTextBlock" To="1" Duration="0" />
            </Storyboard>
          </VisualState>                            
         </VisualStateGroup>                        
      </VisualStateManager.VisualStateGroups>
      <Border x:Name="Border" CornerRadius="4" Background="{TemplateBinding Background}">
         <StackPanel>
           <Grid>
             <TextBlock Name="TextBlock" FontFamily="/Resources/#Entypo" Text=""
                        FontSize="87" Foreground="#909090" HorizontalAlignment="Center"
                        Margin="0,-25,0,0"  Background="Transparent"/>
             <!-- the fake element -->
             <TextBlock Name="DisabledTextBlock" Opacity="0" 
                        FontFamily="{Binding FontFamily, ElementName=TextBlock}" 
                        Text="{Binding Text,ElementName=TextBlock}" 
                        FontSize="{Binding FontSize,ElementName=TextBlock}" 
                        Foreground="Pink" HorizontalAlignment="Center" 
                        Margin="{Binding Margin, ElementName=TextBlock}"  
                        Background="Transparent"
                        FontStyle="{Binding FontStyle,ElementName=TextBlock}" 
                        FontWeight="{Binding FontSize, ElementName=TextBlock}"/>
           </Grid>
           <TextBlock FontFamily="Proxima Nova Rg" Text="Stimulator" FontSize="18" 
                      Foreground="Black" HorizontalAlignment="Center" Margin="0,12,0,0"/>
         </StackPanel>
       </Border>
     </Border>
 </ControlTemplate>

You may have some new requirement, however the idea here is clear. That's the only work-around I think.

King King
  • 61,710
  • 16
  • 105
  • 130
  • 1
    You're the man, that worked. I did have to add a Normal state as well to hide the DisabledTextBlock. Anyhow, I wish I didn't have to add extra elements to get the effect. – Matt Becker Oct 23 '14 at 14:06