【问题标题】:Place storyboard in application resources将故事板放置在应用程序资源中
【发布时间】:2015-12-17 17:47:40
【问题描述】:

我需要在多个地方使用相同的故事板,因此我将故事板放在了我的 Application.Resources 中。当我尝试执行情节提要时,唯一的问题是我需要引用我想要制作动画的目标。这是我的故事板:

    <System:String x:Key="target">border2</System:String>
    <Storyboard x:Key="stHeight">
        <DoubleAnimationUsingKeyFrames 
            Storyboard.TargetProperty="(FrameworkElement.Height)" 
            Storyboard.TargetName="{DynamicResource target}"> 
            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CircleEase EasingMode="EaseOut"/>
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>

我为不同对象的高度设置动画的方法是更改​​动态资源目标。当故事板在当前窗口中时,我能够这样做。但是现在我想将它放在应用程序资源中,我不知道如何引用目标属性。


编辑

我之前发布的解决方案效果很好,但有时很难用代码创建复杂的动画。所以我想出的另一个替代解决方案是创建带有表情混合的故事板。所以我将一个随机控件拖动到表达式混合的主窗口并创建一个随机动画。假设动画是这样的:

         <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="grid">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

然后我复制该代码并将其粘贴到我的工作窗口中,而不是在 APP.XAML 中。

然后在我的代码中假设我有一个:

<Border Name="brdBorder" BorderBrush="Silver" BorderThickness="1" Margin="328,104,0,0"  Background="#FFE52E2E" HorizontalAlignment="Left" Width="94" Height="100" VerticalAlignment="Top" >
    <Border.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Border.RenderTransform>
</Border>

由于某种原因,变换组必须存在才能为对象设置动画。无论如何,假设我的工作窗口中有那个边界,我想用我用表情混合创建的相同动画来制作它。我将在代码中做的是:

Storyboard sb1 = FindResource("Storyboard1") as Storyboard;

foreach (var child in sb1.Children)
{
    Storyboard.SetTargetName(child, brdBorder.Name);
}

sb1.Begin(this);

然后我可以在我的工作窗口上为该边框设置动画。这样做的好处是我能够将相同的动画应用于多个对象(我认为这是创建资源的目的),当我尝试将情节提要放在资源字典或 app.xaml 中时,问题就出现了文件。当我这样做时,c# 能够找到情节提要,但情节提要的属性是只读的,因此我收到错误:

Cannot set a property on object 'System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames' because it is in a read-only state.

我想这样做的原因是将相同的动画应用于多个对象。一个变通的解决方案是使用代码构建基本动画,然后将更复杂的动画(如缓动函数等)保存为资源。让我告诉你我的意思。

在我的资源文件中,我放置了以下资源:

   <EasingDoubleKeyFrame x:Key="pleaseWork">
        <EasingDoubleKeyFrame.EasingFunction >
            <BackEase EasingMode="EaseOut" Amplitude="1"/>
        </EasingDoubleKeyFrame.EasingFunction>
    </EasingDoubleKeyFrame>

在表达式混合中,您可以构建更复杂的缓动函数。然后用后面的代码创建一个基本的故事板:

DoubleAnimation animation = new DoubleAnimation();
            animation.To = 336;   // final value
            //animation.From = 0;
            //animation.BeginTime = TimeSpan.FromSeconds(0); 
            animation.Duration = new Duration(TimeSpan.FromSeconds(5)); // how much time should animation last
            // here comes the magic:
            // note that I can bind to EasingDoubleKeyFrame in my resource file in xaml
            animation.EasingFunction = ((EasingDoubleKeyFrame)FindResource("pleaseWork")).EasingFunction; // apply the easing function 
            Storyboard.SetTarget(animation, groupBox1);  // what object will be animated?
            Storyboard.SetTargetProperty(animation, new PropertyPath(FrameworkElement.HeightProperty)); // what property will be animated

            Storyboard sb = new Storyboard();
            sb.Children.Add(animation);
            sb.Begin();

这使我能够在多个对象上使用相同的故事板。

【问题讨论】:

  • 我认为这是因为border2在全局范围内不存在,它只存在于MainWindow中。但我试图用 MainWindow.border2 替换border2,它仍然不起作用

标签: c# wpf xaml resources dynamicresource


【解决方案1】:

你不需要在任何地方引用目标,你可以在本地创建一个,这是动态资源查找的一大特点,例如

<!-- No more target here! -->
<Application.Resources>
    <Storyboard x:Key="SB_Height" x:Shared="False">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)"
                Storyboard.TargetName="{DynamicResource AnimationTarget}">
            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CircleEase EasingMode="EaseOut" />
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Application.Resources>
<!-- Somewhere else... -->
<Button Name="mybutton" Content="Test" Height="20">
    <Button.Resources>
        <sys:String x:Key="AnimationTarget">mybutton</sys:String>
    </Button.Resources>
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <BeginStoryboard Storyboard="{StaticResource SB_Height}"/>
        </EventTrigger>
    </Button.Triggers>
</Button>

【讨论】:

  • 如果使用该按钮我想为其他内容设置动画,那么我会将字符串资源 AnimationTarget 更改为其他内容,对吗?为什么当我将其更改为其他内容时它不起作用?当我为该按钮设置动画时它可以工作。
  • 我刚刚创建了资源:brdBorder 并且成功了。
  • 非常感谢您的帮助。当我想同时在不同的对象上运行相同的故事板时,它不起作用。我需要从代码运行故事板。
  • 动态资源在 uwp 中不存在。是否存在类似的解决方案?
  • @Briefkasten:我不知道。如果您想为引用故事板的元素设置动画,您应该可以完全删除Storyboard.TargetName。我没有安装 UWP 工具,想不出任何可行的方法......
【解决方案2】:

我终于找到了解决方案!!!!

如果您还记得我在 app.xaml 文件中放置情节提要时遇到的错误是:

无法在对象“System.Windows.Media.Animation.DoubleAnimationUsingKeyFrames”上设置属性,因为它处于只读状态。

换句话说,我无法修改情节提要的目标属性,我可以将其变红。所以解决方案是将Storyboard.TargetName="grid" 更改为Storyboard.TargetName="{binding}"


让我把所有东西放在一起:

step 1:

// create your custom storyboard with expression blend or xaml: 

// let's say it comes out as:

<Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="grid">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="grid">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 2:

// copy and paste  your storyboard to your app.xaml file or to a 
// resource dictionary (if you paste it in a resource dictionary do not 
// forget to merge the dictionaries so that your code is able to find the 
// storyboard as a resource)

<Application x:Class="FilesPro2._1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleK
   ....
   .....
   ...
   etc

--

step 3

// 将 Storyboard.TargetName="grid" 替换为 Storyboard.TargetName="{Binding}"。 // 你的故事板资源不应该是这样的:

<Application.Resources>


        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="90"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="103"/>
            </DoubleAnimationUsingKeyFrames>
            <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="{Binding}">
                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.75,0.5"/>
            </PointAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="{Binding}">
                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="75"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>

--

step 4

//create a method to make it easy to run the same animation on multiple objects:

void runStoryboard(string storyboardName, string objectName)
{
    Storyboard sb = FindResource(storyboardName) as Storyboard;

    foreach (var child in sb.Children)            
        Storyboard.SetTargetName(child, objectName);

    sb.Begin(this); // do not forget the this keyword
}

--

step 5

    // start your animation with the object you wish to animate
            runStoryboard("Storyboard1", brdBorder.Name);

重要提示:

请注意,在使用表达式混合创建情节提要时,有时表达式混合会为您正在制作动画的控件创建一个渲染变换组。在此示例中,我为寄宿生设置动画。为了让那个寄宿生动画它需要有

           <Border.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform/>
                    <RotateTransform/>
                    <TranslateTransform/>
                </TransformGroup>
            </Border.RenderTransform>

换句话说,如果您的动画处理 scaletransform skewtransform 等。然后将该渲染变换组放置到您计划制作动画的所有对象中。


我终于可以制作动画了:

<Border Name="brdBorder"  BorderBrush="Silver" BorderThickness="1" Margin="338,6,0,0" Background="#FFE52E2E" HorizontalAlignment="Left" Width="94" Height="38" VerticalAlignment="Top">
    <Border.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Border.RenderTransform>
</Border>

   //   AND

<Button Name="mybutton" Content="Test" Height="20" Click="mybutton_Click">
    <Button.RenderTransform>
        <TransformGroup>
            <ScaleTransform/>
            <SkewTransform/>
            <RotateTransform/>
            <TranslateTransform/>
        </TransformGroup>
    </Button.RenderTransform>
</Button>



AS:

runStoryboard("Storyboard1", brdBorder.Name);
runStoryboard("Storyboard1", mybutton.Name);

【讨论】:

  • 你本可以听取我的建议并将 x:Shared 设置为 false,这样你就不会任何陷入这种混乱。
  • 但是我需要从代码中调用情节提要,当我使用您的代码时它可以工作,但不能同时对多个动画对象进行动画处理..
  • 您需要将x:Shared 设置为false它有效,我试过了,如果您仍然遇到问题,您只需使用FindResource 两次即可,这样你就得到了两个故事板......
  • x:Sharedfalse 时,您可以在代码中设置之前只读的属性,抛出异常...
  • 是的,对不起。我会给你积分:)。谢谢您的帮助。我的解决方案可能看起来很长,因为我解释了一切。您唯一需要的是我在解决方案中展示的方法,并粘贴任何用绑定资源替换目标属性的情节提要。我想我解释得太多了。再次感谢
【解决方案3】:

为了一个

Windows Phone 8.1 应用程序

我遇到了同样的问题,我添加了许多解决方案来完成这个。

这是我的 App.xaml 文件

<Application.Resources>
        <ResourceDictionary>
            <Storyboard x:Key="ShowMenuAnimationKey">
                <DoubleAnimation
                            Storyboard.TargetName="{Binding}" //this is important
                            Storyboard.TargetProperty="(Canvas.Left)"
                            From="0" To="400" Duration="0:0:1">
                </DoubleAnimation>
            </Storyboard>
        </ResourceDictionary>
    </Application.Resources>

在我的页面中,我有这样的内容 (page.xaml)

<Grid x:Name="ViewPanel" 
                        Canvas.Left="0"
                        Canvas.ZIndex="1">
//Etc ....
</Grid>

现在,您将此方法绑定到一个事件(按钮点击或其他)

page.xaml.cs

private void MenuPanel_OnTap(object sender, GestureEventArgs e)
        {
            Storyboard sb = App.Current.Resources["ShowMenuAnimationKey"] as Storyboard;
            sb.Stop();
            Storyboard.SetTarget(sb, ViewPanel);
            sb.Begin();
        }

希望对你有帮助!

【讨论】:

    猜你喜欢
    • 2016-04-30
    • 2013-04-03
    • 2013-02-02
    • 1970-01-01
    • 2012-01-18
    • 2011-07-08
    • 2013-04-03
    • 2016-05-31
    • 1970-01-01
    相关资源
    最近更新 更多