【问题标题】:Optimize control templates for animating bindings优化动画绑定的控制模板
【发布时间】:2014-12-26 19:12:49
【问题描述】:

在整个 WPF 应用程序中,我们有许多 Styles 和 ControlTemplates 需要为 Binding 值设置动画。由于只有Freezable 值可以参与Storyboards,我们必须创建具有不同绑定值的多个对象并在动画中切换它们的可见性。这是我们的按钮之一:

<ControlTemplate TargetType="{x:Type Button}">
                    <Grid x:Name="Grid">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.2"/>
                                    <VisualTransition GeneratedDuration="0" To="MouseOver"/>
                                    <VisualTransition GeneratedDuration="0" To="Pressed"/>
                                </VisualStateGroup.Transitions>
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="BorderOver">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconUp">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconOver">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="BorderOver">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="BorderDown">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconUp">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconOver">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconDown">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="contentPresenter">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0.5"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconDisabled">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconUp">
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="BorderOver" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Opacity="0"/>
                        <Border x:Name="BorderDown" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}" Opacity="0"/>
                        <Path x:Name="IconUp" Data="M14.077,9.772C11.634,9.772 9.652,11.753 9.652,14.197 9.652,16.641 11.634,18.622 14.077,18.622 16.521,18.622 18.502,16.641 18.502,14.197 18.502,11.753 16.521,9.772 14.077,9.772 M28,12L28,16 24.085,16C23.84,17.369,23.325,18.643,22.592,19.763L25.313,22.485 22.485,25.314 19.791,22.62C18.668,23.383,17.383,23.924,16,24.189L16,28 12,28 12,24.163C10.638,23.88,9.378,23.322,8.274,22.554L5.514,25.314 2.686,22.485 5.504,19.668C4.802,18.57,4.306,17.331,4.068,16L0,16 0,12 4.144,12C4.427,10.722,4.943,9.533,5.656,8.485L2.686,5.515 5.514,2.686 8.513,5.684C9.558,5,10.734,4.499,12,4.236L12,0 16,0 16,4.21C17.285,4.456,18.48,4.946,19.545,5.626L22.485,2.686 25.313,5.515 22.431,8.397C23.176,9.467,23.718,10.685,24.008,12z" Fill="{TemplateBinding Foreground}" Height="12" Width="12" Stretch="Fill" Margin="0,0,4.5,0" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                        <Path x:Name="IconOver" Data="M14.077,9.772C11.634,9.772 9.652,11.753 9.652,14.197 9.652,16.641 11.634,18.622 14.077,18.622 16.521,18.622 18.502,16.641 18.502,14.197 18.502,11.753 16.521,9.772 14.077,9.772 M28,12L28,16 24.085,16C23.84,17.369,23.325,18.643,22.592,19.763L25.313,22.485 22.485,25.314 19.791,22.62C18.668,23.383,17.383,23.924,16,24.189L16,28 12,28 12,24.163C10.638,23.88,9.378,23.322,8.274,22.554L5.514,25.314 2.686,22.485 5.504,19.668C4.802,18.57,4.306,17.331,4.068,16L0,16 0,12 4.144,12C4.427,10.722,4.943,9.533,5.656,8.485L2.686,5.515 5.514,2.686 8.513,5.684C9.558,5,10.734,4.499,12,4.236L12,0 16,0 16,4.21C17.285,4.456,18.48,4.946,19.545,5.626L22.485,2.686 25.313,5.515 22.431,8.397C23.176,9.467,23.718,10.685,24.008,12z" Fill="{TemplateBinding BorderBrush}" Height="12" Width="12" Stretch="Fill" Margin="0,0,4.5,0" VerticalAlignment="Center" HorizontalAlignment="Right" Opacity="0"/>
                        <Path x:Name="IconDown" Data="M14.077,9.772C11.634,9.772 9.652,11.753 9.652,14.197 9.652,16.641 11.634,18.622 14.077,18.622 16.521,18.622 18.502,16.641 18.502,14.197 18.502,11.753 16.521,9.772 14.077,9.772 M28,12L28,16 24.085,16C23.84,17.369,23.325,18.643,22.592,19.763L25.313,22.485 22.485,25.314 19.791,22.62C18.668,23.383,17.383,23.924,16,24.189L16,28 12,28 12,24.163C10.638,23.88,9.378,23.322,8.274,22.554L5.514,25.314 2.686,22.485 5.504,19.668C4.802,18.57,4.306,17.331,4.068,16L0,16 0,12 4.144,12C4.427,10.722,4.943,9.533,5.656,8.485L2.686,5.515 5.514,2.686 8.513,5.684C9.558,5,10.734,4.499,12,4.236L12,0 16,0 16,4.21C17.285,4.456,18.48,4.946,19.545,5.626L22.485,2.686 25.313,5.515 22.431,8.397C23.176,9.467,23.718,10.685,24.008,12z" Fill="{TemplateBinding Background}" Height="12" Width="12" Stretch="Fill" Margin="0,0,4.5,0" VerticalAlignment="Center" HorizontalAlignment="Right" Opacity="0"/>
                        <Path x:Name="IconDisabled" Data="M14.077,9.772C11.634,9.772 9.652,11.753 9.652,14.197 9.652,16.641 11.634,18.622 14.077,18.622 16.521,18.622 18.502,16.641 18.502,14.197 18.502,11.753 16.521,9.772 14.077,9.772 M28,12L28,16 24.085,16C23.84,17.369,23.325,18.643,22.592,19.763L25.313,22.485 22.485,25.314 19.791,22.62C18.668,23.383,17.383,23.924,16,24.189L16,28 12,28 12,24.163C10.638,23.88,9.378,23.322,8.274,22.554L5.514,25.314 2.686,22.485 5.504,19.668C4.802,18.57,4.306,17.331,4.068,16L0,16 0,12 4.144,12C4.427,10.722,4.943,9.533,5.656,8.485L2.686,5.515 5.514,2.686 8.513,5.684C9.558,5,10.734,4.499,12,4.236L12,0 16,0 16,4.21C17.285,4.456,18.48,4.946,19.545,5.626L22.485,2.686 25.313,5.515 22.431,8.397C23.176,9.467,23.718,10.685,24.008,12z" Fill="#FF999999" Height="12" Width="12" Stretch="Fill" Margin="0,0,4.5,0" VerticalAlignment="Center" HorizontalAlignment="Right" Opacity="0"/>

                        <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Right" Margin="4.5,1,21,1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" Width="Auto"/>
                    </Grid>
                </ControlTemplate>  

这些按钮与DynamicResourceses 一起使用。像这样:

<Button Content="..." Style="{DynamicResource hkbtnSettings}" Background="{DynamicResource InterfaceHeaderColor}" Foreground="{DynamicResource InterfaceBodyColor}" BorderBrush="{DynamicResource InterfaceAccentColor}"/>  

我们的应用程序存在一些渲染性能问题。我想知道我们是否可以在性能方面优化这些模板?

【问题讨论】:

    标签: wpf xaml themes controltemplate


    【解决方案1】:

    1) 避免使用动态资源:请参考Link

    动态资源比静态资源具有更多的性能开销,因为它会在每次请求或需要时查找资源。

    2)触发器与视觉状态:请参考Link

    每个视觉状态变化都会启动一个故事板,该故事板又包含更多时间线对象。当然,这比简单地在触发器中设置属性会消耗更多的时间和内存

    3)形状与控制请参考Link

    我们不能将子元素添加到形状,因此它比控件轻...因此在示例中我使用矩形而不是边框​​。

    4) x:Name vs Name请参考Link

    将 x:Name 替换为模板中的“名称”。

    5)避免提供不必要的“名称”属性:请参考link

    在示例中,我删除了网格名称。

    6)优化元素数量请参考link

    XAML 可以显示数千个对象,您可以通过减少模板中的元素数量来加快模板布局和渲染场景。


    考虑所有这些点后的最终模板

    <ControlTemplate TargetType="{x:Type Button}">
            <Grid>
                <Rectangle Name="Rectangle" StrokeThickness="{TemplateBinding BorderThickness}" Margin="{TemplateBinding Padding}" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" Opacity="0"/>
                <Path Name="Icon" Data="M14.077,9.772C11.634,9.772 9.652,11.753 9.652,14.197 9.652,16.641 11.634,18.622 14.077,18.622 16.521,18.622 18.502,16.641 18.502,14.197 18.502,11.753 16.521,9.772 14.077,9.772 M28,12L28,16 24.085,16C23.84,17.369,23.325,18.643,22.592,19.763L25.313,22.485 22.485,25.314 19.791,22.62C18.668,23.383,17.383,23.924,16,24.189L16,28 12,28 12,24.163C10.638,23.88,9.378,23.322,8.274,22.554L5.514,25.314 2.686,22.485 5.504,19.668C4.802,18.57,4.306,17.331,4.068,16L0,16 0,12 4.144,12C4.427,10.722,4.943,9.533,5.656,8.485L2.686,5.515 5.514,2.686 8.513,5.684C9.558,5,10.734,4.499,12,4.236L12,0 16,0 16,4.21C17.285,4.456,18.48,4.946,19.545,5.626L22.485,2.686 25.313,5.515 22.431,8.397C23.176,9.467,23.718,10.685,24.008,12z" Fill="{TemplateBinding Foreground}" Height="12" Width="12" Stretch="Fill" Margin="0,0,4.5,0" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                <ContentPresenter Name="contentPresenter" HorizontalAlignment="Right" Margin="4.5,1,21,1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" Width="Auto"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="Rectangle" Property="Opacity" Value="1"></Setter>
                    <Setter TargetName="Icon" Property="Fill" Value="{Binding Path=BorderBrush,RelativeSource={RelativeSource TemplatedParent}}"></Setter>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter TargetName="Rectangle" Property="Opacity" Value="1"></Setter>
                    <Setter TargetName="Rectangle" Property="Fill" Value="{Binding Path=BorderBrush,RelativeSource={RelativeSource TemplatedParent}}"></Setter>
                    <Setter TargetName="Icon" Property="Fill" Value="{Binding Path=Background,RelativeSource={RelativeSource TemplatedParent}}"></Setter>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Opacity" TargetName="contentPresenter" Value="0.5"></Setter>
                    <Setter TargetName="Icon" Property="Fill" Value="#FF999999"></Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    

    其他一些有用的链接

    1)http://www.centigrade.de/en/blog/article/wpf-performance-2/

    2)http://wpftutorial.net/10PerformanceTips.html

    【讨论】:

    • 大量提示。我不知道特别是数字 5。只是:对于丰富的 UI,我们需要平滑的过渡,所以我们不能使用 setter。由于 UI 是可换肤的,因此必须使用 DynamicResources。那么我们如何优化保留过渡动画的模板呢?
    • 我们可以像prntscr.com/5l2i6h这样在触发器中使用动画以及我已经提供的其他我知道的优化方法。
    • 所以为了有动画,我们必须有 4 个路径对象而不是一个? (因为我们不能在情节提要中使用绑定)
    • 是的,要使用动画,我们必须有 4 个路径对象。
    猜你喜欢
    • 2020-06-15
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    相关资源
    最近更新 更多