첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestSolution.zip
다운로드

[TestLibrary 프로젝트]

▶ MenuButton.xaml

<UserControl x:Class="TestLibrary.MenuButton"
    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:TestLibrary"
    mc:Ignorable="d" 
    d:DesignWidth="300"
    d:DesignHeight="300">
    <Grid Name="rootGrid">
        <Grid.Style>
            <Style TargetType="Grid">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="Cursor" Value="Hand" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Grid.Style>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"   />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"  />
            <ColumnDefinition Width="25" />
        </Grid.ColumnDefinitions>
        <Grid Name="triggerGrid" Grid.Row="0" Grid.Column="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*"    />
            </Grid.ColumnDefinitions>
            <Image Name="image" Grid.Row="0" Grid.Column="0"
                VerticalAlignment="Center"
                Margin="15 10"
                MaxHeight="20" />
            <Label Name="textLabel" Grid.Row="0" Grid.Column="1"
                VerticalAlignment="Stretch"
                VerticalContentAlignment="Center"
                Content="{Binding Text}" />
        </Grid>
        <Image Name="chevronImage" Grid.Row="0" Grid.Column="1"
            HorizontalAlignment="Left"
            Width="15"
            Source="IMAGE/Light24.png">
            <Image.RenderTransform>
                <RotateTransform x:Name="chevronRotateTransform"
                    CenterX="7.5"
                    CenterY="7.5"/>
            </Image.RenderTransform>
        </Image>
        <ItemsControl Name="childItemsControl" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
            Height="0"
            ItemsSource="{Binding}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:MenuButton
                        Height="30"
                        Image="{Binding Image}"
                        Text="{Binding Text}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>

 

728x90

 

▶ MenuButton.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace TestLibrary
{
    /// <summary>
    /// 메뉴 버튼
    /// </summary>
    public partial class MenuButton 
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region UI 테마 속성 - UIThemeProperty

        /// <summary>
        /// UI 테마 속성
        /// </summary>
        public static readonly DependencyProperty UIThemeProperty = DependencyProperty.Register
        (
            "UITheme",
            typeof(UITheme),
            typeof(MenuButton)
        );

        #endregion
        #region HOVER 배경 속성 - HoverBackgroundProperty

        /// <summary>
        /// HOVER 배경 속성
        /// </summary>
        public static readonly DependencyProperty HoverBackgroundProperty = DependencyProperty.Register
        (
            "HoverBackground",
            typeof(SolidColorBrush),
            typeof(MenuButton)
        );

        #endregion
        #region 애니메이션 속도 속성 - AnimationSpeedProperty

        /// <summary>
        /// 애니메이션 속도 속성
        /// </summary>
        public static readonly DependencyProperty AnimationSpeedProperty = DependencyProperty.Register
        (
            "AnimationSpeed",
            typeof(TimeSpan),
            typeof(MenuButton)
        );

        #endregion
        #region 이미지 속성 - ImageProperty

        /// <summary>
        /// 이미지 속성
        /// </summary>
        public static readonly DependencyProperty ImageProperty = DependencyProperty.Register
        (
            "Image",
            typeof(ImageSource),
            typeof(MenuButton)
        );

        #endregion
        #region 텍스트 속성 - TextProperty

        /// <summary>
        /// 텍스트 속성
        /// </summary>
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register
        (
            "Text",
            typeof(string),
            typeof(MenuButton)
        );

        #endregion
        #region 자식 리스트 속성 - ChildrenProperty

        /// <summary>
        /// 자식 리스트 속성
        /// </summary>
        public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register
        (
            "Children",
            typeof(List<MenuButton>),
            typeof(MenuButton)
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 자식 표시 여부
        /// </summary>
        private bool childrenVisible;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 테마 - Theme

        /// <summary>
        /// 테마
        /// </summary>
        public UITheme Theme
        {
            get
            {
                return (UITheme)GetValue(UIThemeProperty);
            }
            set
            {
                SetValue(UIThemeProperty, value);

                BitmapImage bitmapImage;

                switch(Theme)
                {
                    case UITheme.Light :

                        bitmapImage = new BitmapImage(new Uri(@"IMAGE/Light24.png", UriKind.Relative));

                        break;

                    case UITheme.Dark :

                        bitmapImage = new BitmapImage(new Uri(@"IMAGE/Dark24.png", UriKind.Relative));

                        break;

                    default :

                        throw new ArgumentOutOfRangeException();
                }

                Image image = FindName("chevronImage") as Image;

                image.Source = bitmapImage;

                Label textLabel = FindName("textLabel") as Label;

                if(Theme == UITheme.Light)
                {
                    textLabel.Foreground = Brushes.WhiteSmoke;
                }
                else
                {
                    textLabel.Foreground = new SolidColorBrush { Color = Color.FromRgb(30, 30, 30) };
                }
            }
        }

        #endregion
        #region HOVER 배경 - HoverBackground

        /// <summary>
        /// HOVER 배경
        /// </summary>
        public SolidColorBrush HoverBackground
        {
            get
            {
                return (SolidColorBrush)GetValue(HoverBackgroundProperty);
            }
            set
            {
                SetValue(HoverBackgroundProperty, value);
            }
        }

        #endregion
        #region 애니메이션 속도 - AnimationSpeed

        /// <summary>
        /// 애니메이션 속도
        /// </summary>
        public TimeSpan AnimationSpeed
        {
            get
            {
                return (TimeSpan)GetValue(AnimationSpeedProperty);
            }
            set
            {
                SetValue(AnimationSpeedProperty, value);
            }
        }

        #endregion
        #region 이미지 - Image

        /// <summary>
        /// 이미지
        /// </summary>
        public ImageSource Image
        {
            get
            {
                return (ImageSource)GetValue(ImageProperty);
            }
            set
            {
                SetValue(ImageProperty, value);
            }
        }

        #endregion
        #region 텍스트 - Text

        /// <summary>
        /// 텍스트
        /// </summary>
        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        #endregion
        #region 자식 리스트 - Children

        /// <summary>
        /// 자식 리스트
        /// </summary>
        public List<MenuButton> Children
        {
            get
            {
                return (List<MenuButton>)GetValue(ChildrenProperty);
            }
            set
            {
                SetValue(ChildrenProperty, value);

                foreach(MenuButton child in Children)
                {
                    child.ParentButton = this;
                }

                DataContext = Children;

                Image chevronImage = FindName("chevronImage") as Image;

                if(Children == null || Children.Count == 0)
                {
                    chevronImage.Visibility = Visibility.Hidden;
                }
                else
                {
                    chevronImage.Visibility = Visibility.Visible;
                }
            }
        }

        #endregion

        #region 부모 버튼 - ParentButton

        /// <summary>
        /// 부모 버튼
        /// </summary>
        public MenuButton ParentButton { get; private set; }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 확장 높이 - ExpandedHeight

        /// <summary>
        /// 확장 높이
        /// </summary>
        private double ExpandedHeight => Children.Count * ActualHeight + 5;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MenuButton()

        /// <summary>
        /// 생성자
        /// </summary>
        public MenuButton()
        {
            InitializeComponent();

            Children = new List<MenuButton>();

            Theme = UITheme.Light;

            AnimationSpeed = TimeSpan.FromMilliseconds(150);

            MouseEnter                  += UserControl_MouseEnter;
            MouseLeave                  += UserControl_MouseLeave;
            this.triggerGrid.MouseDown  += UserControl_MouseDown;
            this.chevronImage.MouseDown += UserControl_MouseDown;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 템플리트 적용시 처리하기 - OnApplyTemplate()

        /// <summary>
        /// 템플리트 적용시 처리하기
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Label textLabel = FindName("textLabel") as Label;

            textLabel.Content = Text;

            Image image = FindName("image") as Image;

            image.Source = Image;

            ItemsControl childItemsControl = FindName("childItemsControl") as ItemsControl;

            childItemsControl.DataContext = Children;

            childItemsControl.GetBindingExpression(ItemsControl.ItemsSourceProperty).UpdateTarget();

            foreach(MenuButton child in Children)
            {
                child.ParentButton = this;
            }

            Image chevronImage = FindName("chevronImage") as Image;

            if(Children == null || Children.Count == 0)
            {
                chevronImage.Visibility = Visibility.Hidden;
            }
            else
            {
                chevronImage.Visibility = Visibility.Visible;
            }

            Grid rootGrid = FindName("rootGrid") as Grid;

            rootGrid.Background = Background;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 초기화시 처리하기 - OnInitialized(e)

        /// <summary>
        /// 초기화시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnInitialized(EventArgs e)
        {
            InitializeComponent();

            base.OnInitialized(e);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 사용자 컨트롤 마우스 진입시 처리하기 - UserControl_MouseEnter(sender, e)

        /// <summary>
        /// 사용자 컨트롤 마우스 진입시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_MouseEnter(object sender, MouseEventArgs e)
        {
            Grid rootGrid = FindName("rootGrid") as Grid;

            ColorAnimation animation = new ColorAnimation
            {
                To       = HoverBackground.Color,
                Duration = AnimationSpeed
            };

            Storyboard storyboard = new Storyboard();

            storyboard.Children.Add(animation);

            Storyboard.SetTarget        (animation, rootGrid);
            Storyboard.SetTargetProperty(animation, new PropertyPath("Background.Color"));

            storyboard.Begin();
        }

        #endregion
        #region 사용자 컨트롤 마우스 이탈시 처리하기 - UserControl_MouseLeave(sender, e)

        /// <summary>
        /// 사용자 컨트롤 마우스 이탈시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_MouseLeave(object sender, MouseEventArgs e)
        {
            Grid rootGrid = FindName("rootGrid") as Grid;

            ColorAnimation animation = new ColorAnimation
            {
                To       = (Background as SolidColorBrush).Color,
                Duration = AnimationSpeed
            };

            Storyboard storyboard = new Storyboard();

            storyboard.Children.Add(animation);

            Storyboard.SetTarget        (animation, rootGrid);
            Storyboard.SetTargetProperty(animation, new PropertyPath("Background.Color"));

            storyboard.Begin();
        }

        #endregion
        #region 사용자 컨트롤 마우스 DOWN 처리하기 - UserControl_MouseDown(sender, e)

        /// <summary>
        /// 사용자 컨트롤 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void UserControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if(Children.Count == 0)
            {
                return;
            }

            if(this.childrenVisible)
            {
                MenuButton parent = ParentButton;

                while(parent != null)
                {
                    ItemsControl childItemsControl = FindName("childItemsControl") as ItemsControl;

                    parent.Reduce(childItemsControl.ActualHeight);

                    parent = parent.ParentButton;
                }

                Close();
            }
            else
            {
                MenuButton parent = ParentButton;

                while(parent != null)
                {
                    parent.Expand(ExpandedHeight);

                    parent = parent.ParentButton;
                }

                Open();
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 확장하기 - Expand(height)

        /// <summary>
        /// 확장하기
        /// </summary>
        /// <param name="height">높이</param>
        private void Expand(double height)
        {
            ItemsControl childItemsControl = FindName("childItemsControl") as ItemsControl;

            DoubleAnimation animation = new DoubleAnimation
            {
                From     = childItemsControl.ActualHeight,
                To       = childItemsControl.ActualHeight + height,
                Duration = AnimationSpeed
            };

            childItemsControl.BeginAnimation(HeightProperty, animation);
        }

        #endregion
        #region 축소하기 - Reduce(height)

        /// <summary>
        /// 축소하기
        /// </summary>
        /// <param name="height">높이</param>
        private void Reduce(double height)
        {
            ItemsControl childItemsControl = FindName("childItemsControl") as ItemsControl;

            DoubleAnimation animation = new DoubleAnimation
            {
                From     = childItemsControl.ActualHeight,
                To       = childItemsControl.ActualHeight - height,
                Duration = AnimationSpeed
            };

            childItemsControl.BeginAnimation(HeightProperty, animation);
        }

        #endregion

        #region 열기 - Open()

        /// <summary>
        /// 열기
        /// </summary>
        private void Open()
        {
            RotateTransform chevronRotateTransform = FindName("chevronRotateTransform") as RotateTransform;

            DoubleAnimation animation = new DoubleAnimation
            {
                To       = 90,
                Duration = AnimationSpeed
            };

            chevronRotateTransform.BeginAnimation(RotateTransform.AngleProperty, animation);

            Expand(ExpandedHeight);

            this.childrenVisible = true;
        }

        #endregion
        #region 닫기 - Close()

        /// <summary>
        /// 닫기
        /// </summary>
        private void Close()
        {
            RotateTransform chevronRotateTransform = FindName("chevronRotateTransform") as RotateTransform;

            DoubleAnimation angleDoubleAnimation = new DoubleAnimation
            {
                To = 0,
                Duration = AnimationSpeed
            };

            chevronRotateTransform.BeginAnimation(RotateTransform.AngleProperty, angleDoubleAnimation);

            foreach(MenuButton child in Children)
            {
                child.Close();
            }

            if(!this.childrenVisible)
            {
                return;
            }

            ItemsControl childItemsControl = FindName("childItemsControl") as ItemsControl;

            DoubleAnimation heightDoubleAnimation = new DoubleAnimation
            {
                From     = childItemsControl.ActualHeight,
                To       = 0,
                Duration = AnimationSpeed
            };

            childItemsControl.BeginAnimation(HeightProperty, heightDoubleAnimation);

            this.childrenVisible = false;
        }

        #endregion

        #region 배경 설정하기 - SetBackground(background)

        /// <summary>
        /// 배경 설정하기
        /// </summary>
        /// <param name="background">배경</param>
        public void SetBackground(Brush background)
        {
            this.rootGrid.Background = background;
        }

        #endregion
    }
}

 

300x250

 

▶ SideMenu.xaml

<UserControl Name="userControl" x:Class="TestLibrary.SideMenu"
    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:TestLibrary"
    mc:Ignorable="d" 
    d:DesignWidth="800"
    d:DesignHeight="300">
    <UserControl.Resources>
        <SolidColorBrush x:Key="ButtonBackgroundSolidColorBrushKey"
            Color="{Binding ButtonHover, ElementName=userControl}" />
        <SolidColorBrush x:Key="ButtonHoverSolidColorBrushKey"
            Color="{Binding ButtonBackground, ElementName=userControl}" />
        <SolidColorBrush x:Key="ShadowSolidColorBrushKey"
            Color="{Binding ShadowBackground, ElementName=userControl}" />
        <Style TargetType="local:MenuButton">
            <Setter Property="Background"      Value="{DynamicResource ButtonBackgroundSolidColorBrushKey}" />
            <Setter Property="HoverBackground" Value="{DynamicResource ButtonHoverSolidColorBrushKey}"      />
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition x:Name="menuColumnDefinition"   Width="400" />
            <ColumnDefinition x:Name="shadowColumnDefinition" Width="0"   />
        </Grid.ColumnDefinitions>
        <ContentPresenter Grid.Column="0"
            Content="{Binding Menu, ElementName=userControl}" />
        <Border Name="shadowBorder" Grid.Column="1"
            Background="{DynamicResource ShadowSolidColorBrushKey}" />
    </Grid>
</UserControl>

 

▶ SideMenu.xaml.cs

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

namespace TestLibrary
{
    /// <summary>
    /// 사이드 메뉴
    /// </summary>
    public partial class SideMenu
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 테마 속성 - ThemeProperty

        /// <summary>
        /// 테마 속성
        /// </summary>
        public static readonly DependencyProperty ThemeProperty = DependencyProperty.Register
        (
            "Theme",
            typeof(SideMenuTheme),
            typeof(SideMenu)
        );

        #endregion
        #region 상태 속성 - StateProperty

        /// <summary>
        /// 상태 속성
        /// </summary>
        public static readonly DependencyProperty StateProperty = DependencyProperty.Register
        (
            "State",
            typeof(MenuState),
            typeof(SideMenu)
        );

        #endregion
        #region 메뉴 너비 속성 - MenuWidthProperty

        /// <summary>
        /// 메뉴 너비 속성
        /// </summary>
        public static readonly DependencyProperty MenuWidthProperty = DependencyProperty.Register
        (
            "MenuWidth",
            typeof(double),
            typeof(SideMenu)
        );
        
        #endregion
        #region 메뉴 속성 - MenuProperty

        /// <summary>
        /// 메뉴 속성
        /// </summary>
        public static readonly DependencyProperty MenuProperty = DependencyProperty.Register
        (
            "Menu",
            typeof(ScrollViewer),
            typeof(SideMenu)
        );

        #endregion
        #region 그림자 배경 속성 - ShadowBackgroundProperty

        /// <summary>
        /// 그림자 배경 속성
        /// </summary>
        public static readonly DependencyProperty ShadowBackgroundProperty = DependencyProperty.Register
        (
            "ShadowBackground",
            typeof(Brush),
            typeof(SideMenu)
        );

        #endregion
        #region 버튼 배경 속성 - ButtonBackgroundProperty

        /// <summary>
        /// 버튼 배경 속성
        /// </summary>
        public static readonly DependencyProperty ButtonBackgroundProperty = DependencyProperty.Register
        (
            "ButtonBackground",
            typeof(Brush),
            typeof(SideMenu)
        );

        #endregion
        #region 버튼 HOVER 속성 - ButtonHoverProperty

        /// <summary>
        /// 버튼 HOVER 속성
        /// </summary>
        public static readonly DependencyProperty ButtonHoverProperty = DependencyProperty.Register
        (
            "ButtonHover",
            typeof(Brush),
            typeof(SideMenu)
        );

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 표시 여부
        /// </summary>
        private bool isShown;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 테마 - Theme

        /// <summary>
        /// 테마
        /// </summary>
        public SideMenuTheme Theme
        {
            get
            {
                return (SideMenuTheme)GetValue(ThemeProperty);
            }
            set
            {
                if(value == SideMenuTheme.None)
                {
                    return;
                }

                SetValue(ThemeProperty, value);

                SolidColorBrush buttonBackground;
                SolidColorBrush buttonHoverBackground;
                SolidColorBrush background;

                switch(value)
                {
                    case SideMenuTheme.Default :

                        background            = new SolidColorBrush { Color = Color.FromArgb(205, 20, 20, 20) };
                        buttonBackground      = new SolidColorBrush { Color = Color.FromArgb(50 , 30, 30, 30) };
                        buttonHoverBackground = new SolidColorBrush { Color = Color.FromArgb(50 , 70, 70, 70) };

                        break;

                    case SideMenuTheme.Primary :

                        background            = new SolidColorBrush { Color = Color.FromArgb(205, 24, 57 , 85 ) };
                        buttonBackground      = new SolidColorBrush { Color = Color.FromArgb(50 , 35, 85 , 126) };
                        buttonHoverBackground = new SolidColorBrush { Color = Color.FromArgb(50 , 45, 110, 163) };

                        break;

                    case SideMenuTheme.Success :

                        background            = new SolidColorBrush { Color = Color.FromArgb(205, 55, 109, 55) };
                        buttonBackground      = new SolidColorBrush { Color = Color.FromArgb(50 , 65, 129, 65) };
                        buttonHoverBackground = new SolidColorBrush { Color = Color.FromArgb(50 , 87, 172, 87) };

                        break;

                    case SideMenuTheme.Warning :

                        background            = new SolidColorBrush { Color = Color.FromArgb(205, 150, 108, 49) };
                        buttonBackground      = new SolidColorBrush { Color = Color.FromArgb(50 , 179, 129, 58) };
                        buttonHoverBackground = new SolidColorBrush { Color = Color.FromArgb(50 , 216, 155, 70) };

                        break;

                    case SideMenuTheme.Danger :

                        background            = new SolidColorBrush { Color = Color.FromArgb(205, 135, 52, 49) };
                        buttonBackground      = new SolidColorBrush { Color = Color.FromArgb(50 , 179, 69, 65) };
                        buttonHoverBackground = new SolidColorBrush { Color = Color.FromArgb(50 , 238, 92, 86) };

                        break;

                    default :

                        throw new ArgumentOutOfRangeException(nameof(value), value, null);
                }

                ButtonBackground = buttonBackground;
                ButtonHover      = buttonHoverBackground;

                if(Menu != null)
                {
                    Menu.Background = background;

                    StackPanel stackPanel = Menu.Content as StackPanel;

                    foreach(UIElement child in stackPanel.Children)
                    {
                        if(child is MenuButton menuButton)
                        {
                            SetMenuButtonBackground(menuButton, buttonBackground, buttonHoverBackground);
                        }
                    }
                }
            }
        }

        #endregion
        #region 상태 - State

        /// <summary>
        /// 상태
        /// </summary>
        public MenuState State
        {
            get
            {
                return (MenuState)GetValue(StateProperty);
            }
            set
            {
                SetValue(StateProperty, value);

                if(value == MenuState.Visible)
                {
                    Show();
                }
                else
                {
                    Hide();
                }
            }
        }

        #endregion
        #region 메뉴 너비 - MenuWidth

        /// <summary>
        /// 메뉴 너비
        /// </summary>
        public double MenuWidth
        {
            get
            {
                return (double)GetValue(MenuWidthProperty);
            }
            set
            {
                SetValue(MenuWidthProperty, value);
            }
        }

        #endregion
        #region 메뉴 - Menu

        /// <summary>
        /// 메뉴
        /// </summary>
        public ScrollViewer Menu
        {
            get
            {
                return (ScrollViewer)GetValue(MenuProperty);
            }
            set
            {
                SetValue(MenuProperty, value);
            }
        }

        #endregion
        #region 그림자 배경 - ShadowBackground

        /// <summary>
        /// 그림자 배경
        /// </summary>
        public Brush ShadowBackground
        {
            get
            {
                return (Brush)GetValue(ShadowBackgroundProperty);
            }
            set
            {
                SetValue(ShadowBackgroundProperty, value);

                Resources["ShadowSolidColorBrushKey"] = value ?? new SolidColorBrush { Color = Colors.Black, Opacity = .2 };
            }
        }

        #endregion
        #region 버튼 배경 - ButtonBackground

        /// <summary>
        /// 버튼 배경
        /// </summary>
        public Brush ButtonBackground
        {
            get
            {
                return (Brush)GetValue(ButtonBackgroundProperty);
            }
            set
            {
                SetValue(ButtonBackgroundProperty, value);

                Resources["ButtonBackgroundSolidColorBrushKey"] = value;
            }
        }

        #endregion
        #region 버튼 HOVER - ButtonHover

        /// <summary>
        /// 버튼 HOVER
        /// </summary>
        public Brush ButtonHover
        {
            get
            {
                return (Brush)GetValue(ButtonHoverProperty);
            }
            set
            {
                SetValue(ButtonHoverProperty, value);

                Resources["ButtonHoverSolidColorBrushKey"] = value;
            }
        }

        #endregion

        #region 종료 타입 - ClosingType

        /// <summary>
        /// 종료 타입
        /// </summary>
        public ClosingType ClosingType { get; set; }

        #endregion
        #region 표시 여부 - IsShown

        /// <summary>
        /// 표시 여부
        /// </summary>
        public bool IsShown
        {
            get
            {
                return this.isShown;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SideMenu()

        /// <summary>
        /// 생성자
        /// </summary>
        public SideMenu()
        {
            InitializeComponent();

            Theme = SideMenuTheme.Default;

            ClosingType = ClosingType.Auto;

            this.shadowBorder.MouseDown += shadowBorder_MouseDown;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 템플리트 적용시 처리하기 - OnApplyTemplate()

        /// <summary>
        /// 템플리트 적용시 처리하기
        /// </summary>
        public override void OnApplyTemplate()
        {
            Panel.SetZIndex(this, int.MaxValue);

            RenderTransform = new TranslateTransform(-MenuWidth, 0);

            (FindName("menuColumnDefinition") as ColumnDefinition).Width = new GridLength(MenuWidth);

            State            = State;
            Theme            = Theme;
            ShadowBackground = ShadowBackground;
            ButtonBackground = ButtonBackground;
            ButtonHover      = ButtonHover;
        }

        #endregion

        #region 보여주기 - Show()

        /// <summary>
        /// 보여주기
        /// </summary>
        public void Show()
        {
            DoubleAnimation animation = new DoubleAnimation
            {
                From     = -MenuWidth * 0.85,
                To       = 0,
                Duration = TimeSpan.FromMilliseconds(100)
            };

            RenderTransform.BeginAnimation(TranslateTransform.XProperty, animation);

            this.isShown = true;

            (FindName("shadowColumnDefinition") as ColumnDefinition).Width = new GridLength(10000);
        }

        #endregion
        #region 숨기기 - Hide()

        /// <summary>
        /// 숨기기
        /// </summary>
        public void Hide()
        {
            DoubleAnimation animation = new DoubleAnimation
            {
                To       = -MenuWidth,
                Duration = TimeSpan.FromMilliseconds(100)
            };

            RenderTransform.BeginAnimation(TranslateTransform.XProperty, animation);

            this.isShown = false;

            (FindName("shadowColumnDefinition") as ColumnDefinition).Width = new GridLength(0);
        }

        #endregion

        #region 토글하기 - Toggle()

        /// <summary>
        /// 토글하기
        /// </summary>
        public void Toggle()
        {
            if(this.isShown)
            {
                Hide();
            }
            else
            {
                Show();
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 그림자 테두리 마우스 DOWN 처리하기 - shadowBorder_MouseDown(sender, e)

        /// <summary>
        /// 그림자 테두리 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void shadowBorder_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if(ClosingType == ClosingType.Auto)
            {
                Hide();
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 메뉴 버튼 배경 설정하기 - SetMenuButtonBackground(parentButton, buttonBackground, buttonHoverBackground)

        /// <summary>
        /// 메뉴 버튼 배경 설정하기
        /// </summary>
        /// <param name="parentButton">부모 버튼</param>
        /// <param name="buttonBackground">버튼 배경</param>
        /// <param name="buttonHoverBackground">버튼 HOVER 배경</param>
        private void SetMenuButtonBackground(MenuButton parentButton, Brush buttonBackground, SolidColorBrush buttonHoverBackground)
        {
            parentButton.HoverBackground = buttonHoverBackground;

            parentButton.SetBackground(buttonBackground);

            foreach(MenuButton childButton in parentButton.Children)
            {
                SetMenuButtonBackground(childButton, buttonBackground, buttonHoverBackground);
            }
        }

        #endregion
    }
}

 

[TestProject 프로젝트]

▶ MainWindow.xaml

<Window x:Class="TestProject.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:library="clr-namespace:TestLibrary;assembly=TestLibrary"
    mc:Ignorable="d"
    WindowStartupLocation="CenterScreen"
    Width="800"
    Height="600"
    Title="Material 메뉴 사용하기"
    Foreground="WhiteSmoke"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Window.Resources>
        <Style x:Key="TransparentPanelStyleKey" TargetType="Border">
            <Setter Property="BorderBrush"     Value="#66444444" />
            <Setter Property="BorderThickness" Value="1"         />
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush StartPoint="0 0" EndPoint="1 1">
                        <GradientStop Color="#bb222222" Offset="0"   />
                        <GradientStop Color="#aa444444" Offset="0.6" />
                        <GradientStop Color="#99222222" Offset="1"   />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.Background>
            <ImageBrush
                Stretch="UniformToFill"
                ImageSource="IMAGE/mainBackground.jpg" />
        </Grid.Background>
        <library:SideMenu x:Name="customSideMenu"
            HorizontalAlignment="Left"
            MenuWidth="300"
            Theme="None"
            State="Hidden"
            ButtonBackground="Red"
            ButtonHover="Black"
            ShadowBackground="Black">
            <library:SideMenu.Menu>
                <ScrollViewer
                    VerticalScrollBarVisibility="Hidden"
                    Background="Blue">
                    <StackPanel Orientation="Vertical">
                        <Border Background="BlueViolet">
                            <Grid Margin="10">
                                <TextBox
                                    VerticalContentAlignment="Bottom"
                                    Height="100"
                                    BorderThickness="0"
                                    Background="Transparent"
                                    Foreground="WhiteSmoke"
                                    FontWeight="Bold"
                                    FontSize="18">
                                    커스텀 색상 예제 입니다.
                                </TextBox>
                            </Grid>
                        </Border>
                        <library:MenuButton
                            Image="IMAGE/bossLight.png"
                            Text="관리" />
                        <library:MenuButton
                            Image="IMAGE/boxLight.png"
                            Text="포장" />
                        <library:MenuButton
                            Image="IMAGE/bugLight.png"
                            Text="물류" />
                        <library:MenuButton
                            Image="IMAGE/organizationLight.png"
                            Text="조직도" />
                        <library:MenuButton
                            Image="IMAGE/saveLight.png"
                            Text="변경 사항 저장" />
                        <library:MenuButton
                            Image="IMAGE/HelpLight.png"
                            Text="도움말" />
                        <library:MenuButton
                            Image="IMAGE/closeLight.png"
                            Text="메뉴 닫기" />
                    </StackPanel>
                </ScrollViewer>
            </library:SideMenu.Menu>
        </library:SideMenu>
        <library:SideMenu x:Name="defaultSideMenu"
            HorizontalAlignment="Left"
            MenuWidth="300"
            Theme="Default"
            State="Hidden">
            <library:SideMenu.Menu>
                <ScrollViewer VerticalScrollBarVisibility="Hidden">
                    <StackPanel Orientation="Vertical">
                        <Border>
                            <Border.Background>
                                <ImageBrush
                                    Stretch="None"
                                    AlignmentX="Right"
                                    ImageSource="IMAGE/sideMenuBackground.jpg" />
                            </Border.Background>
                            <Grid Margin="10">
                                <TextBox
                                    VerticalContentAlignment="Bottom"
                                    Height="50"
                                    BorderThickness="0"
                                    Background="Transparent"
                                    Foreground="WhiteSmoke"
                                    FontFamily="Calibri"
                                    FontWeight="Bold"
                                    FontSize="18">
                                    환영합니다!
                                </TextBox>
                            </Grid>
                        </Border>
                        <library:MenuButton Image="IMAGE/bossLight.png" Text="자료">
                            <library:MenuButton.Children>
                                <library:MenuButton Text="일반" Image="IMAGE/helpLight.png">
                                    <library:MenuButton.Children>
                                        <library:MenuButton Text="도서"     Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                        <library:MenuButton Text="백과사전" Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                        <library:MenuButton Text="한자사전" Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                        <library:MenuButton Text="역사"     Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                    </library:MenuButton.Children>
                                </library:MenuButton>
                                <library:MenuButton Text="경제" Image="IMAGE/helpLight.png">
                                    <library:MenuButton.Children>
                                        <library:MenuButton Text="자료실" Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                    </library:MenuButton.Children>
                                </library:MenuButton>
                                <library:MenuButton Text="전쟁" Image="IMAGE/helpLight.png">
                                    <library:MenuButton.Children>
                                        <library:MenuButton Text="전쟁사"    Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                        <library:MenuButton Text="무기 체계" Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                        <library:MenuButton Text="세계 정세" Image="IMAGE/boxLight.png" MouseDown="menuButton_Click" />
                                    </library:MenuButton.Children>
                                </library:MenuButton>
                            </library:MenuButton.Children>
                        </library:MenuButton>
                        <library:MenuButton
                            Image="IMAGE/closeLight.png"
                            Text="메뉴 닫기"
                            MouseDown="closeMenuButton_Click" />
                    </StackPanel>
                </ScrollViewer>
            </library:SideMenu.Menu>
        </library:SideMenu>
        <Border
            Style="{StaticResource TransparentPanelStyleKey}"
            HorizontalAlignment="Right">
            <StackPanel
                Margin="10"
                Width="200"
                Orientation="Vertical">
                <Button Name="showDefaultSideMenuButton"
                    Margin="10">
                    <Button.Template>
                        <ControlTemplate>
                            <Border
                                HorizontalAlignment="Right"
                                Width="50"
                                Height="50"
                                CornerRadius="3"
                                BorderBrush="DarkGray"
                                BorderThickness="1"
                                Background="WhiteSmoke">
                                <Image
                                    Margin="12"
                                    Source="IMAGE/dark64.png" />
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
                <Button Name="showCustomSideMenuButton"
                    Margin="10">
                    <Button.Template>
                        <ControlTemplate>
                            <Border
                                HorizontalAlignment="Right"
                                Width="50"
                                Height="50"
                                CornerRadius="3"
                                BorderBrush="DarkGray"
                                BorderThickness="1"
                                Background="#00b500">
                                <Image
                                    Margin="12"
                                    Source="IMAGE/dark64.png" />
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
                <StackPanel
                    Orientation="Vertical"
                    Background="Transparent">
                    <Button Name="defaultSideMenuThemeButton"
                        Margin="0 10 0 0"
                        Width="100"
                        Height="30"
                        Background="#303030"
                        Foreground="WhiteSmoke">
                        Default
                    </Button>
                    <Button Name="primarySideMenuThemeButton"
                        Margin="0 10 0 0"
                        Width="100"
                        Height="30"
                        Background="#337ab6"
                        Foreground="WhiteSmoke">
                        Primary
                    </Button>
                    <Button Name="successSideMenuThemeButton"
                        Margin="0 10 0 0"
                        Width="100"
                        Height="30"
                        Background="#5cb75c"
                        Foreground="WhiteSmoke">
                        Success
                    </Button>
                    <Button Name="warningSideMenuThemeButton"
                        Margin="0 10 0 0"
                        Width="100"
                        Height="30"
                        Background="#efac4e"
                        Foreground="WhiteSmoke">
                        Warning
                    </Button>
                    <Button Name="dangerSideMenuThemeButton"
                        Margin="0 10 0 0"
                        Width="100"
                        Height="30"
                        Background="#D8534F"
                        Foreground="WhiteSmoke">
                        Danger
                    </Button>
                </StackPanel>
                <StackPanel
                    Margin="0 20 0 0"
                    Background="Transparent">
                    <TextBlock
                        Foreground="White"
                        TextWrapping="Wrap"
                        FontWeight="Bold">
                        아래 버튼을 클릭하면 메뉴에서 닫기 버튼을 눌러야 할 때 자동/수동 닫기 유형이 변경됩니다.
                    </TextBlock>
                    <Button Name="toggleClosingTypeButton"
                        Margin="0 10 0 0"
                        Width="150"
                        Height="30">
                        종료 타입 토글
                    </Button>
                </StackPanel>
            </StackPanel>
        </Border>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System.Windows;

using TestLibrary;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            this.showDefaultSideMenuButton.Click  += showDefaultSideMenuButton_Click;
            this.showCustomSideMenuButton.Click   += showCustomSideMenuButton_Click;
            this.defaultSideMenuThemeButton.Click += defaultSideMenuThemeButton_Click;
            this.primarySideMenuThemeButton.Click += primarySideMenuThemeButton_Click;
            this.successSideMenuThemeButton.Click += successSideMenuThemeButton_Click;
            this.warningSideMenuThemeButton.Click += warningSideMenuThemeButton_Click;
            this.dangerSideMenuThemeButton.Click  += dangerSideMenuThemeButton_Click;
            this.toggleClosingTypeButton.Click    += toggleClosingTypeButton_Click;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 디폴트 사이드 메뉴 표시 버튼 클릭시 처리하기 - showDefaultSideMenuButton_Click(sender, e)

        /// <summary>
        /// 디폴트 사이드 메뉴 표시 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void showDefaultSideMenuButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Toggle();
        }

        #endregion
        #region 커스텀 사이드 메뉴 표시 버튼 클릭시 처리하기 - showCustomSideMenuButton_Click(sender, e)

        /// <summary>
        /// 커스텀 사이드 메뉴 표시 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void showCustomSideMenuButton_Click(object sender, RoutedEventArgs e)
        {
            this.customSideMenu.Toggle();
        }

        #endregion

        #region DEFAULT 사이드 메뉴 테마 버튼 클릭시 처리하기 - defaultSideMenuThemeButton_Click(sender, e)

        /// <summary>
        /// DEFAULT 사이드 메뉴 테마 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void defaultSideMenuThemeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Theme = SideMenuTheme.Default;
        }

        #endregion
        #region PRIMARY 사이드 메뉴 테마 버튼 클릭시 처리하기 - primarySideMenuThemeButton_Click(sender, e)

        /// <summary>
        /// PRIMARY 사이드 메뉴 테마 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void primarySideMenuThemeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Theme = SideMenuTheme.Primary;
        }

        #endregion
        #region SUCCESS 사이드 메뉴 테마 버튼 클릭시 처리하기 - successSideMenuThemeButton_Click(sender, e)

        /// <summary>
        /// SUCCESS 사이드 메뉴 테마 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void successSideMenuThemeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Theme = SideMenuTheme.Success;
        }

        #endregion
        #region WARNING 사이드 메뉴 테마 버튼 클릭시 처리하기 - warningSideMenuThemeButton_Click(sender, e)

        /// <summary>
        /// WARNING 사이드 메뉴 테마 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void warningSideMenuThemeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Theme = SideMenuTheme.Warning;
        }

        #endregion
        #region DANGER 사이드 메뉴 테마 버튼 클릭시 처리하기 - dangerSideMenuThemeButton_Click(sender, e)

        /// <summary>
        /// DANGER 사이드 메뉴 테마 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void dangerSideMenuThemeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Theme = SideMenuTheme.Danger;
        }

        #endregion

        #region 종료 타입 토글 버튼 클릭시 처리하기 - toggleClosingTypeButton_Click(sender, e)

        /// <summary>
        /// 종료 타입 토글 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void toggleClosingTypeButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.ClosingType = this.defaultSideMenu.ClosingType == ClosingType.Auto ? ClosingType.Manual : ClosingType.Auto;
        }

        #endregion

        #region 메뉴 버튼 클릭시 처리하기 - menuButton_Click(sender, e)

        /// <summary>
        /// 메뉴 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void menuButton_Click(object sender, RoutedEventArgs e)
        {
            MenuButton menuButton = sender as MenuButton;

            MessageBox.Show(this, $"{menuButton.Text} 메뉴를 클릭했습니다.");
        }

        #endregion
        #region 메뉴 닫기 버튼 클릭시 처리하기 - closeMenuButton_Click(sender, e)

        /// <summary>
        /// 메뉴 닫기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void closeMenuButton_Click(object sender, RoutedEventArgs e)
        {
            this.defaultSideMenu.Hide();
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요