2011-11-22 62 views
11

我试图为Expander控件创建自己的模板。当控件扩展时,我希望内容慢慢下滑。在WPF中实现“滑下”动画

内容的期望高度在编译时是未知的。

我想我们可以定义滑下作为一个动画:

<Storyboard x:Key="ExpandContent"> 

    <DoubleAnimation 
     Storyboard.TargetName="_expanderContent" 
     Storyboard.TargetProperty="Height" 
     From="0.0" 
     To="{Binding ElementName=_expanderContent,Path=DesiredHeight}" 
     Duration="0:0:1.0" /> 
</Storyboard> 

但遗憾的是没有。我们收到错误

无法冻结此Storyboard时间轴树以供跨线程使用。

看来我们在定义动画参数时不能使用绑定。 (也在this question讨论。)

有没有人有任何想法我可以如何处理这个?我很谨慎使用LayoutTransform.ScaleY,因为那会产生扭曲的图像。

这与this question类似,但这个问题有一个答案,涉及编写代码隐藏,这在控制模板中我认为是不可能的。 我想知道是否可以实现基于XAML的解决方案。


这是值得的,这里是我的控制模板的当前状态。

<ControlTemplate x:Key="ExpanderControlTemplate" TargetType="{x:Type Expander}"> 
    <ControlTemplate.Resources> 
      <!-- Here are the storyboards which don't work --> 
      <Storyboard x:Key="ExpandContent"> 

       <DoubleAnimation 
        Storyboard.TargetName="_expanderContent" 
        Storyboard.TargetProperty="Height" 
        From="0.0" 
        To="{Binding ElementName=_expanderContent,Path=DesiredHeight}" 
        Duration="0:0:1.0" /> 
      </Storyboard> 
      <Storyboard x:Key="ContractContent"> 

       <DoubleAnimation 
        Storyboard.TargetName="_expanderContent" 
        Storyboard.TargetProperty="Height" 
        From="{Binding ElementName=_expanderContent,Path=DesiredHeight}" 
        To="0.0" 
        Duration="0:0:1.0" /> 

      </Storyboard> 
     </ControlTemplate.Resources> 
    <Grid Name="MainGrid" Background="White"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Name="ContentRow" Height="Auto" /> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*" /> 
     </Grid.ColumnDefinitions> 
     <Border> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="*" /> 
        <ColumnDefinition Width="Auto" /> 
       </Grid.ColumnDefinitions> 
       <ContentPresenter ContentSource="Header" /> 
       <ToggleButton Template="{StaticResource ProductButtonExpand}" 
           Grid.Column="1" 
           IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" 
           /> 
       <Rectangle Grid.ColumnSpan="2" Fill="#FFDADADA" Height="1" Margin="8,0,8,2" VerticalAlignment="Bottom"/> 

      </Grid> 
     </Border> 

      <ContentPresenter Grid.Row="1" HorizontalAlignment="Stretch" Name="_expanderContent"> 

      </ContentPresenter> 

    </Grid> 
    <ControlTemplate.Triggers> 
     <Trigger Property="IsExpanded" Value="True"> 
      <Setter TargetName="_expanderContent" Property="Height" Value="{Binding ElementName=_expanderContent,Path=DesiredHeight}" /> 

       <!-- Here is where I would activate the storyboard if they did work --> 
       <Trigger.EnterActions> 
       <!--<BeginStoryboard Storyboard="{StaticResource ExpandContent}"/>--> 
      </Trigger.EnterActions> 
       <Trigger.ExitActions> 
        <!--<BeginStoryboard x:Name="ContractContent_BeginStoryboard" Storyboard="{StaticResource ContractContent}"/>--> 
       </Trigger.ExitActions> 
     </Trigger> 
      <Trigger Property="IsExpanded" Value="False"> 

       <Setter TargetName="_expanderContent" Property="Height" Value="0" /> 
      </Trigger> 
     </ControlTemplate.Triggers> 

</ControlTemplate> 
+1

为什么将'To'和'From'设置为DesiredHight?如果你不设置它,它会自动完成。 – icebat

回答

5

如果您可以使用InteractionsFluidLayoutBlend 4 SDK)你是幸运的,它是为那些花哨的东西动画真正有用的。

首先设置内容CP的高度,以0:

<ContentPresenter Grid.Row="1" 
    HorizontalAlignment="Stretch" 
    x:Name="_expanderContent" 
    Height="0"/> 

要动画这一点,Height只需要在表示扩张状态的VisualState是动画到NaN(非离散动画不会让你使用NaN):

xmlns:is="http://schemas.microsoft.com/expression/2010/interactions" 
<Grid x:Name="MainGrid" Background="White"> 
    <VisualStateManager.CustomVisualStateManager> 
     <is:ExtendedVisualStateManager/> 
    </VisualStateManager.CustomVisualStateManager> 
    <VisualStateManager.VisualStateGroups> 
     <VisualStateGroup x:Name="ExpansionStates" is:ExtendedVisualStateManager.UseFluidLayout="True"> 
      <VisualStateGroup.Transitions> 
       <VisualTransition GeneratedDuration="0:0:1"/> 
      </VisualStateGroup.Transitions> 
      <VisualState x:Name="Expanded"> 
       <Storyboard> 
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" 
                Storyboard.TargetName="_expanderContent"> 
         <DiscreteDoubleKeyFrame KeyTime="0" Value="NaN"/> 
        </DoubleAnimationUsingKeyFrames> 
       </Storyboard> 
      </VisualState> 
      <VisualState x:Name="Collapsed"/> 
     </VisualStateGroup> 
    </VisualStateManager.VisualStateGroups> 
    <!-- ... ---> 

这应该是所有必需的,流体布局将从那里为您创建过渡。


如果你有一个代码隐藏解决方案,将被罚款,你甚至可以用代码隐藏在字典中是这样的:

<!-- TestDictionary.xaml --> 
<ResourceDictionary x:Class="Test.TestDictionary" 
        ...> 
//TestDictionary.xaml.cs 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 

namespace Test 
{ 
    partial class TestDictionary : ResourceDictionary 
    { 
     //Handlers and such here 
    } 
} 
+0

该解决方案根本无法工作。 NaN仍然抱怨。 –

+0

@ChrisBordeman:好的,我确信这在写作的时候有效... –

4

这是一种古老的问题,但我今天有这个问题,所以我想发布我的解决方案将是值得的:

我不得不动画网格行的高度属性(向上滑动和向下),但需要动态绑定,以便行再次滑动到与之前相同的位置。

我发现这个答案是非常有益的(后徒劳作战XAML):在 http://go4answers.webhost4life.com/Question/found-solution-work-protected-override-190845.aspx

有时候做事后台代码只是简单:

 Storyboard sb = new Storyboard(); 

     var animation = new GridLengthAnimation 
     { 
       Duration = new Duration(500.Milliseconds()), 
       From = this.myGridRow.Height, 
       To = new GridLength(IsGridRowVisible ? GridRowPreviousHeight : 0, GridUnitType.Pixel) 
     }; 

     // Set the target of the animation 
     Storyboard.SetTarget(animation, this.myGridRow); 
     Storyboard.SetTargetProperty(animation, new PropertyPath("Height")); 

     // Kick the animation off 
     sb.Children.Add(animation); 
     sb.Begin(); 

的GridLengthAnimation类可以发现这里: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/da47a4b8-4d39-4d6e-a570-7dbe51a842e4/

+1

非常感谢!最后我得到了我的参数化动画。事实上,在XAML中似乎是不可能的。 –

+0

很高兴这有帮助。是的,需要一段时间才能知道WPF中的哪些内容在XAML中做的太麻烦,反之亦然;) –

+2

好的工作!比完成同样事情所需的所有XAML技巧要容易得多。对于所有MVVM“没有代码背后”的狂热分子,这都是UI-Only代码 - 克服它。 –

0

有一个准备使用和仅XAML解决方案on CodeProject

的风格:

<local:MultiplyConverter x:Key="MultiplyConverter" /> 
    <Style TargetType="Expander" x:Key="VerticalSlidingEmptyExpander"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type Expander}"> 
        <ScrollViewer x:Name="ExpanderContentScrollView" 
        HorizontalScrollBarVisibility="Hidden" 
        VerticalScrollBarVisibility="Hidden" 
        HorizontalContentAlignment="Stretch" 
        VerticalContentAlignment="Top" 
        > 
         <ScrollViewer.Tag> 
          <system:Double>0.0</system:Double> 
         </ScrollViewer.Tag> 
         <ScrollViewer.Height> 
          <MultiBinding Converter="{StaticResource MultiplyConverter}"> 
           <Binding Path="ActualHeight" ElementName="ExpanderContent"/> 
           <Binding Path="Tag" RelativeSource="{RelativeSource Self}" /> 
          </MultiBinding> 
         </ScrollViewer.Height> 
         <ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/> 
        </ScrollViewer> 
        <ControlTemplate.Triggers> 
         <Trigger Property="IsExpanded" Value="True"> 
          <Trigger.EnterActions> 
           <BeginStoryboard> 
            <Storyboard> 
             <DoubleAnimation 
         Storyboard.TargetName="ExpanderContentScrollView" 
         Storyboard.TargetProperty="Tag" 
         To="1" 
         Duration="0:0:0.2"/> 
            </Storyboard> 
           </BeginStoryboard> 
          </Trigger.EnterActions> 
          <Trigger.ExitActions> 
           <BeginStoryboard> 
            <Storyboard> 
             <DoubleAnimation 
         Storyboard.TargetName="ExpanderContentScrollView" 
         Storyboard.TargetProperty="Tag" 
         To="0" 
         Duration="0:0:0.2"/> 
            </Storyboard> 
           </BeginStoryboard> 
          </Trigger.ExitActions> 
         </Trigger> 
        </ControlTemplate.Triggers> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
    <Style TargetType="Expander" x:Key="HorizontalSlidingEmptyExpander"> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type Expander}"> 
        <ScrollViewer x:Name="ExpanderContentScrollView" 
        HorizontalScrollBarVisibility="Hidden" 
        VerticalScrollBarVisibility="Hidden" 
        HorizontalContentAlignment="Left" 
        VerticalContentAlignment="Stretch" 
        > 
         <ScrollViewer.Tag> 
          <system:Double>0.0</system:Double> 
         </ScrollViewer.Tag> 
         <ScrollViewer.Width> 
          <MultiBinding Converter="{StaticResource MultiplyConverter}"> 
           <Binding Path="ActualWidth" ElementName="ExpanderContent"/> 
           <Binding Path="Tag" RelativeSource="{RelativeSource Self}" /> 
          </MultiBinding> 
         </ScrollViewer.Width> 
         <ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/> 
        </ScrollViewer> 
        <ControlTemplate.Triggers> 
         <Trigger Property="IsExpanded" Value="True"> 
          <Trigger.EnterActions> 
           <BeginStoryboard> 
            <Storyboard> 
             <DoubleAnimation 
         Storyboard.TargetName="ExpanderContentScrollView" 
         Storyboard.TargetProperty="Tag" 
         To="1" 
         Duration="0:0:0.2"/> 
            </Storyboard> 
           </BeginStoryboard> 
          </Trigger.EnterActions> 
          <Trigger.ExitActions> 
           <BeginStoryboard> 
            <Storyboard> 
             <DoubleAnimation 
         Storyboard.TargetName="ExpanderContentScrollView" 
         Storyboard.TargetProperty="Tag" 
         To="0" 
         Duration="0:0:0.2"/> 
            </Storyboard> 
           </BeginStoryboard> 
          </Trigger.ExitActions> 
         </Trigger> 
        </ControlTemplate.Triggers> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

MultiplyConverter:

public class MultiplyConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, 
      object parameter, CultureInfo culture) 
    { 
     double result = 1.0; 
     for (int i = 0; i < values.Length; i++) 
     { 
      if (values[i] is double) 
       result *= (double)values[i]; 
     } 

     return result; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, 
      object parameter, CultureInfo culture) 
    { 
     throw new Exception("Not implemented"); 
    } 
} 

我复制样式有水平和垂直版本,省略了的ToggleButtons,但你可以很容易地从原来的职位。

+0

使用此解决方案问题没有切换按钮,根本没有标题 – igorGIS