[C#프로그래밍] [WPF] PC 카카오톡 만들기 #13 - ControlSlider(UserControl) 만들기

728x90
https://www.youtube.com/watch?v=iLDo5dc1hA4&list=PLlrfTSXS0LLKHfOfwM31jJw3SHuDnkF49&index=14

 

 📌 Slider 애니메이션 적용하기

 

- Controls 폴더에 사용자정의 컨트롤 ControlSlider 파일 생성

-ControlSlider.xaml

<UserControl x:Class="wpfLib.Controls.ControlSlider"
             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:local="clr-namespace:wpfLib.Controls" 
             xmlns:converters="clr-namespace:wpfLib.Converters"
             mc:Ignorable="d" 
             x:Name="root"
             d:DesignHeight="450" d:DesignWidth="800">

    <UserControl.Resources>
        <!--애니메이션추가-->
        <Storyboard
            x:Key="SlideLeftToRight"
            FillBehavior="Stop"
            Completed="Slide_Complete">
            <!--x좌표를 좌측 -> 우측 움직이는 애니메이션-->
            <!--Duration : 움직이는시간-->
            <DoubleAnimation
                Duration="{Binding Duration, ElementName=root}"
                Storyboard.TargetProperty="(RenderTransform).(TranslateTransform.X)"
                From="{Binding ActualWidth, ElementName=root, 
                Converter={StaticResource InvertConverter}}"
                To="0"
                />
        </Storyboard>
    </UserControl.Resources>
    
    <Grid>
        <ContentControl x:Name="content1">
            <ContentControl.RenderTransform>
                <TranslateTransform X="0" Y="0"/>
            </ContentControl.RenderTransform>
        </ContentControl>


        <!--처음 기본으로 보일 화면-->
        <ContentControl x:Name="content2">
            <ContentControl.RenderTransform>
                <TranslateTransform X="0" Y="0"/>
            </ContentControl.RenderTransform>
        </ContentControl>
    </Grid>
    
</UserControl>

 

- Converters 폴더에 클래스파일 InvertConverter 추가

-InvertConverter.cs


namespace wpfLib.Converters
{
    public class InvertConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return -(double)value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

 

- 컨버터 추가 및 우측 애니메이션 추가

-ControlSlider.xaml

    <UserControl.Resources>
        <converters:InvertConverter x:Key="InvertConverter"/>

...

            <!--우측-->
            <DoubleAnimation
                Duration="{Binding Duration, ElementName=root}"
                Storyboard.TargetProperty="(RenderTransform).(TranslateTransform.X)"
                From="0"
                To="{Binding ActualWidth, ElementName=root}"
                />

 

-ControlSlider.xaml.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace wpfLib.Controls
{
    /// <summary>
    /// ControlSlider.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class ControlSlider : UserControl, INotifyPropertyChanged
    {

        private ContentControl _backContent;
        private ContentControl _frontContent;

        private Storyboard _slideLeftToRight;
        //private Storyboard _slideRightToLeft;
        //private Storyboard _slideTopToBottom;
        //private Storyboard _slideBottomToTop;

        private Duration? _duration;

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }


        public ControlSlider()
        {
            InitializeComponent();

            _slideLeftToRight = (Storyboard)Resources["SlideLeftToRight"];
            //_slideRightToLeft = (Storyboard)Resources["SlideRightToLeft"];
            //_slideTopToBottom = (Storyboard)Resources["SlideTopToBottom"];
            //_slideBottomToTop = (Storyboard)Resources["SlideBottomToTop"];

            //_backContent : 처음 보여질 Content 
            _backContent = content2;
            _frontContent = content1;
        }

        private void Slide_Complate(Object sender, EventArgs e)
        {

        }


        //최초 Content 추가하는 함수 생성
        public void InitContent(object newContent)
        {
             //만약 Content가 null이면 초기화 
            _frontContent.Content = null;
            _backContent.Content = newContent;
        }

        //슬라이드 호출 함수
        public void Slide(object newContent, SlideType slideType)
        {
            if (_backContent.Content == null)
            {
                InitContent(newContent);
                return;
            }
        }


            //애니메이션 속도를 조정 할 수 있도록 함수 생성 
            public void SetAnimationSpeed(int milliseconds)
        {
            Duration = new Duration(new TimeSpan(0, 0, 0, 0, milliseconds));
        }


        public Duration? Duration
        {
            get
            {
                if (_duration == null)
                {
                    // Duration 기본 1초로 설정 
                    _duration = new Duration(new TimeSpan(0, 0, 1));
                }
                return _duration;
            }
            set
            {
                if (_duration != value)
                {
                    //// Duration 이  value와 다르다면 OnPropertyChanged 실행
                    _duration = value;
                    OnPropertyChanged();
                }
            }
        }

    }
}

 

-SlideType.cs

namespace wpfLib.Controls
{
    public enum SlideType
    {
        LeftToRight, RightToLeft, TopToBottom, BottomToTop
    }
}

 

-ControlSlider.xaml.cs

        //슬라이드 호출 함수
        public void Slide(object newContent, SlideType slideType)
        {
            if (_backContent.Content == null)
            {
                //만약 Content가 null이면 초기화 
                InitContent(newContent);
                return;
            }

            _frontContent = _backContent == content2 ? content1 : content2;

            _frontContent.Visibility = Visibility.Visible;
            _frontContent.Content = newContent;

            switch (slideType)
            {
                case SlideType.LeftToRight:
                    break;
                case SlideType.RightToLeft:
                    break;
                case SlideType.TopToBottom:
                    break;
                case SlideType.BottomToTop:
                    break;

            }
        }

 

-ControlSlider.xaml.cs

        //슬라이드 호출 함수
        public void Slide(object newContent, SlideType slideType)
        {
            if (_backContent.Content == null)
            {
                //만약 Content가 null이면 초기화 
                InitContent(newContent);
                return;
            }

            _frontContent = _backContent == content2 ? content1 : content2;

            _frontContent.Visibility = Visibility.Visible;
            _frontContent.Content = newContent;

            switch (slideType)
            {
                case SlideType.LeftToRight:
                    BeginSlideStoryboard(_slideLeftToRight);
                    break;
                case SlideType.RightToLeft:
                    //BeginSlideStoryboard(_slideRightToLeft);
                    break;
                case SlideType.TopToBottom:
                    //BeginSlideStoryboard(_slideTopToBottom);
                    break;
                case SlideType.BottomToTop:
                    //BeginSlideStoryboard(_slideBottomToTop);
                    break;
            }
        }

        private void BeginSlideStoryboard(Storyboard storyboard)
        {
            // front는 항상 좌 -> 우
            // back은 최초 한번 이후 뒤에 숨겨짐 
            Storyboard.SetTarget(storyboard.Children[0], _frontContent);
            Storyboard.SetTarget(storyboard.Children[1], _backContent);
            storyboard.Begin();
        }

 

-ControlSlider.xaml.cs

        private void Slide_Complete(object sender, EventArgs e)
        {
            //back은 숨김 
            _backContent.Visibility = Visibility.Collapsed;

            //back에 front Content할당 
            _backContent = _frontContent;
        }

Back에 frontContent 할당하는 이유
: Back은 보이지 않는 화면인데 다음에 frontContent 할당을 해 두면 다음에 들어오게 될 때 비교 대상이 frontContent가 된다.
  그래서 frontContent가 다시 backContent가 되는 것이고 frontContent는 새로운 content가 들어오게 되는 것이다.

- 주석 처리 했던 것들 모두 주석 풀어주기

 

 📌 작동 Test 하기

- 솔루션에 프로젝트 WpfControlSlider 추가 및 참조추가로 WpfLib 등록한 뒤 ViewModel 생성

- firstView.xaml

    <Grid Background="Green">
        <TextBlock Text="firstView" FontSize="50"/>
    </Grid>

 

- SecondView.xaml

    <Grid Background="Yellow">
        <TextBlock Text="secondView" FontSize="50"/>
    </Grid>

 

- MainWindow.xaml

<Window x:Class="WpfControlSlider.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfControlSlider" xmlns:controls="clr-namespace:wpfLib.Controls;assembly=wpfLib"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>

        <controls:ControlSlider Grid.Row="0" x:Name="slider"/>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            
            <Button Grid.Column="0" Content="LeftToRight" Click="LeftToRight_Clicked"/>
            <Button Grid.Column="1" Content="RightToLeft" Click="RightToLeft_Clicked"/>
            <Button Grid.Column="2" Content="TopToBottom" Click="TopToBottom_Clicked"/>
            <Button Grid.Column="3" Content="BottomToTop" Click="BottomToTop_Clicked"/>
        </Grid>
        
    </Grid>
</Window>

 

 

- MainWindow.xaml

    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodels:FirstViewModel}">
            <views:FirstView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
            <views:SecondView/>
        </DataTemplate>
    </Window.Resources>

 

- MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WpfControlSlider.ViewModels;
using wpfLib.ViewModels;

namespace WpfControlSlider
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ViewModelBase _currentViewModel = default;

        public ViewModelBase CurrentViewModel
        {
            get
            {
                if (_currentViewModel is FirstViewModel)
                {
                    _currentViewModel = new SecondViewModel();
                }
                else
                {
                    _currentViewModel = new FirstViewModel();
                }
                return _currentViewModel;
            }
        }


        public MainWindow()
            {
                InitializeComponent();
            }

        private void LeftToRight_Clicked(object sender, RoutedEventArgs e)
        {
            slider.SetAnimationSpeed(100);
            slider.Slide(CurrentViewModel, wpfLib.Controls.SlideType.LeftToRight);
        }

        private void RightToLeft_Clicked(object sender, RoutedEventArgs e)
        {
            slider.SetAnimationSpeed(300);
            slider.Slide(CurrentViewModel, wpfLib.Controls.SlideType.RightToLeft);
        }

        private void TopToBottom_Clicked(object sender, RoutedEventArgs e)
        {
            slider.SetAnimationSpeed(700);
            slider.Slide(CurrentViewModel, wpfLib.Controls.SlideType.TopToBottom);
        }

        private void BottomToTop_Clicked(object sender, RoutedEventArgs e)
        {
            slider.SetAnimationSpeed(1000);
            slider.Slide(CurrentViewModel, wpfLib.Controls.SlideType.BottomToTop);
        }
    }
}

 

이 상태에서 실행을 해 보면 애니메이션이 적용 된 모습을 확인 할 수 있다. 

728x90