WPF中关于对前台Xaml中Triggers的一些重要思考。

    今天在做一个小Demo的时候碰到了一个比较奇怪的问题,就是其中一个Trigger始终无法执行,具体的代码如下

<Trigger Property="Popup.IsOpen" Value="False">  

就是当Popup控件关闭的时候不能触发下面的设置内容

<Setter TargetName="BG" Property="Background" Value="Gray" />  

首先贴一下具体的代码和展现的具体效果。

    <CheckBox x:Name="label" HorizontalAlignment="Center" VerticalAlignment="Center"  FontSize="26" Content="场景列表" HorizontalContentAlignment="Center" 
          VerticalContentAlignment="Center" Opacity="0.6" > <CheckBox.Template> <ControlTemplate TargetType="{x:Type CheckBox}"> <Grid> <Border x:Name="BG" Background="{TemplateBinding Control.Background}"> <ContentPresenter Content="{TemplateBinding ContentControl.Content}"></ContentPresenter> </Border> <Popup x:Name="MyPopup" Placement="Bottom" AllowsTransparency="True" PopupAnimation="Slide" StaysOpen="False"
                     IsOpen="{Binding IsChecked,RelativeSource={RelativeSource TemplatedParent}}" PlacementTarget="{Binding ElementName=BG}"> <StackPanel> <Button Content="按钮1" Width="100" Height="30" FontSize="16" Foreground="Teal"></Button> <Button Content="按钮2" Width="100" Height="30" FontSize="16" Foreground="Teal"></Button> <Button Content="按钮3" Width="100" Height="30" FontSize="16" Foreground="Teal"></Button> <Button Content="按钮4" Width="100" Height="30" FontSize="16" Foreground="Teal"></Button> </StackPanel> </Popup> </Grid> <ControlTemplate.Triggers> <Trigger Property="ToggleButton.IsChecked" Value="True"> <Setter TargetName="BG" Property="Background" Value="Red" /> </Trigger> <Trigger Property="Popup.IsOpen" Value="False"> <Setter TargetName="BG" Property="Background" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </CheckBox.Template> </CheckBox>   

  预期的效果是当我们点击该按钮时,弹出Pop并且设置Border 的背景为红色,但实际情况下是当我们点击之后确实能够弹出Popup但是Border的背景却仍然为灰色。

     按照我们的理解,当我们点击“场景列表”时,首先执行下面的Trigger,然后设置Border的背景为红颜色。

 <Trigger Property="ToggleButton.IsChecked" Value="True">                             
          <Setter TargetName="BG" Property="Background" Value="Red" />                                
 </Trigger> 

  但是结果是最终的背景色也为灰色,那么删除掉第二个Trigger时,又会有怎样的结果

    结果是删掉第二个Trigger之后Popup的背景变成了灰色的,也就是说第二个Trigger又将Border的背景设置成灰色了,我们首先看看第二个Trigger的具体内容:

<Trigger Property="Popup.IsOpen" Value="False"> 
     <Setter TargetName="BG" Property="Background" Value="Gray" />
</Trigger>  

    也就是但是Popup.IsOpen的值为False的时候才会将背景设置成为灰色,但是 当前的Popup的IsOpen属性应用了下面的绑定

IsOpen="{Binding IsChecked,RelativeSource={RelativeSource TemplatedParent}}"   

    IsOpen的值是绑定到CheckBox的IsChecked属性的,但是IsChecked属性的值为true,这个是怎么回事,也就是说这个Popup.IsChecked属性使用的是默认值,并没有绑定到具体的Popup控件上,最后我们查找到在进行定义Trigger属性的时候,有一个属性值就是SourceName这个是和TargetName是一对的,即绑定的源头,所以下面做了如下更改:

 <Trigger Property="Popup.IsOpen" Value="False" SourceName="MyPopup">
      <Setter TargetName="BG" Property="Background" Value="Gray" />
 </Trigger>

   这样再运行代码的时候,可以看到效果是可以出来的,也就是Popup.IsOpen属性确实是关联到MyPopup上面,下面就具体截图为证。

   但是当我们调整两个Trigger的位置的时候,又可以正常执行。 

 <ControlTemplate.Triggers> 
      <Trigger  Property="Popup.IsOpen" Value="False">
            <Setter TargetName="BG" Property="Background"  Value="Gray" />
       </Trigger>                            
       <Trigger Property="ToggleButton.IsChecked" Value="True">                             
            <Setter TargetName="BG" Property="Background"   Value="Red" />                                
      </Trigger> 
 </ControlTemplate.Triggers>                        

  后来仔细想一下也是对的啊,首先WPF这种查找属性的机制都是基于可视化树向上查找的,当执行这两个Trigger的时候,如果先执行Property="ToggleButton.IsChecked"这个属性的时候,再向下执行第二个Trigger的时候如果没有指定SourceName属性,它会默认沿着视觉树向上查找,由于在CheckBox之上我们是找不到Popup控件的,所以它会使用默认值,所以才有了最开始的问题,所以在我们写Trigger的时候,第一个Trigger里面一定要写视觉树中比较底层的元素,然后一级一级往上写,这样我们就不会出现上面出现的各种莫名其妙的问题。第二种原则就是按照规范的写法为每一个Trigger都写上SourceName属性,这样无论先后顺序都可以执行下去的。这个是在定义Trigger的时候的两条基本原则,在使用时需要特别注意。

 

posted @ 2016-04-15 13:55  Hello——寻梦者!  阅读(1813)  评论(1编辑  收藏  举报