SepehrM
SepehrM

Reputation: 1097

Optimize control templates for animating bindings

Throughout our WPF application, we have many Styles with ControlTemplates that need to animate Binding values. Since only Freezable values can take part in Storyboards, we have to create multiple objects with different binding values and toggle their visibility in animations. Here's one of our buttons:

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

These buttons are used with DynamicResourceses. Like this:

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

We are having some rendering performance issues in our application. I was wondering if we could optimize these templates in terms of performance?

Upvotes: 0

Views: 146

Answers (1)

Heena
Heena

Reputation: 8654

1) Avoid Use Of Dynamic resource : Please refer this Link

Dynamic resource has more performance overhead than static resources because it look up for resources every time it requested or needed.

2)Trigger vs. Visual States :Please refer this Link

Every visual state change starts up a storyboard that in turn contains further timeline objects. Naturally this consumes more time and memory than simply setting a property in a trigger

3)Shape vs Control Please refer this Link

we can not add child element to shape hence it is lighter than control...hence in example I used Rectangle instead of Border.

4) x:Name vs Name Please refer this Link

Replace x:Name With "Name" In template.

5)Avoid supplying Unnecessary "name" attributes: Please refer this link

In example I removed grid name.

6)Optimize element count please refer this link

The XAML can display thousands of objects, and you can make your template layout and render scenes faster by reducing the number of elements in template.


Final template after considering all these points

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

Some Other usefull links

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

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

Upvotes: 1

Related Questions