스토리보드 에니메이션

WPF는 기본적으로 렌더링을 전담하는 스레드를 가지고 있음으로해서 자체적인 에니메이션 프레임워크를 가진다. 이번에는 WPF의 에니메이션 프레임워크를 기반으로 한 작은 에니메이션 샘플을 만들어 보고자 한다.

만들고자 하는 에니메이션은 이렇다. 화면상에 Windows Vista 문장을 렌더링하고, ‘W’문자부터 시작해서 한문자 한문자씩 움직이는데 움직이는 형태는 하나의 문자가 아래방향으로 이동하고 다시 원래 위치로 돌아오는 것이다. 여기에 더해서 <VisualBrush>를 이용해서 마치 물위에 비치는 효과를 넣어 마무리하고자 한다.

WPF Window Application 기반으로 기본적으로 XAML은 <Window>로 시작한다. 이 Element안을 작성하는 것이 시작이자 종착점이다. 먼저 Window의 배경을 설정해보자.

   
   
      
        
        
      
   
  

배경을 위에서 아래방향으로 검정색에서 회색으로 그라디언트 채움 효과를 지정한 것이다. 다음으로 주인공이 위치할 컨테이너로써 <Border>을 사용하며 주인공은 <TextBlock>이다. 다시 <Border>는 <StackPanel>을 컨테이너로 사용된다. <StackPanel>은 추가하는 Item을 순서대로 차곡차곡 옆으로, 또는 아래에 위치하도록 한다.

   
   
      
       Windows Vista 
      
   
  

<Border> 역시 배경 효과를 지정할 수 있는데, 체크 무늬를 넣어보도록 하자. 정적 리소스에 바인딩시키는 방법을 사용하고자 하는데 정적 리소스를 정의하기에 앞서 <Border>의 배경을 지정하도록 수정하자.

  

우리가 정의할 정적 리소스의 이름이 MyWireBrushResource 라는 것을 알 수 있다. 이제 이 리소스를 정의해보자.

   
   
     
       
         
           
           
         
       
     
   
  

이렇게 정적 리소스를 만들어 놓으면 다른 여러곳에서 x:Key 속성값을 참조함으로써 재활용이 가능함으로 적극 활용하길 바란다.

이제 주인공과 그 무대가 거의 완성되어져 간다. 무지막 무대 효과로써 수면에 반사되는 효과를 넣어보자.

여기서 사용한 방법은 이렇다. 이미 앞서 만들어 놓은 화면을 <VisualBrush>를 이용해서 또 하나의 <Rectangle>에 채움으로써 구현할 수 있다. <VisualBrush>의 Transform 중 ScaleTransform의 ScaleY의 값을 -1로 주어 위와 아래가 뒤집히게 하면 되는 것이다. 여기에 몇가지 많은 데이타 바인딩 개념이 사용되었는데, 특히 주목해야할 곳은 <VisualBrush>의 Visual 속성에 대한 데이터 바인딩이다. 바인딩의 ElementName의 값이 TextBorder인데, 이 값은 위에서 만든 <Border>의 Name이다. 즉, 앞서 만들어 놓은 <Border>의 모양이 그대로 브러시가 되어 <Rectangle>의 채움 속성으로 사용되는 것이다.

   
     
       
         
           
           
         
       
     
     
       
         
           
             
             
           
         
       
     
  

이제, 에니메이션을 위한 주인공과 배경에 대한 정의가 모두 마무리 되었다. 이제 에니메이션을 지정하는 것만 남았다. 에니메이션의 대상이 되는 것은 Windows Vista이고 이 문자열은 <TextBlock>의 내용이므로 <TextBlock>가 에니메이션의 대상이 된다. 즉, <TextBlock>안에 <TextBlock.TextEffects>와 <TextBlock.Triggers>를 추가하고 <DoubleAnimation>, <StoryBoard>, <Int32AnimationUsingKeyFrames>를 이용해 우리가 원하는 에니메이션을 지정하게 된다. 세세하게 살펴보도록 하자.

      
       Windows Vista 
       
       
       
        
     

기존의 <TextBlock>에 <TextBlock.TextEffects>와 <TextBlock.Triggers>가 새롭게 추가되었다. <TextBlock.TextEffects>는 문자에 대해 여러가지 효과를 줄 수 있는 것으로, 이동 효과에는 회전, 이동, Skew, 늘리기가 있다. <TextBlock.Triggers>는 에니메이션에 대한 정의와 시작 시점을 지정하는 것이다.

<TextBlock.TextEffects>의 정의는 다음과 같은데, 먼저 효과를 받을 문자의 수를 지정하기 위해 <TextEffect>의 PositionCount 속성값으로 1을 사용했으며 이 효과에 대한 이름을 MyTextEffect로 지정했다. 또한 우리가 원하는 에니메이션이 하나의 문자가 아래에서 다시 원래 자리와 에니메이션되는 이동 효과이므로 <TextEffect.Transform>의 <TranslateTransform>을 추가하였고 이름을 TextEffectTranslateTransform으로 주었다.

   
      
        
          
        
      
  

<TextEffect>와 <TranslateTransform>에 이름을 준 이유는 <TextBlock.Triggers>에서 이 이름을 통해 <TextEffect>와 <TranslateTransform>의 속성값을 적절한 시간에 변경시켜 에니메이션이 되도록 하기 위함이다. <TextBlock.Triggers>를 작성하기 전에, 먼저 우리는 영화감독이 되어 각 장면, 장면을 면밀하게 고려해야하는 고통이 필요하다. 고통스럽기도 하지만 한편으로는 멋지지 않은가!!? 먼저 생각해야할 것은 Windows Vista라는 문장은 공백문자 하나를 포함해서 총 13자이다. 이 13개의 문자가 0.5초씩 시간을 할당 받는데, 0.5초 동안 하는 액션(Action~~)은 Y축 아래로 20픽셀 이동하고 다시 원래 자리로 이동하는데 쓰인다. 즉, 0.5초의 반인 0.25초는 아래로 이동하고 나머지 0.25초는 원래 자리로 이동하는데 쓰인다. 비록 간단한 에니메이션의 구현이지만 등장인물의 수와 한치의 시간 오차없는 계산이 필요하다. 시간이 1초만 틀려져도 쌩뚱맞는 액션이 나오게 된다.

   
   
      
        
         
          
          
        
      
   
  

위는 <TextBlock.Triggers>의 아직은 완전하지 않은 시작 단계 코드이다. 여기에서 에니메이션이 시작할 시점을 <TextBlock>의 Loaded 이벤트가 발생할때 시작하도록 지정하고 있다. 여기서 필요한 추가 코드는 <DoubleAnimation>와 <Int32AnimationUsingKeyFrames>의 속성을 지정하는 것이다.

  

먼저 <DoubleAnimation>은 하나의 실수형 값만을 변경함으로써 우리가 원하는 에니메이션을 얻을 수 있는 경우 사용한다. <DoubleAnimation>을 살펴보면 앞서 정의한 TextEffectTranslateTransform이라는 이름의 <TextEffect.Transform>의 Y 속성을 0~20(From, To 속성)으로 0.25초 동안(Duration=”00:00:0.25″) 변화시킨다는 내용이다. RepeatBehavior=”Forever”는 모든 문자들에 대해 반복하다는 의미이고 AutoReverse=”True”는 Y 축으로 0~20까지 변경이 완료되면 다시 역으로 변경되도록 하는 것이다.

   
        
          
          
          
          
          
          
          
          
          
          
          
          
          
        
     

다음으로 <Int32AnimationUsingKeyFrames>은 하나의 정수형 값을 변경하는 에니메이션이면서 정확한 시간별로 프레임을 지정한다. <Int32AnimationUsingKeyFrames>의 대상은 TargetName과 TargetProperty로 지정해준다. 즉 앞서 이동 변환 효과로 설정했던 <TextEffect>의 Name과 <TextEffect>의 효과를 받을 문자의 인덱스 프로퍼티로써 PositionStart 값을 지정했다. 바로 이 PositionStart 프로퍼티가 Int32 형이고 <Int32AnimationUsingKeyFrames>가 에니메이션을 위해 변경시킬 값이다. 총 6.5초 동안 한 사이클을 도는 이 에니메이션의 시간을 0.5초 간격으로 Frame을 나누어주고 있다. 즉, <DiscreteInt32KeyFrame>을 통해 속성 KeyTime 시간에 Value 속성의 값으로 PositionStart 값을 설정하고 있다.

WPF에서 제공하는 에니메이션 기능은 유연하고 막강하다고 생각한다. WPF가 나오기 이전의 개발환경에서 에니메이션을 구현하려면 무척 많은 것을을 고민하고 기존의 것을 대폭적으로 수정해야했으나 WPF는 이미 모든 요소에 대해 에니메이션을 적용받을 수 있도록 되어있다. 이제는 정적인 컨텐츠가 아니라 항상 사용자가 교감하는 동적인 컨텐츠를 만들기가 어려운 것이 아니다. 개발자에게 있어 기술보다는 창의력으로 개발할 수 있는 기반을 제공하고 있다.

Brush for WPF

WPF의 2D Graphic의 효과 중에 채움(Fill) 효과에 대한 것이다. WPF의 채움은 Brush라는 개념으로 이루어지며 다음과 같은 종류가 있다.

  • SolidColorBrush
  • LinearGradientBrush
  • RadialGradientBrush
  • ImageBrush
  • DrawingBrush
  • VisualBrush

SolidBrush는 Geometry에 대해 단색으로 칠하는 브러쉬이고 LinearGradientBrush는 Gradient 색상을 선형으로 생성하여 채워준다. 또한 RadialGradientBrush는 방사형으로 Gradient 색상을 생성하여 채워주며 ImageBrush는 Image를 이용해 원하는 Geometry의 안을 채워준다. 그리고 DrawingBrush는 채우기 위한 내용을 사용자가 직접 또 다른 Geometry를 이용하여 만들어 채울 수 있다.  마지막으로 VisualBrush는 Control 등과 같은 내용(Content)를 이용하여 그 UI의 외형을 Geometry에 채울 수 있는 브러쉬이다. 참고로 브러쉬는 2차원뿐만이 아니라 3차원에서도 사용할 수 있다.

그럼 6개의 브러쉬에 대해 하나 하나 살펴 보기로 하자.

  
    
      
        
      
    

    
      
        
      
    

    
      
        
      
    

    
      
        
      
        
  

Geometry는 Rectangle이고 총 4개를 화면상에 렌더링했으며 Geometry의 Fill을 위해 SolidColorBrush를 사용하였다. 채움색의 지정을 위해 Color 속성을 사용하였고 투명도를 위해 Opacity를 사용하였다.

  
   
     
       
         
         
         
       
     
   

   
     
       
         
         
       
     
   

   
     
       
         
         
         
       
     
   

   
     
       
         
         
         
       
     
   
  

마찬가지로 Rectangle Geometry를 이용하여 화면상에 총 4개를 그렸다. 여러개의 GradientStop Element를 사용해서 선형으로 생성할 Gradient 색상을 단계적으로 지정할 수 있다.

  
  
     
       
         
         
         
       
     
  

  
     
       
         
         
         
       
     
  
  
     
       
         
         
         
       
     
  
  
     
       
         
         
         
       
     
  
  

방사형의 Gradient 색상을 생성하는 것으로 RadialGradientBrush Element를 사용하였고 방사형의 중심을 지정하기 위해 GradientOrigin을 사용하였다. GradientOrigin의 밤위는 0~1 사이의 실수값이다. LinearGradientBrush와 마찬가지로 다수의 GradientStop을 사용하여 색상과 Offset 위치를 지정할 수 있다. 효과적인 방사형의 Gradient 적용을 살펴보기 위해 Geometry로 Ellipse를 사용해보았다.

  
  
     
       
     
  

  
     
       
     
  

  
     
       
     
  

  
     
       
     
  

이미지를 이용하여 Geometry를 채우는 것으로써 Geometry의 크기에 맞게 이미지를 키울 수 도 있고, 키우지 않고 원래 크기대로 채워 그릴 수도 있으며, 타일형식으로 채워 넣을 수도 있다. 사용하는 Element는 ImageBrush이다.    
 

  
  
     
       
         
           
             
               
                 
               
             
             
           
         
       
     
  

  
     
       
         
           
             
               
                 
               
             
             
               
                 
               
               
                 
               
             
             
               
                 
               
               
                 
               
             
           
         
       
     
  

  
     
       
         
           
             
               
                 
               
             
             
           
         
       
     
  

  
     
       
         
           
             
               
                 
               
               
                 
                   
                     
                     
                     
                   
                 
               
             
             
               
                 
               
               
                 
                   
                     
                     
                     
                     
                   
                 
               
             
           
         
       
     
  
  

Effect가 적용된 Geometry 자체를 채움을 위한 브러쉬로써 사용할 수 있는 DrawingBrush Element의 사용예이다. 앞서 사용했던 다양한 Brsuh들이 적용된 Geometry가 다시 Brush로써 사용되는 것을 알 수 있다.

  
  
     
       
         
           
             
               
                 
                   
                     
                       
                         
                         
                       
                     
                     
                       
                         
                         
                       
                     
                   
                 
               
             
             Hello, World!
           
         
       
     
  

  
     
       
         
           
             Hello, World!
           
         
       
     
  

  
     
       
         
           
             Hello, World!
           
         
         
           
         
       
     
  

  
     
       
         
           
             
             
           
         
       
     
  
  

마지막으로 가장 융통성이 뛰어난 VisualBrush이다. Control은 물론이거니와 DrawingBrush의 기능까지도 포함할 수 있는 브러쉬로써 3차원으로 렌더링된 장면까지도 담을 수 있는 Brush이다. 또한 이 브러쉬를 이용하면 3차원 장면에서 2D GUI를 활용할 수 있게 하는 가장 화려한 브러쉬이다.

2D Geometry in WPF

  
    
      
    
  

Path를 이용한 Geometry를 렌더링 하는 가장 간단한 코드이다. Rectangle Geometry를 나타내고 있다. Path Element의 속성으로써 Stroke와 Fill을 줄 수 있는데, Stroke는 외곽선의 색상을 의미하고 Fill은 채움색을 의미한다. StrokeThickness는 외곽선의 굵기값이다.

   
     
       
         
         
         
       
     
   

Path를 이용한 Geometry를 렌더링하는 코드로써 여러개의 Geometry를 하나의 Geometry로 묶기 위해 GeometryGroup Element를 사용하였다. GemoetryGroup는 FillRule 속성을 가지고 있으며 EvenOdd와 Nonzero 값을 가질 수 있다. FillRule는 Geometry가 서로 겹치는 부분을 어떻게 처리할 것인지를 결정하는 것으로써 Nonzero는 그냥 채움이고 EvenOdd는 빈공간으로 채우지 않는다.

  
  
     
       
       
       
     
  
  

FillRule를 EvenOdd인 경우이다.

  
  
     
       
         
           
             
               
                 
                 
               
             
           
           
             
               
                 
                 
               
             
           
         
       
     
  
  

Path를 생성하는 방법으로써 직접 직선이나 다양한 곡선을 이용해 구성하는 방법이다. 직선을 이용한 예로써 LineSegment를 사용하였다.

  
  
     
       
         
           
             
               
                 
               
             
           
         
       
     
  
  

곡선을 이용한 예로써 ArcSegment를 이용하였다. Stroke를 완전하게 폐합하고자 한다면 PathFigure Element의 IsClosed 속성을 True로 주면 된다. 기본값은 False이다.

  
  
     
       
         
           
             
               
                 
               
             
           
         
       
     
  
  

곡선을 이용한 또 다른 예로써 QuadraticBezierSegment를 사용하였다.

  
  
     
       
         
           
             
               
                 
               
             
           
         
       
     
  
  

곡선을 이용한 또 다른 예로써 BezierSegment를 사용하였다.

  
  
     
       
       
       
       
       
         
           
             
               
                 
                   
                   
                 
               
             
           
         
       
     
  
  

Geometry를 이용하여 Image를 Clip하는 코드이다. 이미지를 화면에 나타내기 위해 가장 먼저 Image Element를 사용하였고 Source로 이미지 파일을 지정하였다. 이후에 Clip하기 위한 Geometry를 지정하기 위하는데, 두개 이상의 Geometry를 지정하기 위해 GeometryGroup Element를 사용하였으며 Geometry로써 단순 Geometry와 Path를 이용한 복합 Geometry를 사용하여 Clip을 위한 Geometry를 생성한다.

  
  
     
       
       
       
       
       
         
           
             
               
                 
                   
                   
                 
               
             
           
         
       
     
  
  

Path를 생성하기 위해 두개 이상의 Geometry를 사용할 경우 GeometryGroup로 묶게 되는데, 하나의 Geometry로써 PathGeometry를 사용하여, 이 PathGeometry Element 안쪽에 여러개의 Geometry 격인 PathFigure를 넣는 경우에 대한 코드이다.

  
  
     
       
         
           
             
           
           
             
               
               
               
               
               
                 
                   
                     
                       
                         
                           
                           
                         
                       
                     
                   
                 
               
             
           
         
       
     
  
  

Path 자체를 채움을 위한 Brush의 Pattern으로 사용하는 코드이다.

Path를 생성하는 세번째 경우로써 가장 최적화되고 함축적인 방법이다. Path Element의 Data 속성을 이용하여 좌표를 직접 하나 하나 지정한다. M은 Move, L은 Line, Z는 폐합을 의미한다. Data로써 Data=”M 10,10 A 100,50,45,1,0,200,100″와 Data=”M 10,100 C35,0 135,0 160,100 S285,200 310,100″, Data=”M 10,100 Q 200,200 300,100″, Data=”M 10,100 C 100,0 200,200 300,100″, Data=”M 10,50 V 200″, Data=”M 10,50 H 200″를 사용하여 그 결과를 확인해보기 바란다.

  
  
     
       
         
       
       
         
       
     
  
  

Path를 구상하는 Geometry에 대한 결합규칙을 지정하는 방법 중 Xor 규칙에 대한 코드와 그 결과이다. 겹치는 부분에 대해서 채움을 무시한다.

  
  
     
       
         
       
       
         
       
     
  
  

Path를 구성하는 Geometry를 하나의 Geometry로 합하는 것으로 겹합규칙을 Union을 사용하였다.

  
  
     
       
         
       
       
         
       
     
  
  

Path를 구성하는 Geometry들이 교차하는 부분만을 남기고 나머지는 무시하는 것으로 결합규칙으로써 Intersect를 사용하였다.

  
  
     
       
         
       
       
         
       
     
  
  

Path를 구성하는 Geometry들에서 첫번째에서 두번째 Geometry를 뺀 부분만을 남기는 것으로 결합규칙은 Exclude이다.

Non-Rectangle Window in WPF

오늘 작성해본 WPF에서 직사각형이 아닌 창을 나타내는 코드이다. UI 부분이므로 모든 주요 코드는 XAML에서 작성되며 드레그했을시에 창이 이동되는 이벤트 처리는 Behind Code(.CS 파일)에서 작성하였다. 먼저 XAML 코드는 아래와 같다.


  
    
      
        
          
          
          
          
        
      

      
        
          
            
            
            
            
            
            
            
            
          
        
      
    

    

창을 원하는 모양으로 만들기 위해서 반드시 설정해야할 Window의 속성은 위의 코드에서 분홍색으로 나타낸 WindowStyle, AllowTransparency, Background 속성이다. 그리고 Path Element를 이용해 원하는 모양을 그려주면 끝이다. 참으로 간단하면서도 명확하다. 실행 결과는 다음과 같다.

드레그를 하면 창이 이동을 하는데 그와 관련된 코드는 다음과 같다. 마우스의 왼쪽 버턴을 눌렸을 경우 발생하는 이벤트이다.

void eventMouseLeftButtonDown(object sender, 
                              MouseButtonEventArgs e) {
     DragMove();
}

WPF 어플리케이션 라이프 사이클(Application Life Cycle)


MSDN에 있는 WPF의 라이프 사이클에 대한 그림이다. 가운데 Application Object 상자 안이 코어 부분인데, 하나의 Application은 Run 매서드로 시작해서 Shutdown 매서드의 호출로 끝나게 된다. Shoutdown 매서드의 호출은 ShutdownMode의 값에 따라서 Application이 자동으로 호출해주는 경우와  사용자가 반드시 호출해주는 경우로 구분된다. 그리고 Activated, Deactivated, DispatcherUnhandledException, SessionEnding, Exit는 Application에 발생하는 이벤트이다. SessionEnding의 경우는 사용자가 OS를 Shutdown하거나 Logoff 시에 호출되는데 이 이벤트 안에서 OS의 종료를 취소시킬 수 있다. 또한 DispatcherUnhandledException 이벤트는 Application에서 처리되지 않는 예외가 발생했을 경우에 발생하는 범용 예외 처리가 가능한 곳으로 지정하지 않았을 경우 예외가 발생하면 Application은 자동으로 종료된다.


위의 그림은 WPF Window Application에 대한 또 다른 Life-Cycle이다. 모두 이벤트 명이다.