Animating a GradientBrush in UWP - xaml

I want to animate a GradientBrush (LinearGradientBrush in my case) used by a control's fill property. I tried to change the gradient stops values (offset or color) in my storyboard but it doesn't seem to work. I target a grid's background for the example:
<Grid x:Name="LogoGrid" Height="512" Width="512">
<Grid.Background>
<LinearGradientBrush x:Name="LogoBackgroundBrush" StartPoint="0 0" EndPoint="1 1">
<GradientStop x:Name="Stop0" Color="Transparent" Offset="0" />
<GradientStop x:Name="Stop1" Color="#80FFFFFF" Offset="0.5" />
<GradientStop x:Name="Stop2" Color="Transparent" Offset="1" />
</LinearGradientBrush>
</Grid.Background>
</Grid>
And the storyboard:
<Storyboard x:Key="LoadingStoryBoard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="LogoGrid"
Storyboard.TargetProperty="(UIElement.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)"
RepeatBehavior="Forever" EnableDependentAnimation="True">
<LinearColorKeyFrame Value="#40000000" KeyTime="0:0:1" />
<LinearColorKeyFrame Value="#A0FFFFFF" KeyTime="0:0:2" />
</ColorAnimationUsingKeyFrames>
</Storyboard>

Did you make sure to set EnableDependentAnimation to true?
You can look at my answer to another similar question for a complete example.

You didn't mention how you start the storyboard. Anyway, I made it work by replacing x:Key with x:Name (otherwise I can't reference the storyboard from code).
XAML
<Grid x:Name="LogoGrid">
<Grid.Resources>
<Storyboard x:Name="LoadingStoryBoard">
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="LogoGrid"
Storyboard.TargetProperty="(UIElement.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)"
RepeatBehavior="Forever"
EnableDependentAnimation="True">
<LinearColorKeyFrame Value="#40000000" KeyTime="0:0:1" />
<LinearColorKeyFrame Value="#A0FFFFFF" KeyTime="0:0:2" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Grid.Background>
<LinearGradientBrush x:Name="LogoBackgroundBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop x:Name="Stop0" Color="Transparent" Offset="0" />
<GradientStop x:Name="Stop1" Color="#80FFFFFF" Offset="0.5" />
<GradientStop x:Name="Stop2" Color="Transparent" Offset="1" />
</LinearGradientBrush>
</Grid.Background>
</Grid>
Code-behind
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
Loaded += (sender, args) => LoadingStoryBoard.Begin();
}
}
Here is a complete demo project on GitHub.
Edit
Tangetial: This shows how to access the storyboard through x:Key instead of my x:Name. The trick is to access the storyboard through Resources, e.g.:
((Storyboard)Resources["LoadingStoryboard"]).Begin();

Related

Why is this ColorAnimation Code not working?

I am using this code to animate two gradient stops made with LinearGradientBrush. On execution, the code just stays at the initial gradient stops and there is no animation whatsoever. Neither is there any exception/error.
XAML:
<StackPanel x:Name="myStackPanel" Loaded="myStackPanel_Loaded">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard>
<Storyboard x:Name="colorStoryboard1" Completed="colorStoryboard1_Completed">
<ColorAnimation Storyboard.TargetName="GradStop1"
Storyboard.TargetProperty = "Color"
From="Lavender" To="PaleVioletRed" Duration="0:0:5"/>
<ColorAnimation Storyboard.TargetName="GradStop2"
Storyboard.TargetProperty = "Color"
From="White" To="Lavender" Duration="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<Panel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop x:Name="GradStop1" Color="Lavender" Offset="0"/>
<GradientStop x:Name="GradStop2" Color="White" Offset="1"/>
</LinearGradientBrush>
</Panel.Background>
</StackPanel>
Trigger Code:
public MainPage()
{
this.InitializeComponent();
colorStoryboard1.Begin();
}
The Triggers, EventTrigger, BeginStoryboard are not commonly used in UWP app, these APIs mainly exist for compatibility in XAML originally used for Microsoft Silverlight. In UWP app we should use built-in animations.
But for your scenario, if your animation causes a layout change or otherwise has the potential to impact performance on the UI thread, you often need to explicitly enable the animation to see it run. It means, you need to enable the EnableDependentAnimation property. For more info, you can refer to ColorAnimation.EnableDependentAnimation property.
So you can modify your code for example like this:
<StackPanel x:Name="myStackPanel">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard>
<Storyboard x:Name="colorStoryboard1" Completed="colorStoryboard1_Completed">
<ColorAnimation Storyboard.TargetName="GradStop1"
Storyboard.TargetProperty = "Color"
From="Lavender" To="PaleVioletRed" Duration="0:0:5" EnableDependentAnimation="True" />
<ColorAnimation Storyboard.TargetName="GradStop2"
Storyboard.TargetProperty = "Color"
From="White" To="Lavender" Duration="0:0:5" EnableDependentAnimation="True" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<StackPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop x:Name="GradStop1" Color="Lavender" Offset="0" />
<GradientStop x:Name="GradStop2" Color="White" Offset="1" />
</LinearGradientBrush>
</StackPanel.Background>
</StackPanel>
For this method, since you already defined the trigger in XAML, there is no need to trigger it again in the code behind.
And in case you're interest in the built-in animation in UWP, here is a sample:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<Storyboard x:Name="std">
<ColorAnimation Storyboard.TargetName="GradStop1"
Storyboard.TargetProperty = "Color"
From="Lavender" To="PaleVioletRed" Duration="0:0:5" EnableDependentAnimation="True" />
<ColorAnimation Storyboard.TargetName="GradStop2"
Storyboard.TargetProperty = "Color"
From="White" To="Lavender" Duration="0:0:5" EnableDependentAnimation="True" />
</Storyboard>
</Grid.Resources>
<StackPanel x:Name="myStackPanel" Loaded="myStackPanel_Loaded">
<StackPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop x:Name="GradStop1" Color="Lavender" Offset="0" />
<GradientStop x:Name="GradStop2" Color="White" Offset="1" />
</LinearGradientBrush>
</StackPanel.Background>
</StackPanel>
</Grid>
code behind:
private void myStackPanel_Loaded(object sender, RoutedEventArgs e)
{
std.Begin();
}

How to create a rectangle with a gradient that reflect outwards in WP8.1 (WinRT)?

I am trying to create a rectangle with a gradient that reflects outside. I want something similar to this (but for a rectangle/grid). I was not able to achieve it using a LinearGradientBrush. Below is what I tried. But that is not at all what I want.
<Border Grid.Row="1" Grid.ColumnSpan="2">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Border.Background>
Thank you.
It's doable, but tricky. You'll need to do it in Blend because there's a gradient tool that lets you easily rotate a gradient and adjust the gradient stops. You'll need a parent grid with two child grids of equal size. One child will need the gradient to be horizontal, and the other vertical. You will have to adjust the opacity on both grids so the gradient appears to be one. I threw this XAML together in a few minutes, so it's not perfect, but it might help you out....
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Height="200">
<Grid.Background>
<LinearGradientBrush EndPoint="0.51,1.024"
StartPoint="0.507,0.052">
<GradientStop Color="White"
Offset="1" />
<GradientStop Color="#FFF7F7F7"
Offset="0.098" />
<GradientStop Color="Black"
Offset="0.5" />
<GradientStop Color="#FFC3C3C3"
Offset="0.211" />
<GradientStop Color="White"
Offset="0.829" />
</LinearGradientBrush>
</Grid.Background>
</Grid>
<Grid Height="200"
Margin="0,220">
<Grid.Background>
<LinearGradientBrush EndPoint="1.001,0.588"
StartPoint="-0.001,0.596">
<GradientStop Color="White"
Offset="1" />
<GradientStop Color="#FFF7F7F7" />
<GradientStop Color="#7F000000"
Offset="0.498" />
</LinearGradientBrush>
</Grid.Background>
</Grid>
</Grid>

Attach system colors to LinearGradientBrush

In my application I'd like to create a fading line that has GradientStops with system colors, I'm trying to do it like this:
<UserControl.Resources>
<Style x:Key="Divider" TargetType="Rectangle">
<Setter Property="Height" Value="2" />
<Setter Property="Fill">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="{StaticResource PhoneChromeBrush}" Offset="0.0" />
<GradientStop Color="{StaticResource PhoneInverseBackgroundBrush}" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
But when I try to compile project I get the following error:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.ni.dll
An exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.ni.dll but was not handled in user code
What should I do to fix this?
GradientStop.Color expects a color, not a brush. Use PhoneChromeColor and PhoneInverseBackgroundColor instead:
<UserControl.Resources>
<Style x:Key="Divider" TargetType="Rectangle">
<Setter Property="Height" Value="2" />
<Setter Property="Fill">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="{StaticResource PhoneChromeColor}" Offset="0.0" />
<GradientStop Color="{StaticResource PhoneInverseBackgroundColor}" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
I am not certain but it appears you may be setting a static color as a brush. It is hard to tell since you never posted the code to your static resource for 'PhoneChromeBrush' or 'PhoneInverseBackgroundBrush'. But you are setting gradient stops with these and if they are gradients themselves that may break your code. Usually you reserve 'brush' for a gradient so I was not certain:
could you not do something like:
<UserControl.Resources>
<LinearGradientBrush x:Key="MoneyBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#3A883A" Offset="1" />
<GradientStop Color="#FFFFFF" Offset="0" />
<GradientStop Color="#FF53AA75" Offset="0.50" />
<GradientStop Color="#073307" Offset="0.95" />
</LinearGradientBrush>
<Style x:Key="Divider" TargetType="Rectangle">
<Setter Property="Height" Value="2" />
<Setter Property="Fill" Value="{StaticResource MoneyBrush}"/>
</Style>
</UserControl.Resources>

Styles from a ResourceDictionary is preventing all other functionality

UPDATE: I moved the TextBox's style to the phone:PhoneApplicationPage.Resources tag and it behaves exactly the same way so it turns out that is not the fact the I am using a ResourceDictionary what is causing the problem but that there is something wrong with the way I am defining the style.
I just started playing around with ResourceDictionaries and I really like them but when I tried to use them on my application everything stopped working.
First the following TextBox:
<TextBox Grid.Column="1"
Grid.Row="0"
x:Name="Value"
InputScope="Number"
TextAlignment="Right"
TextChanged="OnValueTextChanged">
<TextBox.Style>
<StaticResource ResourceKey="InputTextBox" />
</TextBox.Style>
</TextBox>
Update: I have updated the ResourceDictionary per XAMeLi's answer and now I see the borders but it would seem as the TextBox does not have any background but, when I click on it nothing happens as if the TextBox is not even there. Then by pure luck I noticed that if I click on the bottom border the numeric keyboard would pop up as if the TextBox is too small or hiding below the border element. I tried modifying the TextBox height to no avail. This is driving me crazy.
Then the ListPickers are even worse:
<toolkit:ListPicker
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
x:Name="CategoriesPicker"
HeaderTemplate="{StaticResource ListPickerHeaderTemplate}"
FullModeItemTemplate="{StaticResource CategoriesPickerTemplate}"
ExpansionMode="FullScreenOnly"
BorderThickness="0"
Padding="0"
Margin="0"
SelectionChanged="OnCategoriesPickerSelectionChanged">
<toolkit:ListPicker.Style>
<StaticResource ResourceKey="ListPickersStyle"/>
</toolkit:ListPicker.Style>
</toolkit:ListPicker>
When the Style is in it won't even bind the data I'm giving to it.
The file with the ResourceDictionary looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit">
<Style x:Name="InputTextBox" TargetType="TextBox">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Margin" Value="-12"/>
<Setter Property="Height" Value="50"/>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
<GradientStop Color="DarkGray" Offset="0"/>
<GradientStop Color="DarkGray" Offset=".3"/>
<GradientStop Color="LightSlateGray" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border
BorderThickness="2"
Margin="15"
CornerRadius="3">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
<GradientStop Offset="0" Color="DarkGray"></GradientStop>
<GradientStop Offset="0.3" Color="DarkGray"></GradientStop>
<GradientStop Offset="1" Color="LightSlateGray"></GradientStop>
</LinearGradientBrush>
</Border.BorderBrush>
<Border
BorderThickness="2"
CornerRadius="3">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="1 1" EndPoint="1 0">
<GradientStop Offset="1" Color="Gray"></GradientStop>
<GradientStop Offset="0.3" Color="DarkGray"></GradientStop>
<GradientStop Offset="0" Color="DarkGray"></GradientStop>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Name="ListPickersStyle" TargetType="toolkit:ListPicker">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ListPicker">
<Border
BorderThickness="2"
Padding="0"
Margin="10"
CornerRadius="3"
Background="DarkGray">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
<GradientStop Offset="0" Color="DarkGray"></GradientStop>
<GradientStop Offset="0.3" Color="DarkGray"></GradientStop>
<GradientStop Offset="1" Color="LightSlateGray"></GradientStop>
</LinearGradientBrush>
</Border.BorderBrush>
<Border BorderThickness="2"
CornerRadius="3">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="1 1" EndPoint="1 0">
<GradientStop Offset="1" Color="Gray"></GradientStop>
<GradientStop Offset="0.3" Color="DarkGray"></GradientStop>
<GradientStop Offset="0" Color="DarkGray"></GradientStop>
</LinearGradientBrush>
</Border.BorderBrush>
<toolkit:ListPicker
BorderThickness="0"
Padding="0"
Margin="0">
<toolkit:ListPicker.Background>
<LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
<GradientStop Offset="0" Color="DarkGray"></GradientStop>
<GradientStop Offset="0.5" Color="DarkGray"></GradientStop>
<GradientStop Offset="1" Color="LightSlateGray"></GradientStop>
</LinearGradientBrush>
</toolkit:ListPicker.Background>
</toolkit:ListPicker>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Please, somebody explain to me what am I doing wrong.
Inside your ControlTemplates there are the controls them selves, i.e. control template for text box is holding a TextBox. This is not how control templates should be used. Use Blend or VS11 to extract the default style for each control (I'd recommend doing it in a new clean solution) and then change the visual appearance.
You should be able to reference the style just like any other property, e.g.:
<TextBox Style="{StaticResource InputTextBox}"/>
And try setting your Style first, then any settings you want to override, e.g.:
<TextBox Style="{StaticResource InputTextBox}" TextAlignment="Right" />
You should use x:Key instead of x:Name in ResourceDictionary:
x:Key and x:Name are not identical concepts. x:Key is used exclusively
in resource dictionaries. x:Name is used for all areas of XAML. A
FindName call using a key value will not retrieve a keyed resource.
However, Silverlight 5 can use an x:Name (or Name) attribute as the
substitute resource key for a resource item if no x:Key exists on the
item.
and as soon as Windows Phone is far from Silverlight 5, you can not use x:Name in the dictionary.

In XAML TabControl, how to set the style of the focused tab header?

I can set the background of each TabItem with TabItem.Background, but when that tab is selected it is plain vanilla white.
How do I set the style of the tab header that is focused?
<TabControl DockPanel.Dock="Top">
<TabControl.Background>
<LinearGradientBrush EndPoint="1.115,1.13" StartPoint="0,-0.02">
<GradientStop Color="#FFFFFFFF" Offset="1"/>
<GradientStop Color="#FFE0E376" Offset="0"/>
</LinearGradientBrush>
</TabControl.Background>
<TabItem Header="Allgem." Cursor="Hand">
<TabItem.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF3F3F3" Offset="0"/>
<GradientStop Color="#FFF11818" Offset="1"/>
</LinearGradientBrush>
</TabItem.Background>
<StackPanel DockPanel.Dock="Bottom" Width="400" HorizontalAlignment="Left" Margin="10">
...
You can use a trigger to change the style only for the selected tab:
<TabControl DockPanel.Dock="Top">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF3F3F3" Offset="0"/>
<GradientStop Color="#FFF11818" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
<TabControl.Background>
<LinearGradientBrush EndPoint="1.115,1.13" StartPoint="0,-0.02">
<GradientStop Color="#FFFFFFFF" Offset="1"/>
<GradientStop Color="#FFE0E376" Offset="0"/>
</LinearGradientBrush>
</TabControl.Background>
<TabItem Header="Allgem." Cursor="Hand">
<StackPanel DockPanel.Dock="Bottom" Width="400"
HorizontalAlignment="Left" Margin="10">
...
</StackPanel>
</TabItem>
</TabControl>
This will set the background of the selected tab to the red gradient used in your sample code.