I'm trying to animate a RadialGradientBrush in my application. I get the super helpful exception:
Additional information: 'System.Windows.Style' value cannot be assigned to property 'Style' of object 'System.Windows.Controls.Border'. '[Unknown]' property does not point to a DependencyObject in path '(0).(1).[0].(2)'. Error at object 'System.Windows.Style' in markup file 'Eng.Modules.Core;component/system/grid/systemgridview.xaml' Line 252 Position 51.
I know it's something wrong with the indirect property targeting or partial path qualification in my DoubleAnimation's Storyboard.TargetProperty attribute. Any ideas?
<Border>
<Border.Resources>
<RadialGradientBrush x:Key="SomeBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Border.Resources>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SomeBrush}" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="SomeStoryBoard">
<Storyboard>
<!-- RIGHT HERE -->
<DoubleAnimation
Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Offset)"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="SomeStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
The first problem is that you are setting the background to a gradient brush in your DataTrigger. Because this gets applied later, the animation won't be able to find the brush (hence the cryptic error about not finding a dependency property). So the first thing I did was set the border's background to the brush manually, rather than in the trigger.
The second problem was how you were setting up the target property. You don't need to use the parenthesis syntax- it works just fine as follows: Background.GradientStops[0].Offset.
With these changes the border animates perfectly; here is the final mark-up:
<Border>
<Border.Background>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Border.Background>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="SomeStoryBoard">
<Storyboard>
<!-- RIGHT HERE -->
<DoubleAnimation
Storyboard.TargetProperty="Background.GradientStops[0].Offset"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="SomeStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
My Answer as per the comments in the code sample.
I've removed the MultiDataTrigger from my first post, as I can't get this working myself with bound collections. I may have to ask this to see if anyone has an answer to the issue.
<Grid>
<Grid.Resources>
<!-- Don't use a ControlTemplate. This destroys your control structure,
which doesn't make sense if all you want to do is animate the background. -->
<Style x:Key="MyAnimatedFeatureStyle" TargetType="Button">
<Style.Resources>
<RadialGradientBrush x:Key="GoldRadialGradientBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<Storyboard RepeatBehavior="Forever" AutoReverse="False" x:Key="SomeStoryBoard">
<DoubleAnimation Storyboard.TargetProperty="Background.GradientStops[0].Offset" From="0" To="1" Duration="0:0:1" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<!-- Your primary binding condition. Whatever this may be. -->
<DataTrigger Binding="{Binding Path=IsSOMTHINGSET}" Value="true" >
<!-- The Background is here in the setter, because we only want the background colored and animated on the binding condition. -->
<Setter Property="Background" Value="{StaticResource GoldRadialGradientBrush}" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BorderStoryBoard" Storyboard="{StaticResource SomeStoryBoard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BorderStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Style="{StaticResource MyAnimatedFeatureStyle}" />
</Grid>
I actually developed a solution to the problem just as Charlie posted his. It looks like we came up with close to the same thing. Just posting mine for reference:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ControlTemplate.Resources>
<RadialGradientBrush x:Key="GoldRadialGradientBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<Storyboard x:Key="SomeStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].Offset"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</ControlTemplate.Resources>
<Grid>
<Border Background="{StaticResource GoldRadialGradientBrush}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BorderStoryBoard" Storyboard="{StaticResource SomeStoryBoard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BorderStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Related
I am defining a Style for WPF TabControl TabItem.
I want to show a slight "pushed down" effect on TabItem when mouse (left button) is pressed down and the TabItem is not selected but I cannot find the Trigger for this.
I have tried to define a MultiTrigger with IsMouseLeftButtonDown and IsPressed properties but neither one is recognized.
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="bd" BorderThickness="1,1,0,0">
<Grid>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"
ContentSource="Header"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseLeftButtonDown" Value="True" /> <!--NOT RECOGNIZED-->
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="bd" Property="Background" >
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Green" Offset="0"/>
<GradientStop Color="Yellow" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How to set a Trigger that gets into action when mouse left button is pressed down on a TabItem ?
EDIT
I tried EventTrigger but I don't see any effect with the below
<EventTrigger RoutedEvent="UIElement.PreviewMouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="Green" Duration="0:0:3" />
</Storyboard>
<BeginStoryboard/>
<EventTrigger/>
Can it be achieved with this way somehow ?
This example shows you, how to work with EventTriggers.
Note
You might have to set your Highlighting-Logic fully in those Triggers, since values are overridden everytime, the Trigger fires.
<EventTrigger RoutedEvent="PreviewMouseDown" >
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard >
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="bd" Storyboard.TargetProperty="Background" Duration="0:0:3" BeginTime="0" AutoReverse="True">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Green}" />
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="PreviewMouseUp" >
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard >
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="bd" Storyboard.TargetProperty="Background" Duration="0:0:3" BeginTime="0" AutoReverse="True">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Transparent}" />
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
I have an animation that currently starts when a Control is loaded (the animation is essentially a waiting spinner, that is applied to an empty ContentControl).
The animation however will constantly spin taking up resources. What I'd like is for the animation to start / stop based on whether the animation control is visible or not, is this possible?
<Canvas.Triggers>
<EventTrigger RoutedEvent="ContentControl.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="SpinnerRotate"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:01.3"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
I must have this working for both Silverlight and WPF.
I have created an example of spinning an Ellipse based on the Visibility property. Perhaps you can use something from this.
<Canvas>
<Ellipse x:Name="Circle" Width="30" Height="30"
Canvas.Left="50"
Canvas.Top="50">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" CenterX="15" CenterY="15"/>
</Ellipse.RenderTransform>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard x:Name="SpinStoryboard">
<Storyboard >
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle"
From="0" To="360" Duration="0:0:01.3"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="SpinStoryboard"></StopStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</Canvas>
Might not be elegant, but works.
<Border x:Name="square" Height="20" Width="20" Background="Aqua">
<Border.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard x:Name="spinner">
<DoubleAnimation
Storyboard.TargetName="square"
Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0:0:01.3"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={RelativeSource Self}, Path=Visibility}" Value="{x:Static Visibility.Collapsed}">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="spinner"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
I am trying to animate a linear brush on a border using a data trigger but have come accross a problem where I cannot use the TargetName
My code is as follows, can anyone suggest a way to resolve this?
<Border Grid.Row="2" BorderThickness="10" Height="100" Width="100" >
<Border.BorderBrush>
<LinearGradientBrush>
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop x:Name="gradient" Color="Orange" Offset="0.5" />
<GradientStop Color="Yellow" Offset="1.0" />
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Resources>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=testBrdrWin, Path=Pulse}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="gradient"
Storyboard.TargetProperty="Offset"
From="0" To="1" Duration="0:0:1"
AutoReverse="True" RepeatBehavior="Forever"
/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=testBrdrWin, Path=Pulse}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="gradient"
Storyboard.TargetProperty="Offset"
To="0.5" Duration="0:0:01"
AutoReverse="False"
/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
</Border>
Thanks
You can't use Storyboard.TargetName here. You have to use a complete PropertyPath Syntax.
LinearGradientBrush has to be in Style itself.
In this example code I have removed all special things and therefore it will work stand alone too if you MouseOver the Border. Adapt it for your needs again.
<Border BorderThickness="10" Height="100" Width="100" >
<Border.Resources>
<Style TargetType="Border">
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.5" />
<GradientStop Color="Yellow" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="BorderBrush.(GradientBrush.GradientStops)[1].(GradientStop.Offset)"
From="0" To="1" Duration="0:0:1"
AutoReverse="True" RepeatBehavior="Forever"
/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
</Border>
I have a validation template as below. But we can set the error icon either inside control or left of the control. Can you please tell me how can i make it blink at the right of the control (Outside the textBox and right)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Storyboard x:Key="FlashErrorIcon">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Hidden}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:00.2000000" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:00.4000000" Value="{x:Static Visibility.Hidden}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:00.6000000" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:00.8000000" Value="{x:Static Visibility.Hidden}"/>
<DiscreteObjectKeyFrame KeyTime="00:00:01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Style x:Key="myErrorTemplate" TargetType="Control">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Ellipse DockPanel.Dock="Left"
ToolTip="{Binding ElementName=myTextbox,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
Width="15" Height="15"
Margin="-25,0,0,0"
StrokeThickness="1" Fill="Red" >
<Ellipse.Stroke>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FFFA0404" Offset="0"/>
<GradientStop Color="#FFC9C7C7" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Stroke>
<Ellipse.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource FlashErrorIcon}"/>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
<TextBlock DockPanel.Dock="Left"
ToolTip="{Binding ElementName=myControl,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
Foreground="White"
FontSize="11pt"
Margin="-15,5,0,0" FontWeight="Bold">!
<TextBlock.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource FlashErrorIcon}"/>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="myControl"/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource myErrorTemplate}" />
Change the location of the error icon via modifications to the template.
I have a button with it's control template set to a TextBlock. I want the text to "Glow" when the mouse is over it or it has gained focus by moving to it via the keyboard. I can't seem to get this working as I presume I'm setting the effect in the wrong place. Has anyone done this before that could share the xaml for it. Here is my style so far.
<!--Back Button-->
<Style x:Key="MoviesBackButton"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock Text="Back" Style="{DynamicResource MoviesButtonBackTextBlock}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width"
Value="40" />
<Setter Property="Height"
Value="25" />
<Setter Property="VerticalAlignment"
Value="Top" />
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="Margin"
Value="10,5,0,0" />
</Style>
<Style x:Key="MoviesButtonBackTextBlock"
TargetType="TextBlock">
<Setter Property="Foreground"
Value="{DynamicResource MoviesButtonBackTextBlockForeground}" />
<Setter Property="FontFamily"
Value="Segoe UI Light, Lucida Sans Unicode, Verdana" />
<Setter Property="FontSize"
Value="20" />
<Setter Property="TextOptions.TextHintingMode"
Value="Animated" />
</Style>
<LinearGradientBrush x:Key="MoviesButtonBackTextBlockForeground"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="LightGray"
Offset="0" />
<GradientStop Color="Gray"
Offset="1" />
</LinearGradientBrush>
Here's one solution for mouse-over glow. You can add similar event triggers for Button.GotKeyboardFocus/LostKeyboardFocus.
If I was doing this for real, I would probably create a custom control and use visual states. Search for 'Visual State Manager' for more information.
<Grid>
<Grid.Resources>
<Style x:Key="TextBoxGlow" TargetType="{x:Type Button}">
<Style.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Button.Content).(TextBlock.Effect).Opacity" From="0"
To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Button.Content).(TextBlock.Effect).Opacity" From="1"
To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<StackPanel>
<Button Style="{StaticResource TextBoxGlow}" Margin="5">
<TextBlock Text="I'm glowing" FontSize="28" Padding="10">
<TextBlock.Effect>
<DropShadowEffect BlurRadius="8" Color="Crimson" ShadowDepth="0" Opacity="0" />
</TextBlock.Effect>
</TextBlock>
</Button>
</StackPanel>
</Grid>