2011-11-22 43 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