VisualStateManager和VisualGroup的优先级

3

我试图在一个ToggleButton的ControlTemplate中使用VisualStateManager。当ToggleButton被选中和未选中时,我希望它看起来不同。同时,当ToggleButton被禁用时,我也想要它看起来不同。但是,我遇到了Unchecked VisualState似乎比Disabled VisualState更有优先级的问题。

文档说明:“每个VisualStateGroup包含一组相互排斥的VisualState对象。” 这很好,但是组之间的相互排斥呢?

无论如何,这是我的ControlTemplate。 我该如何使TextBlock在三种状态(Checked、Unchecked和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>
1个回答

4
每个 VisualStateGroup 包含一组相互排斥的 VisualState 对象。 实际上这是正确的。不过还有一个要求,那就是每个 VisualState 不应该影响相同的属性。在这种情况下,你的 Unchecked 状态和 Disabled 状态都会影响同一元素的 Foreground 属性。 因此我认为我们无法有任何优雅的解决方案。我们只有这个变通方法(实际上在 WPF 中对元素进行样式设置时经常使用)。我们需要一个名为 DisabledTextBlock 的假元素,它应该放置在与原始元素 TextBlock 相同的网格中。一旦进入 Disabled 状态,该假元素应该显示出来,并隐藏原始元素以及隐藏 Unchecked(或 Checked)状态的所有效果,将 Disabled 的效果带到前面。以下是可行的代码:
<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>

您可能有一些新的要求,但是这里的想法很清楚。这是我想到的唯一解决方法。


1
你真行,这个方法可行。我确实需要添加一个正常状态来隐藏DisabledTextBlock。不过,我希望不必添加额外的元素就能达到效果。 - Matt Becker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接