Animation with semi-transparent colours has strange effect - wpf

I was working with some simple animations, when I noticed a strange effect when moving the mouse over a button. The colour of the background seemed to get dark and then lighten again, yet the animation code did not make it do that. Here is a stripped down version of the code that demonstrated this bizarre effect:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="950" Width="800">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Padding" Value="16,3,16,5" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="White" BorderBrush="#33323232" Height="{TemplateBinding Height}" MinWidth="{TemplateBinding MinWidth}" BorderThickness="2" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ContentPresenter Name="ContentPresenter" ContentSource="Content" TextElement.FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard DecelerationRatio="0.75">
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color" BeginTime="0:0:0" Duration="0:0:2" To="#1A323232" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard DecelerationRatio="0.75">
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color" BeginTime="0:0:0" Duration="0:0:2" From="#1A323232" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Height="32" Content="Click me" />
</Window>
Note that I slowed the animation right down in this example, to make this weird effect clearer for you. After experimenting, I discovered that if I use a darker colour to animate to, then the animation would go straight to that colour (work properly). I also noticed that if I set the alpha channel to 1 (FF), the problem also disappears. Try replacing the animations in the example above with these two:
<ColorAnimation Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background.Color"
BeginTime="0:0:0" Duration="0:0:2" To="#EAEAEA" />
...
<ColorAnimation Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background.Color"
BeginTime="0:0:0" Duration="0:0:2" From="#EAEAEA" />
Now, you should see the desired effect. This is what I had to do in the end, but I really wanted to use the semi-transparent colours instead. So, my question is does anybody know what is going wrong when animating with semi-transparent colours and how do I fix it?

When you animate an opaque white towards an increasingly transparent gray or black, the color first becomes increasingly gray, but at a certain point the white background starts to shine through more and more, so that finally (at #00000000) only the white background remains.
The effect is not so strange when you try this:
<StackPanel Background="White">
<Rectangle Width="100" Height="20" Fill="#ffff"/>
<Rectangle Width="100" Height="20" Fill="#eeee"/>
<Rectangle Width="100" Height="20" Fill="#dddd"/>
<Rectangle Width="100" Height="20" Fill="#cccc"/>
<Rectangle Width="100" Height="20" Fill="#bbbb"/>
<Rectangle Width="100" Height="20" Fill="#aaaa"/>
<Rectangle Width="100" Height="20" Fill="#9999"/>
<Rectangle Width="100" Height="20" Fill="#8888"/>
<Rectangle Width="100" Height="20" Fill="#7777"/>
<Rectangle Width="100" Height="20" Fill="#6666"/>
<Rectangle Width="100" Height="20" Fill="#5555"/>
<Rectangle Width="100" Height="20" Fill="#4444"/>
<Rectangle Width="100" Height="20" Fill="#3333"/>
<Rectangle Width="100" Height="20" Fill="#2222"/>
<Rectangle Width="100" Height="20" Fill="#1111"/>
</StackPanel>
which creates this output:
It looks entirely different on a black background:

Related

UWP, What is the correct syntax for a storyboard to access a path in a controltemplate?

I am working on a UWP windows 10 phone app and having trouble getting a storyboard to rotatetransform a path defined inside a buttons controltemplate.
I would prefer to acheive this in xaml. Do i specify for storyboard.targetproperty a quazi xaml path? => viewer.navbutton.PART_Arrow?
<Grid x:Name="layoutRoot" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="ShowNav">
<VisualState.Setters>
<Setter Target="navigation.(FrameworkElement.Height)" Value="Auto"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="HideNav">
<VisualState.Setters>
<Setter Target="navigation.(FrameworkElement.Height)" Value="0"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.Resources>
<Storyboard x:Name="rotateStoryBoard">
<DoubleAnimation Storyboard.TargetName="rotateDownArrow"
Storyboard.TargetProperty="Angle"
From="0"
To="90"
Duration="0:0:2"/>
</Storyboard>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0" x:Name="navigation">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" x:Name="navbar" Margin="0,0,0,0"/>
<Button Grid.Column="0" x:Name="refresh_Copy"
VerticalAlignment="Top"
Content="Refresh"
Click="refresh_Click"
RenderTransformOrigin="1.457,0.562" />
</Grid>
<Grid Grid.Row="1" x:Name="viewer">
<Button x:Name="navButton"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Canvas.ZIndex="1"
Click="navButton_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Center">
<Path x:Name="PART_Circle"
Fill="{ThemeResource ButtonBackgroundThemeBrush}"
Stroke="{ThemeResource ButtonBackgroundThemeBrush}"
StrokeThickness="1.5"
Data="M0,0 A30,30 0 0 0 60,0"/>
<Path x:Name="PART_Arrow"
Stroke="GreenYellow"
StrokeThickness="1.5"
Data="M12,6 L30,18 L48,6">
<Path.RenderTransform>
<RotateTransform x:Name="rotateDownArrow"
Angle="0"
CenterX="30"
CenterY="12"/>
</Path.RenderTransform>
</Path>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<WebView x:Name="Web"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
NavigationCompleted="Web_NavigationCompleted"
ScriptNotify="Web_OnScriptNotify"
Canvas.ZIndex="0"/>
</Grid>
</Grid>
What is the correct syntax for a storyboard to access a path in a controltemplate?
The Path control you want to access is inside a ControlTemplate. According to the XAML name scopes in templates section of this article:
Templates in XAML provide the ability to reuse and reapply content in a straightforward way, but templates might also include elements with names defined at the template level. That same template might be used multiple times in a page. For this reason, templates define their own XAML name scopes, independent of the containing page where the style or template is applied.
So the ControlTemplate has its own namescopes, you cannot directly access a control inside it by Name. You may need to use VisualTreeHelper class to help you access the controls.
But if you want a storyboard can access the control, you can let the storyboard in a same template with the control and they will be in a same scope.
In additionally, Button control template contains four default VisualStates, trigger the storyboard by one visual state may be recommended. The following code put the storyboard into the ControlTemplate and it is triggered when the button is pressed that you can reference.
<Grid
x:Name="layoutRoot"
Padding="50"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
...
<Grid x:Name="viewer" Grid.Row="1">
<Button
x:Name="navButton"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Canvas.ZIndex="1"
Click="navButton_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Center">
<Grid.Resources>
<Storyboard x:Name="rotateStoryBoard">
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:2"
From="0"
Storyboard.TargetName="rotateDownArrow"
Storyboard.TargetProperty="Angle"
To="90" />
</Storyboard>
</Grid.Resources>
<Path
x:Name="PART_Circle"
Fill="{ThemeResource ButtonBackgroundThemeBrush}"
Stroke="{ThemeResource ButtonBackgroundThemeBrush}"
StrokeThickness="1.5"
Data="M0,0 A30,30 0 0 0 60,0" />
<Path
x:Name="PART_Arrow"
Stroke="GreenYellow"
StrokeThickness="1.5"
Data="M12,6 L30,18 L48,6">
<Path.RenderTransform>
<RotateTransform x:Name="rotateDownArrow" Angle="0" CenterX="30" CenterY="12" />
</Path.RenderTransform>
</Path>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
Duration="0:0:2"
From="0"
Storyboard.TargetName="rotateDownArrow"
Storyboard.TargetProperty="Angle"
To="90" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
...
</Grid>

WPF change background of disabled buttons

I am trying to learn WPF and I have made a simple Tic Tac Toe application. The application works but I'm trying to style it a bit. I can't find out how to set the button disabled background for all my buttons (and keep the content visible). My question is how set up the code in the resource dictionary? The commented code is what I tried. To give all buttons the same style I added a resource dictionary.
The resource dictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!--<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding Path=SelectedDate,
StringFormat={}{0:d},
RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center" HorizontalAlignment="Left"
Padding="4,0,0,0" />
</Border>-->
<!--<Border BorderBrush="Aquamarine" BorderThickness="1"></Border>-->
<!-- Outer Rectangle with rounded corners. -->
<!-- Present Content (text) of the button. -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
App.xaml
<Application x:Class="FirstApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary Source="ResourceDictionary.xaml" />
</Application.Resources>
</Application>
MainWindow.xaml
<Window x:Class="FirstApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="525">
<Grid Margin="0,0,0.4,-19.2" Name="controlGrid">
<Button x:Name="A" Content="" HorizontalAlignment="Left" Margin="117,59,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="B" Content="" HorizontalAlignment="Left" Margin="223,59,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="C" Content="" HorizontalAlignment="Left" Margin="330,59,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="D" Content="" HorizontalAlignment="Left" Margin="117,157,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" RenderTransformOrigin="0.302,0.614" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="E" Content="" HorizontalAlignment="Left" Margin="223,157,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="F" Content="" HorizontalAlignment="Left" Margin="330,157,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="G" Content="" HorizontalAlignment="Left" Margin="117,255,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="H" Content="" HorizontalAlignment="Left" Margin="223,255,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<Button x:Name="I" Content="" HorizontalAlignment="Left" Margin="330,255,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="button_click" Background="#FFEBEB33" MouseEnter="button_enter" MouseLeave="button_leave" FontSize="36"/>
<ToolBar Margin="0,0,0,450.6">
<Button Name="newButton" Click="newButton_Click">New</Button>
</ToolBar>
</Grid>
</Window>
Possibly more than you were looking for:
<Style TargetType="{x:Type Button}" x:Key="ButtonBase">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Foreground" Value="#FF959595" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="10,0" />
<Setter Property="Margin" Value="2" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="Height" Value="25" />
<Setter Property="MinWidth" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation To="#FFFFFFFF" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0:0:0.07" />
<ColorAnimation To="#FFDEDEDE" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[1].(GradientStop.Color)"
Duration="0:0:0.07" />
<ColorAnimation To="#FF959595" Storyboard.TargetName="BrBrush"
Storyboard.TargetProperty="Color" Duration="0:0:0.07" />
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation To="#FF00B4E4" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0:0:0.07" />
<ColorAnimation To="#FF0083C3" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[1].(GradientStop.Color)"
Duration="0:0:0.07" />
<ColorAnimation To="#FF4C7B8F" Storyboard.TargetName="BrBrush"
Storyboard.TargetProperty="Color" Duration="0:0:0.07" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation To="#DBEDFD" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0:0:0.05" />
<ColorAnimation To="#C4E0FC" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[1].(GradientStop.Color)"
Duration="0:0:0.05" />
<ColorAnimation To="#4C7B8F" Storyboard.TargetName="BrBrush"
Storyboard.TargetProperty="Color" Duration="0:0:0.05" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation To="#EFEFEF" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[0].(GradientStop.Color)"
Duration="0:0:0" />
<ColorAnimation To="#EFEFEF" Storyboard.TargetName="BgBrush"
Storyboard.TargetProperty="(GradientBrush.GradientStops)[1].(GradientStop.Color)"
Duration="0:0:0" />
<ColorAnimation To="#D9D9D9" Storyboard.TargetName="BrBrush"
Storyboard.TargetProperty="Color" Duration="0:0:0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Chrome" BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<Border.BorderBrush>
<SolidColorBrush x:Name="BrBrush" Color="#ACACAC" />
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush x:Name="BgBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0" />
<GradientStop Color="#FFE5E5E5" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="#FFFFFF" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Foreground" Value="#000000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit based on comments/feedback by King King
The above example is certainly more than the OP has requested, however, considering the context of the question and their interest in Button control State styling, I felt it advantageous to provide additional detail (albeit without explanation admittedly).
Prior to <Setter Property="Tempalte"> I am altering the basic visual style of the Button control. I should hope none of this requires explaining as it seems fairly self explanatory.
Post <Setter Property="Template"> I am altering the visual states of the Button ControlTemplate (these being Normal, MouseOver, Pressed & Disabled). This allows me to customise the look and feel of the Button whilst it is in one of these states. In this example I am using a StoryBoard so that I can animate the Buttons State transitions.
The four States are styled in a similar format (animated gradient background & animated solid border), the differences between these states being the colours and durations they employ to provide the desired effect.
After the VisualStates I alter the main content template, how I want the Button control to be presented. I provide a ContentPresenter which will be used to display the content of the Button, I bind certain characteristics of the ContentPresenter to the Button being template, allowing for these characteristics to be controlled on a per Button basis if so desired. The ContentPresenter is surrounded by a Border which defines a BrBrush (boarder brush) and a BgBrush (background brush). These are the same brushes that are referenced in the VisualStates templates and provide a default state for them to work to/from.
Finally I alter the ControlTemplate.Triggers, customising the visual style of the Button (its foreground colour in this example) based on the Property Value of the activated Trigger.

WPF Animation to all rectangles in a grid

I am a starter in wpf programming. The following code shows an error when I tried to animate colour of all rectangles used in grid 2. Do you know why?
<ColorAnimation Storyboard.TargetName="grid2"
Storyboard.TargetProperty="Rectangle.Fill.Color"
To="Indigo"/>
Try this
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<Style x:Key="AniamateRectangle" TargetType="Grid">
<Style.Resources>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Fill.Color" BeginTime="0:0:0.2" Duration="0:0:0.5" To="Indigo"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<Grid Style="{StaticResource AniamateRectangle}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Height="100" Fill="Transparent" Width="100"/>
<Rectangle Height="100" Fill="Transparent" Width="100" Grid.Column="1"/>
<Rectangle Height="100" Fill="Transparent" Width="100" Grid.Column="2"/>
</Grid>

RenderTransform in WPF causing unexpected layout changes

I am trying to animate a number of shapes within a visualbrush but when I perform a rotation the shapes 'pulse'. I am assuming that as the shapes rotate the bounding boxes are forcing a layout pass. However since I am using a RenderTransform I wasn't expecting this to trigger layout changes.
This code illustrates the problem:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="200">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1"
Height="100" Width="100">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="inner_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="-360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Ellipse x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
<Ellipse x:Name="inner_Ellipse"
Stroke="Red" StrokeThickness="1"
Width="18" Height="18"
Margin="1,1,0,0"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
This is a simple sample of a much more complicated application where I am using the Visual Brushes to decorate 2d planes being manipulated in 3d. It all works well until I try and animate the brushes. I have tried several different approaches but always seem to run into this layout issue.
Any suggestions appreciated.
Thanks
Rob
I was able to track down the cause of your problem. It has to do with the Stretch="Uniform" Property setting on your VisualBrush. It appears the framework is computing a bounding rectangle on your VisuaBrush.Visual, and then stretching it to fit Border.Background. The following code should illustrate the behavior. I took out your inner_Ellipse and added an outer_Rectangle that should simulate the bounding rectangle being stretched:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="200">
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1"
Height="100" Width="100">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Rectangle"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Ellipse x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
<Rectangle x:Name="outer_Rectangle"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="20"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
</Window>
As to solving the problem, I am not sure. One way would be to use Stretch="None" on your VisualBrush, but that doesn't seem ideal because it then falls on you to deal with the size of your VisualBrush.Visual contents.
Well after a good deal of trial and error I have a working solution. The contents of the VisualBrush correctly scale and don't cause the bounding box problem:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight" >
<StackPanel>
<Border BorderBrush="Black" BorderThickness="1"
Height="400" Width="400">
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="inner_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="-360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="outer_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)">
<LinearDoubleKeyFrame KeyTime="0:0:3" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Background>
<VisualBrush Stretch="UniformToFill">
<VisualBrush.Visual>
<Border BorderBrush="Transparent" BorderThickness="1"
Width="200" Height="200">
<StackPanel Width="200" Height="200">
<Canvas>
<Rectangle x:Name="outer_Ellipse"
Stroke="Blue" StrokeThickness="1"
Width="20" Height="200"
Canvas.Left="40"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform/>
</Rectangle.RenderTransform>
</Rectangle>
<Ellipse x:Name="inner_Ellipse"
Stroke="Red" StrokeThickness="1"
StrokeDashArray="2"
Canvas.Top="30"
Canvas.Left="20"
Width="200" Height="200"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<RotateTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</StackPanel>
</Border>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</StackPanel>
</Window>
The 'secret' appears to be wrapping the canvas containing the brush contents in a StackPanel and wrapping this in a Border. (a Grid, DockPanel and WrapPanel will also work in place of the StackPanel).
<Border BorderBrush="Transparent" BorderThickness="1"
Width="200" Height="200">
<StackPanel Width="200" Height="200">
The Border must have a BorderBrush set and a BorderThickness. Also they must both have an explicit width and height. Here I have set values appropriate to the content so it scales correctly.
Without all 3 of these components the bounding box issue re occurs. Clearly something about the layout policies of the containers makes a difference but I have no idea what or why.
Not at all a satisfying solution and I would appreciate it if anyone can shine a light on what's going on here.
At least it works, both in the above demo and my main application, so far anyway!
Rob

WPF -- it's gotta be easier than I'm making it

I'm having the darndest time figuring this out: say I've got two Button and three TextBlocks. I want either button to trigger a simple Storyboard on ALL TextBlocks. Currently I'm trying to define a generic Textblock style that contains the Storyboard, and then the trigger comes from any Button click. This is the closest I've come but the app crashes on startup...what am I don't wrong here:
<Window.Resources>
<Style TargetType="TextBlock" >
<Setter Property="Foreground" Value="Blue" />
<Style.Resources>
<Storyboard x:Key="TextBlockOpacity" Storyboard.TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" />
</Storyboard>
</Style.Resources>
</Style>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="button" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>
<TextBlock x:Name="textBlock1" Margin="228,54,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="TextBlock" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock2" Margin="228,103,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="Hello" TextWrapping="Wrap"/>
</Grid>
If you "dedicate" the button to changing the opacity, you could harness its DataContext and animate it. Then simply bind your elements' Opacity to the DataContext:
(I've also refactored your xaml a bit)
<Window x:Class="SomeNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Storyboard x:Key="TextBlockOpacity" Storyboard.TargetName="button1" Storyboard.TargetProperty="DataContext" >
<DoubleAnimation From="0.1" To="1"/>
</Storyboard>
<Style TargetType="TextBlock" >
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Background" Value="LightGray" />
<Setter Property="FontSize" Value="26.667" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="Height" Value="45" />
<Setter Property="Opacity" Value="{Binding ElementName=button1, Path=DataContext}"/>
</Style>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ListBox.SelectionChanged">
<BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="button1" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button">
<Button.DataContext>
<System:Double>0</System:Double>
</Button.DataContext>
</Button>
<Button x:Name="button2" HorizontalAlignment="Right" Margin="0,54,29,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>
<ListBox x:Name="listBox1" Height="50" VerticalAlignment="Top">
<ListBox.Items>
<System:String>Text1</System:String>
<System:String>Text2</System:String>
</ListBox.Items>
</ListBox>
<TextBlock x:Name="textBlock1" Margin="51,114,61,0" Text="TextBlock" Height="45" VerticalAlignment="Top" Width="166" />
<TextBlock x:Name="textBlock2" Margin="51,0,74,42" Text="Hello" Height="45" Width="153" VerticalAlignment="Bottom" />
</Grid>
</Window>
Also note one thing - this is the approach to use if you want to minimize your code, and make it all happen in xaml. Your approach would anmate the Opacity of the whole Window. That's why in the code above, TextBlocks bind to the button's DataContext, which is itself animated.
It is of course doable without binding to a common value (the DataContext), but then you need to repeat X animations (because you need to set X TargetNames). This approach above is more easily extendable and maintainable.
EDIT
Added another Button and a ListBox for variety :)
Based on kek444's Xaml-only solution, I present a slightly improved version that doesn't rely on the DataContext of the button and can have multiple triggers.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<UIElement x:Key="OpacityCounter" Opacity="0"/>
<Style TargetType="TextBlock">
<Setter Property="Opacity" Value="{Binding Source={StaticResource OpacityCounter}, Path=Opacity}" />
</Style>
<Storyboard x:Key="OnClick1">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.Target="{StaticResource OpacityCounter}" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button2">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="button1" Width="131" Height="37" Content="Button 1" Margin="0,0,0,22"/>
<Button x:Name="button2" Width="131" Height="37" Content="Button 2" Margin="0,0,0,22"/>
</StackPanel>
<TextBlock x:Name="textBlock" Height="27" Text="TextBlock 1" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock1" Height="27" Text="TextBlock 2" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock2" Height="27" Text="TextBlock 3" TextWrapping="Wrap" />
<TextBlock x:Name="textBlock3" Height="27" Text="TextBlock 4" TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Window>
To use a ListBox as a trigger mechanism (provided you have a ListBox named "listbox1" someplace, add the following to Window.Triggers:
<EventTrigger RoutedEvent="Selector.SelectionChanged" SourceName="listbox1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
or to trigger off a specific ListBoxItem, you'll need (where item1 is a named ListBoxItem):
<EventTrigger RoutedEvent="ListBoxItem.Selected" SourceName="item1">
<BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
In your sample, you are defining the Storyboard inside a Style as a Resource, but then you are trying to access it as a Window resource. Try moving the Storyboard declaration to Window.Resources, then reference the Storyboard in the Style.
I don't know right off if it will do what you want, but I would start there.

Resources