WPF

[WPF] Slide Animation 만들기

Chanhongv 2024. 9. 2. 10:45

 

위 그림처럼 메뉴 선택시 밑줄이 움직이는 애니메이션을 주고 싶었다.

저걸 어떻게 구현할까 고민 하던 중 Grid. Column 으로 나누고 Border 를 만들어서 선택된 RadioButton 에 따라서

Border 의 Grid.Column 을 이동시키면 되겠구나 라고 생각했다.

 

NO Animation Code

   <Style TargetType="Border" x:Key="AnimatedBorderStyle">
            <Setter Property="Grid.Column" Value="0"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, ElementName=RightRadioButton}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <Int32Animation Storyboard.TargetProperty="(Grid.Column)"
                                                To="1" Duration="0:0:0.3" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                    <DataTrigger.ExitActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <Int32Animation Storyboard.TargetProperty="(Grid.Column)"
                                                To="0" Duration="0:0:0.3" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.ExitActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>

 

Border 스타일을 만들어서 적용을 해 보았더니, Border 표시가 되긴하지만 애니메이션 효과는 볼 수 없었다.

찾아보니 Grid.Column 으로는 애니메이션을 줄 수 없다는 의견들을 찾아서 포기했다.

 

xaml 상에서 여러가지를 시도해보았지만

' 스레드에서 사용하기 위해 이 Storyboard 시간 표시 막대 트리를 고정할 수 없습니다.'

오류가 자꾸 발생하였고

돌고 돌아 Behind code 에서 수정하기로 했다.

 

// Animation Border        
        <Border x:Name="AnimatedBorder"
                Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
                HorizontalAlignment="Left"
                BorderThickness="0 2 0 0"
                BorderBrush="#343D4C">
            <Border.RenderTransform>
                <TranslateTransform/>
            </Border.RenderTransform>
        </Border>

// Grid Column 개수는 3개
   <Grid.ColumnDefinitions>
       <ColumnDefinition Width="Auto" x:Name="GridColumn0"/>
       <ColumnDefinition Width="Auto" x:Name="GridColumn1"/>
       <ColumnDefinition Width="*"/>
   </Grid.ColumnDefinitions>

 

우선 Border 에 RenderTransform, TranslateTransform 을 주어야만 애니메이션 효과를 기대할 수 있다.

 

// ActualWidth 저장 변수
private double GridColumn0ActualWidth;
private double GridColumn1ActualWidth;

// Loaded 에서 ActualWidth 를 가져온다.
this.Dispatcher.BeginInvoke(new Action(() =>
 {
     GridColumn0ActualWidth = GridColumn0.ActualWidth;
     GridColumn1ActualWidth = GridColumn1.ActualWidth;
     if (IsCheckedBefore == true)
     {
         AnimatedBorder.Width = GridColumn0ActualWidth;
         AnimatedBorder.RenderTransform = new TranslateTransform(0, 0);
     }
     else
     {
         AnimatedBorder.Width = GridColumn1ActualWidth;
         AnimatedBorder.RenderTransform = new TranslateTransform(GridColumn0ActualWidth, 0);
     }
 }), System.Windows.Threading.DispatcherPriority.Loaded);

// 좌측 라디오버튼
private void ExecuteCheckBefore()
 {
     if (IsCheckedBefore == true)
     {
         SlideAnimation(AnimatedBorder, GridColumn0.ActualWidth, 0, GridColumn0ActualWidth);
     }
 }
 
 // 우측 라디오버튼
 private void ExecuteCheckAfter()
 {
     if (IsCheckedAfter == true)
     {
         SlideAnimation(AnimatedBorder, 0, GridColumn0.ActualWidth, GridColumn1ActualWidth);
     }
 }

 

RadioButton 에 Command 를 주어 내가 원하는 이벤트가 발생할때마다 SlidAnimation 함수가 실행되도록 하였다.

 

  private void SlideAnimation(UIElement element, double from, double to, double borderWidth)
  {
      // DoubleAnimation 생성
      DoubleAnimation animation = new DoubleAnimation
      {
          From = from,
          To = to,
          Duration = new Duration(TimeSpan.FromSeconds(0.2))
      };

      // 애니메이션 적용할 속성 설정
      Storyboard.SetTarget(animation, element);
      Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));

      // Storyboard 생성 및 애니메이션 추가
      Storyboard storyboard = new Storyboard();
      storyboard.Children.Add(animation);

      // 애니메이션 시작
      storyboard.Begin();

      AnimatedBorder.Width = borderWidth;
  }

 

Behind Code 에서 즉석으로 from, to 를 변경하였다.