Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lag when changing color of button border on WP8

I'm moving a object over a bunch of Buttons, and when this object is over a button I change the color of the border. This is no problem, I can do this through bindings, storyboards or style setters/Visual state. When I run this on my emulator it works nice and smooth, but when I run it on my windows phone, there is a small lag when the Border of the country changes. Is there a way to avoid this?

1. CODE Example

<Button x:Name="Button1" BorderThickness="0" BorderBrush="Transparent">
    <Button.Template>
        <ControlTemplate x:Name="Control">
            <Path x:Name="CountryUser" Style="{StaticResource style_ColorButton}" Data="{Binding UserView.buttonData}" Fill="{StaticResource ButtonBackground}">
                <Path.Resources>
                    <Storyboard x:Name="StoryBoard1">
                        <ColorAnimation Storyboard.TargetName="User" Storyboard.TargetProperty="(Stroke).(SolidColorBrush.Color)" To="Blue" Duration="0"/>
                    </Storyboard>
                </Path.Resources>
            </Path>
        </ControlTemplate> 
    </Button.Template>

and activation

foreach (UIElement x in ElementsAtPoint)
{
    f = x as FrameworkElement;
    if (f is Path)
    {
        try
        {
            h = f as Path;
            Storyboard sb = h.Resources["StoryBoard1"] as Storyboard;
            sb.Begin();
        }
        catch
        {
        }

        break;
    }
}

Extra

One difference which comes to mind between emulator and an actual device is the accuracy of the touch point. In the emulator you use a mouse which has much higher resolution. On a device where you're using your finger the accuracy is much lower, i.e. millimeters vs pixels.

I just tested this with a simple counter and a break point in the manipulation_completed_event. But the count is the same in both cases. And it only tried to run the storyboard once.

Extra2

To clarify the lag I see:

I am dragging an element that follows the finger smoothly, when I cross into a new buttton, the element I am dragging stops a short while. The color of the button is changed and the element moves again.

When I move back after the button has changed the color. The element moves smoothly with my finger when I move between the buttons.

Therefore there is a lag when the storyboard is activated and it is seen in that the element I drag cannot follow the finger.

I am therefore trying to find alternativ ways to change the color that might have better performance, on the Phone. Beacuse it works fine on the emulator.

I have tested on two different lumia 920s and one lumia 520

2. Code Example

Using Visual State Manager with help from Shawn Kendrot, from another question: Using Dependency object for color animation WP8 replicated below:

<Style x:Key="ButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
    <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
    <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMedium}"/>
    <Setter Property="Padding" Value="10,5,10,6"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid Background="Transparent">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="Orange"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneButtonBasePressedForegroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
                        <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Notice it has a MouseOver VisualState. Then assign the style and subscribe to the event handlers

<Button Content="Drag over me" Style="{StaticResource ButtonStyle}"  MouseEnter="OnButtonMouseEnter" MouseLeave="OnButtonMouseLeave"/>

And in the event handler change the visual state.

private void OnButtonMouseEnter(object sender, MouseEventArgs e)
{
    VisualStateManager.GoToState((Control)sender, "MouseOver", true);
}

private void OnButtonMouseLeave(object sender, MouseEventArgs e)
{
    VisualStateManager.GoToState((Control)sender, "Normal", true);
}

Still with lag, Anyone else have a solution that could be tested?

Extra

Since the issue might be low fps, i.e. a render problem when I want to change the color, could the dispatcher be used ?

So in the first code example where I start a storyboard, I could instead call a function in the view to utilize the dispatcher? Anyone has an idea for doing this and or if it is a good idea?

appreciating all the help, since I cannot understand why I can move objects smooth around on the screen but when I want to change the color of the border I can see it lagging :/

like image 783
JTIM Avatar asked Apr 11 '14 13:04

JTIM


2 Answers

I've had performance issues on animations in my apps, and often solved them by setting the CacheMode property to BitmapCache on elements that move.

The idea is that it indicates the framework take a screenshot of the control and store the resulting bitmap in the GPU's memory, so that the control doesn't have to be redrawn on every frame.

You can look at this article: CacheMode and why it matters for more insight.

like image 64
Olivier Payen Avatar answered Sep 23 '22 18:09

Olivier Payen


What are the debug/performance info you get when running attached to debugger?

Enable them adding the following at the end of App constructor:

        // Show graphics profiling information while debugging.
        if (Debugger.IsAttached)
        {
            // Display the current frame rate counters.
            Application.Current.Host.Settings.EnableFrameRateCounter = true;

            // Show the areas of the app that are being redrawn in each frame.
            //Application.Current.Host.Settings.EnableRedrawRegions = true;

            // Enable non-production analysis visualization mode,
            // which shows areas of a page that are handed off to GPU with a colored overlay.
            //Application.Current.Host.Settings.EnableCacheVisualization = true;

            // Prevent the screen from turning off while under the debugger by disabling
            // the application's idle detection.
            // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
            // and consume battery power when the user is not using the phone.
            PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
        }
like image 30
roalz Avatar answered Sep 20 '22 18:09

roalz