Reputation: 4098
(This is an attempt to solve my earlier problem in a different way.)
I've created a user control which uses a RadialGradientBrush that I would like to be able to animate by setting a property on my view model. The gradient brush also has some properties that are bound to the view model.
The XAML for the user control is (some properties snipped for brevity):
<UserControl x:Class="WpfApplication1.AnimatedLineArrow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Controls="clr-namespace:Microsoft.Expression.Controls;assembly=Microsoft.Expression.Drawing"
mc:Ignorable="d" d:DesignHeight="150" d:DesignWidth="300"
Name="animatedLineArrow">
<Grid>
<Controls:LineArrow x:Name="ArrowControl"
StartCorner="{Binding ElementName=animatedLineArrow, Path=StartCorner, FallbackValue=TopRight}"
Width="{Binding ElementName=animatedLineArrow, Path=Width, FallbackValue=200}"
Height="{Binding ElementName=animatedLineArrow, Path=Height, FallbackValue=200}"
<Controls:LineArrow.Stroke>
<RadialGradientBrush RadiusX="0.2" RadiusY="1.0"
Center="{Binding ElementName=animatedLineArrow, Path=StartPoint, Mode=OneWay}"
GradientOrigin="{Binding ElementName=animatedLineArrow, Path=StartPoint, Mode=OneWay}">
<RadialGradientBrush.GradientStops>
<GradientStop Color="{Binding ElementName=animatedLineArrow, Path=HighlightColour, Mode=OneWay, FallbackValue=Cyan}" Offset="0.0" />
<GradientStop Color="{Binding ElementName=animatedLineArrow, Path=PrimaryColour, Mode=OneWay, FallbackValue=Navy}" Offset="1.0" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Controls:LineArrow.Stroke>
</Controls:LineArrow>
</Grid>
</UserControl>
The code-behind sets up the various dependency properties, and in the control's Loaded event, defines a Storyboard to animate the RadialGradientBrush's Center and GradientOrigin properties, and then defines the DataTriggers that should respond to the value of one of those dependency properties:
private void ConfigureAnimation(object sender, EventArgs e)
{
StartPoint = StartingPoints[StartCorner];
EndPoint = EndingPoints[StartCorner];
AnimatedLineArrow arrow = (AnimatedLineArrow)sender;
Storyboard storyboard = CreateStoryboard(arrow);
DataTrigger startTrigger = new DataTrigger
{
Binding = new Binding
{
Path = new PropertyPath(IsRunningProperty),
RelativeSource = RelativeSource.Self
},
Value = true
};
startTrigger.EnterActions.Add(new BeginStoryboard { Storyboard = storyboard, Name = "beginStoryboard" });
DataTrigger endTrigger = new DataTrigger
{
Binding = new Binding
{
Path = new PropertyPath(IsRunningProperty),
RelativeSource = RelativeSource.Self
},
Value = false
};
endTrigger.EnterActions.Add(new StopStoryboard { BeginStoryboardName = "beginStoryboard" });
Style style = new Style(typeof(AnimatedLineArrow));
style.Triggers.Add(startTrigger);
style.Triggers.Add(endTrigger);
arrow.Style = style;
}
private Storyboard CreateStoryboard(AnimatedLineArrow arrow)
{
Storyboard storyboard = new Storyboard();
PointAnimation originAnimation = new PointAnimation(StartingPoints[StartCorner], EndingPoints[StartCorner], Duration, FillBehavior.HoldEnd);
PointAnimation centreAnimation = originAnimation.Clone();
Storyboard.SetTarget(originAnimation, arrow);
Storyboard.SetTargetProperty(originAnimation, new PropertyPath(RadialGradientBrush.GradientOriginProperty));
Storyboard.SetTarget(centreAnimation, arrow);
Storyboard.SetTargetProperty(centreAnimation, new PropertyPath(RadialGradientBrush.CenterProperty));
storyboard.Children.Add(originAnimation);
storyboard.Children.Add(centreAnimation);
return storyboard;
}
When I try to run the project, it compiles successfully and the window loads successfully with the control in its default state. However, when the DataTrigger fires for the first time, I get the following exception:
System.InvalidOperationException was unhandled
Message='beginStoryboard' name cannot be found in the name scope of 'System.Windows.Style'.
Source=PresentationFramework
I've attached a sample project to demonstrate what I am trying to achieve.
Upvotes: 2
Views: 1217
Reputation: 41403
It looks like you'd need to register the name of your BeginStoryboard using Style.RegisterName. Something like:
//...
BeginStoryboard bs = new BeginStoryboard { Storyboard = storyboard, Name = "beginStoryboard" };
startTrigger.EnterActions.Add(bs);
//...
style.RegisterName(bs.Name, bs);
For your animation/storyboard, you are effectively trying to animate the RadialGradientBrush properties on the AnimatedLineArrow (not on the actual RadialGradientBrush). You'd either need to set the storyboard target to the RadialGradientBrush or expose another property on AnimatedLineArrow that you can animate.
Something like:
public static readonly DependencyProperty AnimatedPointProperty = DependencyProperty.Register("AnimatedPoint",
typeof(Point), typeof(AnimatedLineArrow), new FrameworkPropertyMetadata(new Point()));
public Point AnimatedPoint {
get { return (Point)this.GetValue(AnimatedLineArrow.AnimatedPointProperty); }
set { this.SetValue(AnimatedLineArrow.AnimatedPointProperty, value); }
}
private Storyboard CreateStoryboard(AnimatedLineArrow arrow)
{
Storyboard storyboard = new Storyboard();
PointAnimation originAnimation = new PointAnimation(StartingPoints[StartCorner], EndingPoints[StartCorner], Duration, FillBehavior.HoldEnd);
Storyboard.SetTarget(originAnimation, arrow);
Storyboard.SetTargetProperty(originAnimation, new PropertyPath(AnimatedPointProperty));
storyboard.Children.Add(originAnimation);
return storyboard;
}
Then in your AnimatedLineArrow.xaml, you'd need to use:
<RadialGradientBrush RadiusX="0.2" RadiusY="1.0"
Center="{Binding ElementName=animatedLineArrow, Path=StartPoint, Mode=OneWay}"
GradientOrigin="{Binding ElementName=animatedLineArrow, Path=AnimatedPoint, Mode=OneWay}">
<RadialGradientBrush.GradientStops>
<GradientStop Color="{Binding ElementName=animatedLineArrow, Path=HighlightColour, Mode=OneWay, FallbackValue=Cyan}" Offset="0.0" />
<GradientStop Color="{Binding ElementName=animatedLineArrow, Path=PrimaryColour, Mode=OneWay, FallbackValue=Navy}" Offset="1.0" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
Upvotes: 2