I have a problem with color animation. This is my source:
<Window.Resources>
<hedit:BrushToColorConverter x:Key="BrushToColorConverter" />
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Resources>
<Storyboard x:Key="buttonAnimIn">
<!-- Problem line -->
<ColorAnimation Storyboard.TargetName="bntBack" Storyboard.TargetProperty="Color" To="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Converter={StaticResource BrushToColorConverter}}" />
</Storyboard>
<Storyboard x:Key="buttonAnimOut">
<ColorAnimation Storyboard.TargetName="bntBack" Storyboard.TargetProperty="Color" To="Blue" />
</Storyboard>
<Storyboard x:Key="buttonAnimForegroundIn">
<ColorAnimation Storyboard.TargetName="btnFore" Storyboard.TargetProperty="Color" To="Blue" />
</Storyboard>
<Storyboard x:Key="buttonAnimForegroundOut">
<ColorAnimation Storyboard.TargetName="btnFore" Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</ControlTemplate.Resources>
<Border Name="border"
BorderThickness="1"
Padding="4,2"
BorderBrush="DarkGray"
CornerRadius="3">
<Border.Background>
<SolidColorBrush Color="Blue" x:Name="bntBack" />
</Border.Background>
<ContentControl HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Content}">
<ContentControl.Foreground>
<SolidColorBrush Color="Red" x:Name="btnFore" />
</ContentControl.Foreground>
</ContentControl >
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource buttonAnimIn}" />
<BeginStoryboard Storyboard="{StaticResource buttonAnimForegroundIn}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource buttonAnimOut}" />
<BeginStoryboard Storyboard="{StaticResource buttonAnimForegroundOut}" />
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
The problem is:
Cannot convert the value in attribute 'Style' to object of type 'System.Windows.Style'. Cannot freeze this Storyboard timeline tree for use across threads. Error at object 'System.Windows.Controls.Button' in markup file 'HLSLEditor;component/mainwindow.xaml' Line 223 Position 25.
When using fixed colors it worked, but it cannot work with the Foreground color of the parent...
How do I do an animation to the foreground or background color?
Thanks!
You cannot freeze Bindings, you probably can get around this issue by declaring a color as a resource and then bind your Control's Background to it while using StaticResource in the animation.
e.g.
<Window.Background>
<SolidColorBrush Color="{DynamicResource Background}"/>
</Window.Background>
<Window.Resources>
<Color x:Key="Background">Green</Color>
</Window.Resources>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1"
To="{StaticResource Background}"/>
Alternative using a resource class:
public static class MyColors
{
public static Color MyHighlightColor = Color.FromArgb(255, 0, 88, 0);
}
<ColorAnimation Storyboard.TargetProperty="Foreground.Color"
Duration="0:0:1"
To="{x:Static local:MyColors.MyHighlightColor}"/>
I think that understanding the error might give you a way of fixing the problem.
Animation requires the use of threads besides the UI thread. So storyboards have to be freezable, which means that all the animations in the storyboard must be freezable, and everything those animations use must also be freezable.
Bindings aren't freezable - pretty much by definition, as they are a mechanism whereby a dependency property can be changed. You can't use a dynamic binding in a color animation - there's the possibility that the property could change while the animation was running. The same thing happens whether you're binding to an object or you're using DynamicResource.
The thing is, this is protecting you from something that you don't really want anyway. You don't really want the colors to change while the animation is running. That's not what you're trying to accomplish. You want the color resources that the animation is using to change if the user selects a different skin.
So instead of binding storyboards to skinnable resources, add the storyboards to the dictionary of resources that get set when the skin changes (using static bindings to set the colors), and use dynamic binding in your event triggers. That should work.
When I came across this problem I worked around it by modifying my style to contain two identical elements on top of each other - one for the 'normal' state and one for the 'pressed' state. The 'pressed' one had its Opacity set to 0 by default and the other one had an Opacity of 1. My animation changed the opacities from 0 to 1 and vice versa.
This approach avoided actually animating the Color property but produced the same effect whilst keeping everything in XAML. As the colours were set in the style definition rather than the animation they could be bound as required. This will probably not be suitable for all situations but for my fairly simple style it was a very quick way to achieve the desired effect.
Related
This question already has an answer here:
: 'Cannot animate 'Fill.Color' on an immutable object instance.'
(1 answer)
Closed 2 years ago.
I am new to WPF, I Have a rectangle that I try to animate to flesh red color if some boolean is true. and I want it to stop when the boolean is false. In order to do that, I used the <DataTrigger.ExitActions>
However, I still want my Fill color to change according to the AlertColor, but after the animation is stopped it seems like the binding is stoped also, and the background-color stays LightPink only.
why? how can I fix this and is there a better approach to animate the color only in case of a specific color and stop the animation when the color changes (with Binding)?
The XAML relevant code:
<Rectangle Width="840" Height="40">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="{Binding AlertUnit.AlertColor , FallbackValue=LightPink}"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding AlertUnit.Emergency}" Value="true" >
<DataTrigger.EnterActions>
<BeginStoryboard Name="FlashingRedAnimation">
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="White" Duration="0:0:1" AutoReverse="True"
RepeatBehavior="Forever"></ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashingRedAnimation" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
edit:
I found the problem it was a really silly one. the binding should be:
Binding AlertUnit.AlertColor.Color
not:
Binding AlertUnit.AlertColor
Thank you all for the help.
I produced a similar visual effect by animating the opacitity value of a brush rather than the colour.
<Style TargetType="{x:Type vctrl:perBlinkingBorder}">
<Style.Triggers>
<Trigger Property="IsBlinking" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="(BlinkingBorderBrush).Opacity">
<DoubleAnimation
AutoReverse="True"
RepeatBehavior="Forever"
From="1"
To="0"
Duration="0:0:0.5">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
To ensure that the visual effect is removed, I have two brush properties on the perBlinkingBorder control, and switch between them when the IsBlinking property is set.
More details and a demo project on my recent blog post.
I found the problem it was a really silly one.
the binding should be:
Binding AlertUnit.AlertColor.Color
not:
Binding AlertUnit.AlertColor
Thank you all for the help.
I have complicated animation, which has to run at startup and every time when mouse leaves buttons (I have many buttons)
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard x:Name="storyboard">
<Storyboard>
...
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- I have to do this for every button -->
<EventTrigger RoutedEvent="MouseEnter" SourceName="button1">
<SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
<PauseStoryboard BeginStoryboardName="storyboard"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave" SourceName="button1">
<ResumeStoryboard BeginStoryboardName="storyboard"/>
</EventTrigger>
</Window.Triggers>
Instead of creating new animations I reuse just one, which was played in Loaded, this is why here Seek.., Pause.. and ResumeStoryboard.
Everything works fine, but there is a lot of duplicated code.
I started thinking of making a style for buttons to avoid WET xaml coding. At first, I tried this (simply to see if idea is good)
<Button x:Name="button1">
<Button.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
...
Not very surprisingly I got an exception
System.Windows.Media.Animation Warning: 6 : Unable to perform action because the specified Storyboard was never applied to this object for interactive control.; Action='Seek'; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='56868664'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; TargetElement='System.Windows.Controls.Button'; TargetElement.HashCode='23765798'; TargetElement.Type='System.Windows.Controls.Button'
I have feeling it should be something to do with Storyboard.Target..., but trying to give window name produces same error
<Window x:Name="window"
...
<SeekStoryboard BeginStoryboardName="storyboard" Offset="0" Storyboard.TargetName="window"/>
What should I do? My aim is to have button style (defined in window resources), but I have problem to target storyboard within window triggers from child control event trigger.
I understand that you have many buttons. So the Storyboard in Window.Triggers should be used for all the buttons (animate once when the window is loaded). However the Storyboard requires a specific target, how could you set it for all the buttons? We can only duplicate the code and target each one by its name. That's the first problem.
The second problem appears while attempting to solve the first problem. When placing all the SeekStoryboard, PauseStoryboard, ... inside a Button's Style, it cannot find the Storyboard's name in the Style scope. That means the only way to fix this is place the Storyboard right inside the Button's Style. But then we cannot access this Storyboard easily inside Window.Triggers. However to solve the first problem we should also not place the Storyboard inside Window.Triggers scope. Why don't we trigger the Storyboard when the Button's Loaded instead? Then we can place all inside a Button's Style. The following code should solve all your problems:
<Window.Resources>
<Storyboard x:Key="sb">
<!-- more code here ... -->
</Storyboard>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Visibility,
RelativeSource={RelativeSource AncestorType=Window},
Mode=OneTime}" Value="Visible">
<DataTrigger.EnterActions>
<StaticResource ResourceKey="beginSb"/>
</DataTrigger.EnterActions>
</DataTrigger>
<EventTrigger RoutedEvent="MouseEnter">
<SeekStoryboard BeginStoryboardName="storyboard" Offset="0"/>
<PauseStoryboard BeginStoryboardName="storyboard"/>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<ResumeStoryboard BeginStoryboardName="storyboard"/>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
I wrote this code and got an exception:
Background property does not point to a dependencyobject in path '(0).(1)'
I saw this problem in other posts in the forum but didn't founded a solution.
<WrapPanel.Style>
<Style>
<Style.Triggers>
<Trigger Property "WrapPanel.Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard HandoffBehavior="Compose">
<Storyboard RepeatBehavior="Forever" AutoReverse="True">
<ColorAnimation
Storyboard.TargetProperty="(WrapPanel.Background).(SolidColorBrush.Color)"
Duration="00:00:01" To="Red"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
Any help with this?
You most likely failed to set a value for the initial background brush. You can either do so with a style setter, or else just set a value on the panel directly. The style setter is probably better:
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Blue"/>
</Setter.Value>
</Setter>
Note that you can also specify the TargetType property on your style, so that you don't have to prefix all property reference with WrapPanel:
<Style TargetType="WrapPanel">
You must set the Background property of the WrapPanel! Otherwise the WPF subsystem doesn't recognize it as a SolidColorBrush (could be another brush as well).
<WrapPanel Background="White">
...
</WrapPanel>
is sufficient.
I have a custom button-style with a ColorAnimation.
This works fine, but when pressed multiple times repeatedly, it stays stuck on the target color.
<Style TargetType="Button" x:Key="mainButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.10"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
To="Red"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I resolve this ?
Update
Yeh if you cannot afford to remove the Storyboard in Trigger.ExitActions then you do indeed have to address the From issue for intermediate starting Storyboard's yourself.
However specifying a hard-coded From isn't the only solution. You can let the animation reset itself to the underlying base color when it's starting up.
The benefit of this is by not specifying a From you got one less thing to keep track of with future updates.
<Storyboard AutoReverse="True">
<!-- By not specifying a To or From we pretty much reset the property to un-animated state(Exactly what the hard-coded from does) -->
<ColorAnimation Duration="0:0:0"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" />
<!-- This part is same as original time to kick into new Foreground as desired -->
<ColorAnimation Duration="0:0:1.5"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
To="Red" />
</Storyboard>
You have not set the From property on your ColorAnimation. So when you press the button in the middle of its animation, the Storyboard takes the current Foreground color value as its From, and this is the color that the animation reverses back to.
Now when you repeatedly press the button, the From color moves closer and closer to red, giving the impression that the color is stuck on red.
Update:
This answer only points out the problem. Refer to Viv's answer for an elegant solution
I'm trying to create a text box, which would glow when focused.
All samples of how to do this I've seen so far were based on OuterGlowBitmapEffect , and it appears that it does not work in .net 4.
The recommendation in the second article is to use blur effect. I have no idea on how to use blur to get object's outer layer to glow without distorting the inner content of the object.
Ultimately, I'm hoping to create a text box, which would display glow up animation when focused, and the glow would slowly (1-2 seconds) fade after the control has lost focus.
Any ideas on what is the best way to do this in wpf 4.0?
You can try to get a decent "Glow-Effect" with DropShadowEffect. Here is an example
Update. A TextBox that starts to "glow" when focused and the "glow" slowly fades out for two seconds when it loses focus
<TextBox Text="Test">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0"
Color="Gold"
Opacity="0"
BlurRadius="8"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1.0"
Storyboard.TargetProperty="(Effect).Opacity"
Duration="00:00:00"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0.0"
Storyboard.TargetProperty="(Effect).Opacity"
Duration="00:00:02"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Take a look at
http://wpfthemes.codeplex.com/
for a lot of good wpf theme ideas. In particular, look at the theme for the textbox in Bureau Black. I think what you want is actually what they use for their 'mouseover' attribute, but it should be easy to change that to a focused property instead.