如何在WPF中为模板化父级Panel.ZIndex设置动画?

时间:2011-07-12 17:19:39

标签: wpf animation storyboard z-index

我正在尝试将模板化项目(ZIndex)带入数据绑定堆栈面板。由于stackpanel为其中的每个项目创建了一个ContentPresenter,我的可视化树看起来像这样:

ItemsControl
  Border
    ItemsPresenter
      StackPanel
        ContentPresenter
          ToggleButton
        ContentPresenter
          ToggleButton
        ContentPresenter
          ToggleButton

单击ToggleButton时,它会触发这样的故事板:

<Storyboard x:Key="MyStoryboard" >
    <Int32AnimationUsingKeyFrames BeginTime="0:0:0.000" Duration="0:0:0.350" 
          Storyboard.TargetProperty="TemplatedParent.(Panel.ZIndex)" >
        <Int32KeyFrameCollection>
            <DiscreteInt32KeyFrame KeyTime="0:0:0.000" Value="99" />
        </Int32KeyFrameCollection>
    </Int32AnimationUsingKeyFrames>
</Storyboard>       



<Style x:Key="SubStateOptionButtonStyle" TargetType="ToggleButton">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">

                <!-- Toggle Button Contents here -->

                <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="Button.Click">
                        <EventTrigger.Actions>
                            <BeginStoryboard Storyboard="{StaticResource MyStoryboard}" />
                        </EventTrigger.Actions>
                    </EventTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

然而,TemplatedParent。(Panel.ZIndex)给了我一个奇怪的例外:

值不能为空。 参数名称:dp

在System.Windows.DependencyObject.GetValue(DependencyProperty dp)    在System.Windows.Media.Animation.Storyboard.ProcessComplexPath(HybridDictionary clockMappings,DependencyObject targetObject,PropertyPath path,AnimationClock animationClock,HandoffBehavior handoffBehavior,Int64 layer)    在System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock,DependencyObject containsObject,INameScope nameScope,DependencyObject parentObject,String parentObjectName,PropertyPath parentPropertyPath,HandoffBehavior handoffBehavior,HybridDictionary clockMappings,Int64 layer)    在System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock,DependencyObject containsObject,INameScope nameScope,DependencyObject parentObject,String parentObjectName,PropertyPath parentPropertyPath,HandoffBehavior handoffBehavior,HybridDictionary clockMappings,Int64 layer)    在System.Windows.Media.Animation.Storyboard.BeginCommon(DependencyObject containsObject,INameScope nameScope,HandoffBehavior handoffBehavior,Boolean isControllable,Int64 layer)    在System.Windows.Media.Animation.BeginStoryboard.Begin(DependencyObject targetObject,INameScope nameScope,Int64 layer)

我通过代码隐藏双重检查ToggleButton的TemplatedParent确实是ContentPresenter,我可以用这样的东西设置ZIndex:

Panel.SetZIndex((sender as ToggleButton).TemplatedParent as UIElement, 99);

但是我想用故事板制作动画,最好使用直接XAML,因为它需要在故事板的持续时间内保持前进,然后再回到原来的位置。

我已经考虑从ItemsControl继承以防止创建ContentPresenter,但这对于看起来应该很容易的事情来说是一个严厉的解决方案。

1 个答案:

答案 0 :(得分:0)

以下是这个问题的答案: http://social.msdn.microsoft.com/Forums/en/wpf/thread/7b9b8209-063d-46b2-a03f-98b393cf9514 (作者Min Zhu [MSFT])

您的原始代码不起作用,因为TemplatedParent不是依赖项属性。所以它不是动画的有效路径。

TI82建议的代码是正确的。它仍然不起作用,因为TemplatedParent是Button。但是您要设置动画的元素是项容器。

要为项容器设置动画,可以使用ItemsControl的ItemContainerStyle。 Button.Click是一个冒泡的路由事件,因此它也会从按钮冒泡到项目容器。

请尝试以下代码。

<Window.Resources>
    <Storyboard x:Key="MyStoryboard">
      <ColorAnimation Storyboard.TargetName="ButtonRect" 
                            Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" 
                            To="Red" 
                            Duration="0:0:0.350"
                            FillBehavior="Stop"/>
    </Storyboard>

    <Style x:Key="MyButtonStyle" TargetType="Button">
      <Setter Property="Panel.ZIndex" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}, Mode=OneWayToSource, Path=(Panel.ZIndex)}"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Grid Height="60" Margin="0,0,0,-10" >
              <Border BorderBrush="Black" BorderThickness="2" CornerRadius="15">
                <Rectangle x:Name="ButtonRect" RadiusX="15" RadiusY="15" Fill="AliceBlue" />
              </Border>
              <TextBlock Text="{Binding}" FontSize="15" TextAlignment="Center" VerticalAlignment="Center"/>
            </Grid>
            <ControlTemplate.Triggers>
              <EventTrigger RoutedEvent="Button.Click">
                <EventTrigger.Actions>
                  <BeginStoryboard Storyboard="{StaticResource MyStoryboard}" />
                </EventTrigger.Actions>
              </EventTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

    <DataTemplate x:Key="MyButtonTemplate">
      <Grid>
        <Button Style="{StaticResource MyButtonStyle}" />
      </Grid>
    </DataTemplate>


    <Storyboard x:Key="MyStoryboard2">
      <Int32AnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.ZIndex)" 
       BeginTime="0:0:0.000" Duration="0:0:0.350" FillBehavior="Stop">
        <Int32KeyFrameCollection>
          <DiscreteInt32KeyFrame KeyTime="0:0:0.000" Value="99" />
        </Int32KeyFrameCollection>
      </Int32AnimationUsingKeyFrames>
    </Storyboard>

    <Style x:Key="MyItemContainerStyle">
      <Style.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
          <BeginStoryboard Storyboard="{StaticResource MyStoryboard2}"/>
        </EventTrigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>

  <Grid>
    <ItemsControl ItemsSource="ABCDEF" 
           ItemTemplate="{StaticResource MyButtonTemplate}" 
           ItemContainerStyle="{StaticResource MyItemContainerStyle}">
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <StackPanel Orientation="Vertical" />
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
    </ItemsControl>
  </Grid>