VisualStateManager и сгенерированные переходы - PullRequest
9 голосов
/ 28 января 2011

Просто, когда мне кажется, что я понимаю VisualStateManager, что-то доказывает, что я не прав.

Я использую WPF 4 и пытаюсь просто увеличить элемент при наведении мыши и уменьшить его при отпускании мыши. Я решил, что просто определю каждое состояние в VisualStateGroup, а затем укажу VisualTransition с GeneratedDuration:

<Border x:Name="PART_Root" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderTransformOrigin="0.5,0.5">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup Name="CommonStates">
            <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1"/>
            </VisualStateGroup.Transitions>

            <VisualState Name="Normal"/>

            <VisualState Name="MouseOver">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1.5" Duration="0"/>
                    <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1.5" Duration="0"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Border.RenderTransform>
        <ScaleTransform x:Name="scaleTransform" ScaleX="1" ScaleY="1"/>
    </Border.RenderTransform>

    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>

Поскольку у меня есть универсальный VisualTransition с GeneratedDuration, я ожидал, что VSM будет генерировать промежуточные анимации. То есть наведение на элемент управления должно анимировать свойства ScaleTransform от 1 до 1,5 в течение 1 секунды. То же самое с выключенным. Вместо этого задержка составляет 1 секунду, а затем свойства ScaleTransform мгновенно привязываются к 1,5 или обратно к 1.

Если я вручную укажу переходы следующим образом, то получу желаемое поведение:

<VisualStateGroup.Transitions>
    <VisualTransition From="Normal" To="MouseOver">
        <Storyboard>
            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1.5" Duration="{StaticResource MouseEnterDuration}"/>
            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1.5" Duration="{StaticResource MouseEnterDuration}"/>
        </Storyboard>
    </VisualTransition>

    <VisualTransition From="MouseOver" To="Normal">
        <Storyboard>
            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX" To="1" Duration="{StaticResource MouseLeaveDuration}"/>
            <DoubleAnimation Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY" To="1" Duration="{StaticResource MouseLeaveDuration}"/>
        </Storyboard>
    </VisualTransition>
</VisualStateGroup.Transitions>

Но почему я должен это делать? Я думал, что весь смысл сгенерированных переходов в том, что переход будет, вы знаете, сгенерирован. Что я тут недопонимаю?

ОБНОВЛЕНИЕ : Согласно ответу Рика, Blend генерирует что-то, что делает работу. Таким образом, работая в обратном направлении, я определил, что на самом деле я ссылаюсь на ScaleTransform напрямую, а не на UIElement, который его содержит. Я изменил свой XAML на следующий, и он работает как положено:

<Border x:Name="PART_Root" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderTransformOrigin="0.5,0.5">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup Name="CommonStates">
            <VisualStateGroup.Transitions>
                <VisualTransition From="Normal" To="MouseOver" GeneratedDuration="{StaticResource MouseEnterDuration}"/>

                <VisualTransition From="MouseOver" To="Normal" GeneratedDuration="{StaticResource MouseLeaveDuration}"/>
            </VisualStateGroup.Transitions>

            <VisualState Name="Normal"/>

            <VisualState Name="MouseOver">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="PART_Root" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)" To="{StaticResource MouseOverScale}" Duration="0"/>
                    <DoubleAnimation Storyboard.TargetName="PART_Root" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" To="{StaticResource MouseOverScale}" Duration="0"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Border.RenderTransform>
        <ScaleTransform/>
    </Border.RenderTransform>

    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>

Кажется смешным (и очевидная ошибка), но это работает.

Спасибо

1 Ответ

7 голосов
/ 29 января 2011

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

<Grid>
    <Grid.Resources>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="PART_Root" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderTransformOrigin="0.5,0.5">
                            <Border.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform/>
                                    <SkewTransform/>
                                    <RotateTransform/>
                                    <TranslateTransform/>
                                </TransformGroup>
                            </Border.RenderTransform>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup Name="CommonStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0:0:1"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState Name="Normal"/>
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="PART_Root">
                                                <EasingDoubleKeyFrame KeyTime="0" Value="2"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="PART_Root">
                                                <EasingDoubleKeyFrame KeyTime="0" Value="2"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Style="{StaticResource ButtonStyle1}"/>
</Grid>

Хотя Blend использует более общую группу преобразований, основное отличие, которое мы можем видеть, состоит в том, что раскадровка нацелена на элемент и путь свойствачерез этот элемент к коэффициентам масштабирования.

...