Подтвердить что ты не робот

Достижение анимации "сползания вниз" в WPF

Я пытаюсь создать свой собственный шаблон для элемента управления Expander. Когда элемент управления расширен, я хочу, чтобы содержимое медленно скользило.

Желаемая высота содержимого неизвестна во время компиляции.

Я думал, что мы можем определить слайд вниз как анимацию:

<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>

Но, к сожалению, нет. Мы получаем ошибку

Невозможно заморозить это дерево хронологии раскадровки для использования в потоках.

Похоже, мы не можем использовать привязку при определении параметров анимации. (Обсуждается также в этом вопросе.)

Есть ли у кого-нибудь идеи о том, как я могу подойти к этому? Я опасаюсь использовать LayoutTransform.ScaleY, потому что это создаст искаженное изображение.

Это похоже на этот вопрос, но в этом вопросе есть ответ, связанный с написанием кода, который, как я думаю, невозможен в шаблоне управления. Мне интересно, возможно ли решение на основе 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>
4b9b3361

Ответ 1

Если вы можете использовать Interactions с FluidLayout (Blend 4 SDK), вам повезло, это действительно полезно для этих фантазийных анимаций вещи.

Сначала установите содержание CP Height в 0:

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

Чтобы оживить это, нужно просто анимировать Height на NaN в VisualState, который представляет расширенное состояние (недискретная анимация не позволит вам использовать 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
    }
}

Ответ 2

Это своего рода старый вопрос, но у меня были проблемы с этим сегодня, поэтому я думаю, что публикация моего решения будет стоить того:

Мне нужно было оживить свойство Height строки сетки (сдвигаться вверх и вниз), но нужно динамическое связывание, чтобы строка снова скользнула в ту же позицию, что и раньше.

Я нашел этот ответ очень полезным (после бесплодного сражения с 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/

Ответ 3

Существует готовое к использованию и XAML-решение в 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, но вы можете легко получить это из исходного сообщения.