Combining WPF DataTriggers and Storyboard in code

北城以北 提交于 2020-01-15 07:18:06

问题


(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.


回答1:


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>


来源:https://stackoverflow.com/questions/5863722/combining-wpf-datatriggers-and-storyboard-in-code

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!