Storyboard 개요

이 항목에서는 Storyboard 개체를 사용하여 애니메이션을 구성하고 적용하는 방법을 보여 줍니다. 또한 Storyboard 개체를 대화형으로 조작하는 방법을 설명하고 간접적인 속성 대상 지정 구문에 대해서도 설명합니다.

필수 구성 요소

이 항목을 이해하려면 다양한 애니메이션 형식과 해당 기본 기능에 대해 잘 알고 있어야 합니다. 애니메이션 소개를 보려면 애니메이션 개요를 참조하세요. 또한 연결된 속성을 사용하는 방법을 알아야 합니다. 연결된 속성에 대한 자세한 내용은 연결된 속성 개요를 참조하세요.

스토리보드란?

애니메이션만이 타임라인의 유용한 형식은 아닙니다. 타임라인의 집합을 구성하고 속성에 타임라인을 적용하는 데 도움이 되는 다른 타임라인 클래스도 제공됩니다. 컨테이너 타임라인은 TimelineGroup 클래스에서 파생되며 ParallelTimelineStoryboard를 포함합니다.

Storyboard는 포함된 일정에 대한 대상 지정 정보를 제공하는 컨테이너 타임라인의 유형입니다. 스토리보드에는 다른 컨테이너 타임라인 및 애니메이션을 비롯한 모든 유형의 Timeline이 포함될 수 있습니다. Storyboard 개체를 사용하여 다양한 개체 및 속성에 영향을 미치는 타임라인을 단일 타임라인 트리에 조합함으로써 복잡한 타이밍 동작을 쉽게 구성하고 제어할 수 있습니다. 예를 들어 다음 세 가지 작업을 수행하는 단추가 필요하다고 가정해 봅니다.

  • 단추를 선택하면 단추가 커지고 색이 변경됩니다.

  • 클릭할 때 축소되었다가 다시 원래 크기로 커집니다.

  • 사용되지 않도록 설정되면 축소되었다가 50% 불투명도로 페이드됩니다.

이 경우 동일한 개체에 적용되는 여러 애니메이션 집합이 있고 단추 상태에 따라 다른 시간에 재생하려고 합니다. Storyboard 개체를 사용하여 애니메이션을 구성하고 그룹의 애니메이션을 하나 이상의 개체에 적용할 수 있습니다.

스토리보드를 사용할 수 있는 위치

Storyboard를 사용하여 애니메이션 적용 가능 클래스의 종속성 속성에 애니메이션 효과를 줄 수 있습니다(클래스가 애니메이션 적용 가능해지는 경우에 대한 자세한 내용은 애니메이션 개요 참조). 그러나 스토리보딩은 프레임워크 수준 기능이므로 개체는 FrameworkElement 또는 FrameworkContentElementNameScope에 속해야 합니다.

예를 들어, Storyboard를 사용하여 다음을 수행할 수 있습니다.

그러나 이름을 FrameworkElement 또는 FrameworkContentElement에 등록하지 않았거나 FrameworkElement 또는 FrameworkContentElement의 속성을 설정하는 데 사용되지 않은 SolidColorBrush에 애니메이션 효과를 적용하는 데 Storyboard를 사용할 수 없습니다.

Storyboard를 사용하여 애니메이션을 적용하는 방법

Storyboard를 사용하여 애니메이션을 구성하고 적용하려면 애니메이션을 Storyboard의 자식 타임라인으로 추가합니다. Storyboard 클래스는 Storyboard.TargetNameStoryboard.TargetProperty 연결된 속성을 제공합니다. 애니메이션에 이러한 속성을 설정하여 해당 대상 개체 및 속성을 지정합니다.

대상에 애니메이션을 적용하려면 트리거 작업 또는 메서드를 사용하여 Storyboard를 시작합니다. XAML에서는 BeginStoryboard 개체를 EventTrigger, Trigger 또는 DataTrigger와 함께 사용합니다. 코드에서는 Begin 메서드를 사용할 수도 있습니다.

다음 표에서는 각 Storyboard 시작 기술이 지원되는 다양한 위치, 즉 인스턴스별, 스타일, 컨트롤 템플릿 및 데이터 템플릿에 대해 설명합니다. "인스턴스별"은 스타일, 컨트롤 템플릿 또는 데이터 템플릿이 아닌 개체의 인스턴스에 직접 애니메이션 또는 storyboard를 적용하는 기술을 나타냅니다.

storyboard 시작 방법... 인스턴스별 스타일 컨트롤 템플릿 데이터 템플릿 예제
BeginStoryboardEventTrigger 스토리보드를 사용하여 속성에 애니메이션 효과 주기
BeginStoryboardTrigger 속성 속성 값 변경 시 애니메이션 트리거
BeginStoryboardMultiTrigger 속성 MultiTrigger 클래스 예제
BeginStoryboardDataTrigger 방법: 데이터가 변경될 때 애니메이션 트리거Changes
BeginStoryboardMultiDataTrigger MultiDataTrigger 클래스 예제
Begin 메서드 Yes 없음 아니요 아니요 스토리보드를 사용하여 속성에 애니메이션 효과 주기

다음 예제에서는 Storyboard를 사용하여 Rectangle 요소의 Width와 해당 Rectangle 요소를 그리는 데 사용되는 SolidColorBrushColor에 애니메이션 효과를 적용합니다.

<!-- This example shows how to animate with a storyboard.-->
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.Samples.Animation.StoryboardsExample" 
  WindowTitle="Storyboards Example">
  <StackPanel Margin="20">
    
    <Rectangle Name="MyRectangle"
      Width="100"
      Height="100">
      <Rectangle.Fill>
        <SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
      </Rectangle.Fill>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.MouseEnter">
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation 
                Storyboard.TargetName="MyRectangle"
                Storyboard.TargetProperty="Width"
                From="100" To="200" Duration="0:0:1" />
              
              <ColorAnimation 
                Storyboard.TargetName="MySolidColorBrush"
                Storyboard.TargetProperty="Color"
                From="Blue" To="Red" Duration="0:0:1" />  
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle> 
  </StackPanel>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Data;
using System.Windows.Shapes;
using System.Windows.Input;

namespace Microsoft.Samples.Animation
{
    public class StoryboardsExample : Page
    {
        public StoryboardsExample()
        {
            this.WindowTitle = "Storyboards Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(20);

            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "MyRectangle";

            // Create a name scope for the page.
            NameScope.SetNameScope(this, new NameScope());

            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
            this.RegisterName("MySolidColorBrush", mySolidColorBrush);
            myRectangle.Fill = mySolidColorBrush;

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 100;
            myDoubleAnimation.To = 200;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation,
                new PropertyPath(Rectangle.WidthProperty));

            ColorAnimation myColorAnimation = new ColorAnimation();
            myColorAnimation.From = Colors.Blue;
            myColorAnimation.To = Colors.Red;
            myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
            Storyboard.SetTargetProperty(myColorAnimation,
                new PropertyPath(SolidColorBrush.ColorProperty));
            Storyboard myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            myStoryboard.Children.Add(myColorAnimation);

            myRectangle.MouseEnter += delegate(object sender, MouseEventArgs e)
            {
                myStoryboard.Begin(this);
            };

            myStackPanel.Children.Add(myRectangle);
            this.Content = myStackPanel;
        }
    }
}

다음 섹션에서는 TargetNameTargetProperty 연결된 속성에 대해 자세히 설명합니다.

프레임워크 요소, 프레임워크 콘텐츠 요소 및 Freezable을 대상으로 지정

이전 섹션에서는 애니메이션이 대상을 찾기 위해서는 대상의 이름 및 애니메이션 효과를 주려는 속성을 알고 있어야 한다는 사실을 언급했습니다. 애니메이션 효과를 적용할 속성을 지정하는 것은 간단합니다. 애니메이션 효과를 적용할 속성의 이름으로 TargetProperty를 설정하기만 하면 됩니다. 애니메이션에 Storyboard.TargetName 속성을 설정하여 해당 속성에 애니메이션 효과를 적용할 개체의 이름을 지정합니다.

주의

TargetName의 대체 방법으로 Target 속성을 사용하여 개체에 직접 바인딩할 수 있지만 직렬화할 수는 없습니다. XAML에서 Target 개체를 올바로 참조할 수 있다는 보장이 없습니다.

TargetName 속성이 작동하려면 대상으로 지정된 개체에 이름이 있어야 합니다. XAML에서 FrameworkElement 또는 FrameworkContentElement에 이름을 할당하는 것은 Freezable 개체에 이름을 할당하는 것과 다릅니다.

프레임워크 요소는 FrameworkElement 클래스에서 상속하는 클래스입니다. 프레임워크 요소의 예로는 Window, DockPanel, Button, Rectangle이 있습니다. 기본적으로 모든 창, 패널 및 컨트롤은 요소입니다. 프레임워크 콘텐츠 요소는 FrameworkContentElement 클래스에서 상속하는 클래스입니다. 프레임워크 콘텐츠 요소의 예로는 FlowDocument, Paragraph가 있습니다. 형식이 프레임워크 요소인지 또는 프레임워크 콘텐츠 요소인지 확실하지 않은 경우 Name 속성을 가지는지 여부를 확인합니다. 이 속성을 가질 경우 프레임워크 요소이거나 프레임워크 콘텐츠 요소일 것입니다. 확실히 하려면 해당 형식 페이지의 상속 계층 구조 섹션을 확인합니다.

XAML에서 프레임워크 요소 또는 프레임워크 콘텐츠 요소를 대상으로 지정할 수 있도록 하려면 해당 Name 속성을 설정합니다. 코드에서는 RegisterName 메서드를 사용하여 요소의 이름을 NameScope를 만든 요소에도 등록해야 합니다.

위의 예제에서 가져온 다음 예제에서는 이름 MyRectangleFrameworkElement 형식의 Rectangle에 할당합니다.

<Rectangle Name="MyRectangle"
  Width="100"
  Height="100">
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "MyRectangle";

// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());

this.RegisterName(myRectangle.Name, myRectangle);

이름이 지정되면 해당 요소의 속성에 애니메이션 효과를 줄 수 있습니다.

<DoubleAnimation 
  Storyboard.TargetName="MyRectangle"
  Storyboard.TargetProperty="Width"
  From="100" To="200" Duration="0:0:1" />
Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
Storyboard.SetTargetProperty(myDoubleAnimation,
    new PropertyPath(Rectangle.WidthProperty));

Freezable 형식은 Freezable 클래스에서 상속하는 클래스입니다. Freezable의 예로는 SolidColorBrush, RotateTransform, GradientStop이 있습니다.

XAML에서 Freezable을 애니메이션 대상으로 지정할 수 있게 하려면 x:Name 지시문을 사용하여 이름을 할당합니다. 코드에서는 RegisterName 메서드를 사용하여 해당 이름을 NameScope를 만든 요소에도 등록합니다.

다음 예제에서는 Freezable 개체에 이름을 할당합니다.

<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
this.RegisterName("MySolidColorBrush", mySolidColorBrush);

그런 후 개체를 애니메이션 대상으로 지정할 수 있습니다.

<ColorAnimation 
  Storyboard.TargetName="MySolidColorBrush"
  Storyboard.TargetProperty="Color"
  From="Blue" To="Red" Duration="0:0:1" />  
Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
Storyboard.SetTargetProperty(myColorAnimation,
    new PropertyPath(SolidColorBrush.ColorProperty));

Storyboard 개체는 이름 범위를 사용하여 TargetName 속성을 확인합니다. WPF 이름 범위에 대한 자세한 내용은 WPF XAML 이름 범위를 참조하세요. TargetName 속성을 생략하면 애니메이션은 정의된 요소, 또는 스타일의 경우, 스타일이 지정된 요소를 대상으로 지정합니다.

Freezable 개체에 이름을 할당할 수 없는 경우도 있습니다. 예를 들어 Freezable이 리소스로 선언되거나 스타일에서 속성 값을 설정하는 데 사용되면 이름을 부여할 수 없습니다. 이름이 없기 때문에 직접적으로 대상으로 지정할 수 없지만 간접적으로 지정할 수는 있습니다. 다음 섹션에서는 간접 대상 지정을 사용하는 방법을 설명합니다.

간접 대상 지정

Freezable이 리소스로 선언되거나 스타일의 속성 값을 설정하는 데 사용되는 경우와 같이 Freezable을 애니메이션에서 직접적으로 대상으로 지정할 수 없는 경우가 있습니다. 이러한 경우 직접적으로 대상으로 지정할 수 없지만 여전히 Freezable 개체에 애니메이션 효과를 적용할 수 있습니다. TargetName 속성을 Freezable의 이름으로 설정하는 대신 Freezable가 "속한" 요소의 이름을 지정합니다. 예를 들어 사각형 요소의 Fill를 설정하는 데 사용된 SolidColorBrush는 해당 사각형에 속합니다. 브러시에 애니메이션 효과를 적용하려면 Freezable을 사용하여 설정한 프레임워크 요소 또는 프레임워크 콘텐츠 요소의 속성에서 시작하고 애니메이션 효과를 적용할 Freezable 속성에서 끝나는 속성 체인을 사용해서 애니메이션의 TargetProperty를 설정합니다.

<ColorAnimation 
  Storyboard.TargetName="Rectangle01"
  Storyboard.TargetProperty="Fill.Color"
  From="Blue" To="AliceBlue" Duration="0:0:1" />
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Freezable이 고정되면 복제본이 만들어지고 해당 복제본에 애니메이션 효과가 적용됩니다. 이 경우 원래 개체에 실제로 애니메이션 효과가 적용되는 것이 아니므로 원래 개체의 HasAnimatedProperties 속성은 계속해서 false를 반환합니다. 복제에 대한 자세한 내용은 Freezable 개체 개요를 참조하세요.

또한 간접 속성 대상 지정을 사용할 경우에는 존재하지 않는 개체를 대상으로 지정할 수 있습니다. 예를 들어 특정 단추의 BackgroundSolidColorBrush가 설정되어 있지만 실제로는 LinearGradientBrush가 단추의 배경을 설정하는 데 사용되었을 때 해당 색에 애니메이션 효과를 적용하려고 한다고 가정해 봅니다. 이러한 경우 예외는 throw되지 않습니다. LinearGradientBrushColor 속성 변경에 반응하지 않으므로 애니메이션이 시각적 효과를 나타내지 못합니다.

다음 섹션에서는 간접 속성 대상 지정 구문을 좀 더 자세히 설명합니다.

XAML에서 Freezable 속성을 간접적으로 대상으로 지정

XAML에서 Freezable의 속성을 대상으로 지정하려면 다음 구문을 사용합니다.

속성 구문
ElementPropertyName.FreezablePropertyName

Where

  • ElementPropertyNameFreezable을 사용하여 설정한 FrameworkElement의 속성입니다.

  • FreezablePropertyName은 애니메이션 효과를 적용할 Freezable의 속성입니다.

다음 코드에서는 사각형 요소의 Fill을 설정하는 데 사용된 SolidColorBrushColor에 애니메이션 효과를 적용하는 방법을 보여 줍니다.

<Rectangle
  Name="Rectangle01"
  Height="100"
  Width="100"
  Fill="{StaticResource MySolidColorBrushResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation 
            Storyboard.TargetName="Rectangle01"
            Storyboard.TargetProperty="Fill.Color"
            From="Blue" To="AliceBlue" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>

컬렉션 또는 배열에 포함된 Freezable을 대상으로 지정해야 하는 경우도 있습니다.

컬렉션에 포함된 Freezable을 대상으로 지정하려면 다음 경로 구문을 사용합니다.

경로 구문
ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

여기서 CollectionIndex는 해당 배열 또는 컬렉션에 있는 개체의 인덱스입니다.

예를 들어 사각형의 RenderTransform 속성에 TransformGroup 리소스가 적용되어 있고 포함된 변환 중 하나에 애니메이션 효과를 주려고 한다고 가정해 봅니다.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>

다음 예제에서는 이전 예제에 나온 RotateTransformAngle 속성에 애니메이션 효과를 적용하는 방법을 보여 줍니다.

<Rectangle
  Name="Rectangle02"
  Height="100"
  Width="100"
  Fill="Blue"
  RenderTransform="{StaticResource MyTransformGroupResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimation 
            Storyboard.TargetName="Rectangle02"
            Storyboard.TargetProperty="RenderTransform.Children[1].Angle"
            From="0" To="360" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>  

코드에서 Freezable 속성을 간접적으로 대상으로 지정

코드에서는 PropertyPath 개체를 만듭니다. PropertyPath를 만들 때 PathPathParameters를 지정합니다.

PathParameters를 만들려면 종속성 속성 식별자 필드 목록이 포함된 DependencyProperty 형식의 배열을 만듭니다. 첫 번째 식별자 필드는 Freezable을 사용하여 설정한 FrameworkElement 또는 FrameworkContentElement의 속성에 대한 필드입니다. 다음 식별자 필드는 대상으로 지정할 Freezable의 속성을 나타냅니다. 이를 FreezableFrameworkElement 개체에 연결하는 속성 체인으로 생각할 수 있습니다.

다음 예제는 사각형 요소의 Fill을 설정하는 데 사용된 SolidColorBrushColor를 대상으로 지정하는 종속성 속성 체인입니다.

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};

Path도 지정해야 합니다. PathPath에게 PathParameters를 해석하는 방법을 알려주는 String입니다. 여기서는 다음 구문을 사용합니다.

속성 경로 구문
(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

Where

  • OwnerPropertyArrayIndexFreezable을 사용하여 설정한 FrameworkElement 개체 속성의 식별자를 포함하는 DependencyProperty 배열의 인덱스입니다.

  • FreezablePropertyArrayIndex는 대상으로 지정할 속성의 식별자를 포함하는 DependencyProperty 배열의 인덱스입니다.

다음 예제에서는 위의 예제에 정의된 PathParameters가 함께 나오는 Path를 보여 줍니다.

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";

다음 예제는 이전 예제의 코드를 결합하여 사각형 요소의 Fill을 설정하는 데 사용된 SolidColorBrushColor에 애니메이션 효과를 적용합니다.


// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());

Rectangle rectangle01 = new Rectangle();
rectangle01.Name = "Rectangle01";
this.RegisterName(rectangle01.Name, rectangle01);
rectangle01.Width = 100;
rectangle01.Height = 100;
rectangle01.Fill =
    (SolidColorBrush)this.Resources["MySolidColorBrushResource"];

ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = Colors.Blue;
myColorAnimation.To = Colors.AliceBlue;
myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myColorAnimation, rectangle01.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myColorAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle01.Triggers.Add(myMouseEnterTrigger);

컬렉션 또는 배열에 포함된 Freezable을 대상으로 지정해야 하는 경우도 있습니다. 예를 들어 사각형의 RenderTransform 속성에 TransformGroup 리소스가 적용되어 있고 포함된 변환 중 하나에 애니메이션 효과를 주려고 한다고 가정해 봅니다.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>  

컬렉션에 포함된 Freezable을 대상으로 지정하려면 다음 경로 구문을 사용합니다.

경로 구문
(OwnerPropertyArrayIndex).(CollectionChildrenPropertyArrayIndex)[CollectionIndex].(FreezablePropertyArrayIndex)

여기서 CollectionIndex는 해당 배열 또는 컬렉션에 있는 개체의 인덱스입니다.

TransformGroup의 두 번째 변환인 RotateTransformAngle 속성을 대상으로 지정하려면 다음 PathPathParameters를 사용합니다.

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty,
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

다음 예제에서는 TransformGroup에 포함된 RotateTransformAngle에 애니메이션 효과를 적용하는 전체 코드를 보여 줍니다.

Rectangle rectangle02 = new Rectangle();
rectangle02.Name = "Rectangle02";
this.RegisterName(rectangle02.Name, rectangle02);
rectangle02.Width = 100;
rectangle02.Height = 100;
rectangle02.Fill = Brushes.Blue;
rectangle02.RenderTransform =
    (TransformGroup)this.Resources["MyTransformGroupResource"];

DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 0;
myDoubleAnimation.To = 360;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myDoubleAnimation, rectangle02.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty,
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle02.Triggers.Add(myMouseEnterTrigger);

Freezable을 시작 지점으로 사용해서 간접적으로 대상 지정

이전 섹션에서는 FrameworkElement 또는 FrameworkContentElement에서 시작한 후 Freezable 하위 속성에 대한 속성 체인을 만들어 간접적으로 Freezable을 대상으로 지정하는 방법을 설명했습니다. Freezable을 시작점으로 사용하여 해당 Freezable 하위 속성 중 하나를 간접적으로 대상으로 지정할 수도 있습니다. Freezable을 간접 대상 지정을 위한 시작점으로 사용할 때 한 가지 추가적인 제한 사항이 적용됩니다. 즉, Freezable과 간접적으로 대상으로 지정된 하위 속성 사이의 모든 Freezable이 고정되지 않아야 한다는 것입니다.

XAML에서 Storyboard를 대화형으로 제어

XAML(Extensible Application Markup Language)에서 스토리보드를 시작하려면 BeginStoryboard 트리거 작업을 사용합니다. BeginStoryboard는 애니메이션 효과를 적용할 개체 및 속성에 애니메이션을 배포하고 스토리보드를 시작합니다. (이 프로세스에 대한 자세한 내용은 애니메이션 및 타이밍 시스템 개요를 참조하세요.) Name 속성을 지정하여 BeginStoryboard에 이름을 지정하는 경우 제어 가능한 스토리보드로 만듭니다. 그러면 Storyboard를 시작한 후에 대화형으로 제어할 수 있습니다. 다음은 Storyboard를 제어하기 위해 이벤트 트리거와 함께 사용하는 제어 가능한 Storyboard 작업 목록입니다.

다음 예제에서는 제어 가능한 Storyboard 작업이 Storyboard를 대화형으로 제어하는 데 사용됩니다.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.SDK.Animation.ControllableStoryboardExample"
  WindowTitle="Fading Rectangle Example">
  <StackPanel Margin="10">

    <Rectangle
      Name="MyRectangle"
      Width="100" 
      Height="100"
      Fill="Blue">
    </Rectangle>

    <Button Name="BeginButton">Begin</Button>
    <Button Name="PauseButton">Pause</Button>
    <Button Name="ResumeButton">Resume</Button>
    <Button Name="SkipToFillButton">Skip To Fill</Button>
    <Button Name="StopButton">Stop</Button>

    <StackPanel.Triggers>
      <EventTrigger RoutedEvent="Button.Click" SourceName="BeginButton">
        <BeginStoryboard Name="MyBeginStoryboard">
          <Storyboard>
            <DoubleAnimation
              Storyboard.TargetName="MyRectangle" 
              Storyboard.TargetProperty="(Rectangle.Opacity)"
              From="1.0" To="0.0" Duration="0:0:5" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="PauseButton">
        <PauseStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="ResumeButton">
        <ResumeStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="SkipToFillButton">
        <SkipStoryboardToFill BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="StopButton">
        <StopStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
    </StackPanel.Triggers>
  </StackPanel>
</Page>

코드를 사용하여 Storyboard를 대화형으로 제어

앞의 예제에서는 트리거 작업을 사용해서 애니메이션 효과를 적용하는 방법을 살펴보았습니다. 코드에서는 Storyboard 클래스의 대화형 메서드를 사용 하여 스토리보드를 제어할 수도 있습니다. 코드에서 Storyboard를 대화형으로 만들려면 스토리보드의 Begin 메서드의 해당 오버로드를 사용하고 true를 지정하여 제어 가능하게 만들어야 합니다. 참조 된 Begin(FrameworkElement, Boolean) 자세한 페이지입니다.

다음 목록에서는 시작된 Storyboard를 조작하는 데 사용할 수 있는 메서드를 보여 줍니다.

이러한 메서드를 사용할 때의 이점은 Trigger 또는 TriggerAction 개체를 만들 필요가 없다는 것입니다. 조작하려는 제어 가능한 Storyboard에 대한 참조만 있으면 됩니다.

참고

Clock에서, 따라서 Storyboard에서도 수행되는 모든 대화형 작업은 다음 렌더링 직전에 발생하는 타이밍 엔진의 다음 틱에서 발생합니다. 예를 들어 Seek 메서드를 사용하여 애니메이션의 다음 지점으로 이동하는 경우 속성 값이 즉시 변경되지 않고 타이밍 엔진의 다음 틱에서 변경됩니다.

다음 예제에서는 Storyboard 클래스의 대화형 메서드를 사용하여 애니메이션을 적용하고 제어하는 방법을 보여 줍니다.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SDKSample
{

    public class ControllableStoryboardExample : Page
    {
        private Storyboard myStoryboard;

        public ControllableStoryboardExample()
        {

            // Create a name scope for the page.

            NameScope.SetNameScope(this, new NameScope());

            this.WindowTitle = "Controllable Storyboard Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(10);

            // Create a rectangle.
            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "myRectangle";

            // Assign the rectangle a name by
            // registering it with the page, so that
            // it can be targeted by storyboard
            // animations.
            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            myRectangle.Fill = Brushes.Blue;
            myStackPanel.Children.Add(myRectangle);

            //
            // Create an animation and a storyboard to animate the
            // rectangle.
            //
            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 1.0;
            myDoubleAnimation.To = 0.0;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
            myDoubleAnimation.AutoReverse = true;

            // Create the storyboard.
            myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.OpacityProperty));

            //
            // Create some buttons to control the storyboard
            // and a panel to contain them.
            //
            StackPanel buttonPanel = new StackPanel();
            buttonPanel.Orientation = Orientation.Horizontal;
            Button beginButton = new Button();
            beginButton.Content = "Begin";
            beginButton.Click += new RoutedEventHandler(beginButton_Clicked);
            buttonPanel.Children.Add(beginButton);
            Button pauseButton = new Button();
            pauseButton.Content = "Pause";
            pauseButton.Click += new RoutedEventHandler(pauseButton_Clicked);
            buttonPanel.Children.Add(pauseButton);
            Button resumeButton = new Button();
            resumeButton.Content = "Resume";
            resumeButton.Click += new RoutedEventHandler(resumeButton_Clicked);
            buttonPanel.Children.Add(resumeButton);
            Button skipToFillButton = new Button();
            skipToFillButton.Content = "Skip to Fill";
            skipToFillButton.Click += new RoutedEventHandler(skipToFillButton_Clicked);
            buttonPanel.Children.Add(skipToFillButton);
            Button setSpeedRatioButton = new Button();
            setSpeedRatioButton.Content = "Triple Speed";
            setSpeedRatioButton.Click += new RoutedEventHandler(setSpeedRatioButton_Clicked);
            buttonPanel.Children.Add(setSpeedRatioButton);
            Button stopButton = new Button();
            stopButton.Content = "Stop";
            stopButton.Click += new RoutedEventHandler(stopButton_Clicked);
            buttonPanel.Children.Add(stopButton);
            myStackPanel.Children.Add(buttonPanel);
            this.Content = myStackPanel;
        }

        // Begins the storyboard.
        private void beginButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Specifying "true" as the second Begin parameter
            // makes this storyboard controllable.
            myStoryboard.Begin(this, true);
        }

        // Pauses the storyboard.
        private void pauseButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Pause(this);
        }

        // Resumes the storyboard.
        private void resumeButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Resume(this);
        }

        // Advances the storyboard to its fill period.
        private void skipToFillButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.SkipToFill(this);
        }

        // Updates the storyboard's speed.
        private void setSpeedRatioButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(this, 3);
        }

        // Stops the storyboard.
        private void stopButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Stop(this);
        }
    }
}

Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Shapes
Imports System.Windows.Media
Imports System.Windows.Media.Animation

Namespace SDKSample

    Public Class ControllableStoryboardExample
        Inherits Page
        Private myStoryboard As Storyboard

        Public Sub New()

            ' Create a name scope for the page.

            NameScope.SetNameScope(Me, New NameScope())

            Me.WindowTitle = "Controllable Storyboard Example"
            Dim myStackPanel As New StackPanel()
            myStackPanel.Margin = New Thickness(10)

            ' Create a rectangle.
            Dim myRectangle As New Rectangle()
            myRectangle.Name = "myRectangle"

            ' Assign the rectangle a name by 
            ' registering it with the page, so that
            ' it can be targeted by storyboard
            ' animations.
            Me.RegisterName(myRectangle.Name, myRectangle)
            myRectangle.Width = 100
            myRectangle.Height = 100
            myRectangle.Fill = Brushes.Blue
            myStackPanel.Children.Add(myRectangle)

            '
            ' Create an animation and a storyboard to animate the
            ' rectangle.
            '
            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 1.0
            myDoubleAnimation.To = 0.0
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromMilliseconds(5000))
            myDoubleAnimation.AutoReverse = True

            ' Create the storyboard.
            myStoryboard = New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.OpacityProperty))

            '
            ' Create some buttons to control the storyboard
            ' and a panel to contain them.
            '
            Dim buttonPanel As New StackPanel()
            buttonPanel.Orientation = Orientation.Horizontal
            Dim beginButton As New Button()
            beginButton.Content = "Begin"
            AddHandler beginButton.Click, AddressOf beginButton_Clicked
            buttonPanel.Children.Add(beginButton)
            Dim pauseButton As New Button()
            pauseButton.Content = "Pause"
            AddHandler pauseButton.Click, AddressOf pauseButton_Clicked
            buttonPanel.Children.Add(pauseButton)
            Dim resumeButton As New Button()
            resumeButton.Content = "Resume"
            AddHandler resumeButton.Click, AddressOf resumeButton_Clicked
            buttonPanel.Children.Add(resumeButton)
            Dim skipToFillButton As New Button()
            skipToFillButton.Content = "Skip to Fill"
            AddHandler skipToFillButton.Click, AddressOf skipToFillButton_Clicked
            buttonPanel.Children.Add(skipToFillButton)
            Dim setSpeedRatioButton As New Button()
            setSpeedRatioButton.Content = "Triple Speed"
            AddHandler setSpeedRatioButton.Click, AddressOf setSpeedRatioButton_Clicked
            buttonPanel.Children.Add(setSpeedRatioButton)
            Dim stopButton As New Button()
            stopButton.Content = "Stop"
            AddHandler stopButton.Click, AddressOf stopButton_Clicked
            buttonPanel.Children.Add(stopButton)
            myStackPanel.Children.Add(buttonPanel)
            Me.Content = myStackPanel


        End Sub

        ' Begins the storyboard.
        Private Sub beginButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Specifying "true" as the second Begin parameter
            ' makes this storyboard controllable.
            myStoryboard.Begin(Me, True)

        End Sub

        ' Pauses the storyboard.
        Private Sub pauseButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Pause(Me)

        End Sub

        ' Resumes the storyboard.
        Private Sub resumeButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Resume(Me)

        End Sub

        ' Advances the storyboard to its fill period.
        Private Sub skipToFillButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.SkipToFill(Me)

        End Sub

        ' Updates the storyboard's speed.
        Private Sub setSpeedRatioButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(Me, 3)

        End Sub

        ' Stops the storyboard.
        Private Sub stopButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Stop(Me)

        End Sub

    End Class

End Namespace

스타일에서 애니메이션 효과 주기

Storyboard 개체를 사용하여 Style에서 애니메이션을 정의할 수 있습니다. Style에서 Storyboard를 사용하여 애니메이션 효과를 적용하는 것은 Storyboard 사용과 비슷하지만 다음 세 가지 예외가 있습니다.

  • TargetName을 지정하지 않습니다. Storyboard는 항상 Style이 적용된 요소를 대상으로 지정합니다. Freezable 개체를 대상으로 지정하려면 간접 대상 지정을 사용해야 합니다. 간접 대상에 대한 자세한 내용은 간접 대상 지정 섹션을 참조하세요.

  • EventTrigger 또는 TriggerSourceName을 지정할 수 없습니다.

  • 동적 리소스 참조 또는 데이터 바인딩 식을 사용하여 Storyboard 또는 애니메이션 속성 값을 설정할 수 없습니다. 이는 Style 내의 모든 항목은 스레드로부터 안전해야 하며 타이밍 시스템은 Storyboard 개체를 Freeze하여 스레드로부터 안전하게 만들어야 하기 때문입니다. Storyboard는 자신 또는 자식 타임라인이 동적 리소스 참조 또는 데이터 바인딩 식을 포함하는 경우 고정될 수 없습니다. 고정 및 기타 Freezable 기능에 대한 자세한 내용은 Freezable 개체 개요를 참조하세요.

  • XAML에서는 Storyboard 또는 애니메이션 이벤트에 대해 이벤트 처리기를 선언할 수 없습니다.

스타일의 Storyboard를 정의하는 방법을 보여 주는 예제에 대해서는 스타일에서 애니메이션 효과 적용 예제를 참조하세요.

ControlTemplate에서 애니메이션 효과 적용

Storyboard 개체를 사용하여 ControlTemplate에서 애니메이션을 정의할 수 있습니다. ControlTemplate에서 Storyboard를 사용하여 애니메이션 효과를 적용하는 것은 Storyboard 사용과 비슷하지만 다음 두 가지 예외가 있습니다.

  • TargetNameControlTemplate의 자식 개체만 참조할 수 있습니다. TargetName이 지정되지 않은 경우 애니메이션은 ControlTemplate가 적용된 요소를 대상으로 지정합니다.

  • EventTrigger 또는 TriggerSourceNameControlTemplate의 자식 개체만 참조할 수 있습니다.

  • 동적 리소스 참조 또는 데이터 바인딩 식을 사용하여 Storyboard 또는 애니메이션 속성 값을 설정할 수 없습니다. 이는 ControlTemplate 내의 모든 항목은 스레드로부터 안전해야 하며 타이밍 시스템은 Storyboard 개체를 Freeze하여 스레드로부터 안전하게 만들어야 하기 때문입니다. Storyboard는 자신 또는 자식 타임라인이 동적 리소스 참조 또는 데이터 바인딩 식을 포함하는 경우 고정될 수 없습니다. 고정 및 기타 Freezable 기능에 대한 자세한 내용은 Freezable 개체 개요를 참조하세요.

  • XAML에서는 Storyboard 또는 애니메이션 이벤트에 대해 이벤트 처리기를 선언할 수 없습니다.

ControlTemplate에서 스토리보드를 정의하는 방법을 보여 주는 예제는 ControlTemplate에서 애니메이션 효과 적용 예제를 참조하세요.

속성 값이 변경될 때 애니메이션 효과 주기

스타일 및 컨트롤 템플릿에서 트리거 개체를 사용하여 속성이 변경될 때 Storyboard를 시작할 수 있습니다. 예제를 보려면 속성 값이 변경될 때 애니메이션 트리거ControlTemplate에서 애니메이션 효과 적용을 참조하세요.

속성 Trigger 개체에 의해 적용된 애니메이션은 EventTrigger 애니메이션이나 Storyboard 메서드를 사용하여 시작한 애니메이션보다 좀 더 복잡한 방식으로 동작합니다. 이들은 다른 Trigger 개체로 정의된 애니메이션을 “전달”하지만 EventTrigger 및 메서드 트리거 애니메이션으로 구성됩니다.

참고 항목