■ DatePicker 컨트롤 만들기

----------------------------------------------------------------------------------------------------

DatePicker.xaml

 

 

<UserControl

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:g="clr-namespace:System.Globalization;assembly=mscorlib"

    x:Class="TestProject.DatePicker">

    <UserControl.Resources>

        <Style TargetType="{x:Type RepeatButton}">

            <Setter Property="Width"     Value="{Binding RelativeSource={RelativeSource self}, Path=ActualHeight}" />

            <Setter Property="Focusable" Value="False" />

            <Style.Triggers>

                <DataTrigger Binding="{Binding ElementName=nullCheckBox, Path=IsChecked}" Value="True">

                    <Setter Property="IsEnabled" Value="False" />

                </DataTrigger>

            </Style.Triggers>

        </Style>

        <Style TargetType="{x:Type StatusBarItem}">

            <Setter Property="Margin"              Value="1"      />

            <Setter Property="HorizontalAlignment" Value="Center" />

            <Setter Property="VerticalAlignment"   Value="Center" />

        </Style>

        <Style TargetType="{x:Type ListBoxItem}">

            <Setter Property="BorderThickness"            Value="1"           />

            <Setter Property="BorderBrush"                Value="Transparent" />

            <Setter Property="Margin"                     Value="1"           />

            <Setter Property="HorizontalContentAlignment" Value="Center"      />

            <Style.Triggers>

                <MultiTrigger>

                    <MultiTrigger.Conditions>

                        <Condition Property="IsSelected"                 Value="True" />

                        <Condition Property="Selector.IsSelectionActive" Value="False" />

                    </MultiTrigger.Conditions>

                    <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />

                </MultiTrigger>

                <DataTrigger Binding="{Binding ElementName=nullCheckBox, Path=IsChecked}" Value="True">

                    <Setter Property="IsEnabled" Value="False" />

                </DataTrigger>

            </Style.Triggers>

        </Style>

    </UserControl.Resources>

    <Border

        BorderThickness="1"

        BorderBrush="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">

        <Grid>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <Grid

                Background="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}"

                TextBlock.Foreground="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}">

                <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="Auto" />

                    <ColumnDefinition Width="*"    />

                    <ColumnDefinition Width="Auto" />

                </Grid.ColumnDefinitions>

                <RepeatButton

                    Grid.Column="0"

                    FontWeight="Bold"

                    Content="&lt;"

                    Click="previousRepeatButton_Click" />

                <TextBlock

                    Name="monthYearTextBlock"

                    Grid.Column="1"

                    HorizontalAlignment="Center"

                    VerticalAlignment="Center"

                    Margin="3" />

                <RepeatButton

                    Grid.Column="2"

                    FontWeight="Bold"

                    Content="&gt;"

                    Click="nextRepeatButton_Click" />

            </Grid>

            <StatusBar

                Grid.Row="1"

                ItemsSource="{Binding Source={x:Static g:DateTimeFormatInfo.CurrentInfo}, Path=AbbreviatedDayNames}">

                <StatusBar.ItemsPanel>

                    <ItemsPanelTemplate>

                        <UniformGrid Rows="1" />

                    </ItemsPanelTemplate>

                </StatusBar.ItemsPanel>

            </StatusBar>

            <Border

                Grid.Row="2"

                BorderThickness="0 1 0 1"

                BorderBrush="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">

                <ListBox

                    Name="monthListBox"

                    SelectionChanged="monthListBox_SelectionChanged">

                    <ListBox.ItemsPanel>

                        <ItemsPanelTemplate>

                            <UniformGrid

                                Name="monthUniformGrid"

                                Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}"

                                Columns="7"

                                Rows="6"

                                IsItemsHost="True" />

                        </ItemsPanelTemplate>

                    </ListBox.ItemsPanel>

                    <ListBoxItem>더미 항목</ListBoxItem>

                </ListBox>

            </Border>

            <CheckBox

                Name="nullCheckBox"

                Grid.Row="3"

                Margin="3"

                HorizontalAlignment="Center"

                VerticalAlignment="Center"

                Checked="nullCheckBox_Checked"

                Unchecked="nullCheckBox_Unchecked">

                미적용

            </CheckBox>

        </Grid>

    </Border>

</UserControl>

 

 

DatePicker.xaml.cs

 

 

using System;

using System.Globalization;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Controls.Primitives;

using System.Windows.Input;

using System.Windows.Media;

 

namespace TestProject

{

    /// <summary>

    /// 일자 선택기

    /// </summary>

    public partial class DatePicker

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 일자 변경시 - DateChanged

 

        /// <summary>

        /// 일자 변경시

        /// </summary>

        public event RoutedPropertyChangedEventHandler<DateTime?> DateChanged

        {

            add

            {

                AddHandler(DateChangedEvent, value);

            }

            remove

            {

                RemoveHandler(DateChangedEvent, value);

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Static

        //////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 일자 속성

        /// </summary>

        public static readonly DependencyProperty DateProperty = DependencyProperty.Register

        (

            "Date",

            typeof(DateTime?),

            typeof(DatePicker),

            new PropertyMetadata

            (

                new DateTime(),

                DatePropertyChangedCallback

            )

        );

 

        /// <summary>

        /// 일자 변경 이벤트

        /// </summary>

        public static readonly RoutedEvent DateChangedEvent = EventManager.RegisterRoutedEvent

        (

            "DateChanged",

            RoutingStrategy.Bubble,

            typeof(RoutedPropertyChangedEventHandler<DateTime?>),

            typeof(DatePicker)

        );

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Instance

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

 

        #region Field

 

        /// <summary>

        /// 월 유니폼 그리드

        /// </summary>

        private UniformGrid monthUniformGrid;

 

        /// <summary>

        /// 저장 일시

        /// </summary>

        private DateTime dateTimeSaved = DateTime.Now.Date;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 일자 - Date

 

        /// <summary>

        /// 일자

        /// </summary>

        public DateTime? Date

        {

            set

            {

                SetValue(DateProperty, value);

            }

            get

            {

                return (DateTime?)GetValue(DateProperty);

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - DatePicker()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public DatePicker()

        {

            InitializeComponent();

 

            Date = dateTimeSaved;

 

            Loaded += userControl_Loaded;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Static

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

        ////////////////////////////////////////////////////////////////////// Event

 

        #region 일자 속성 변경 콜백 처리하기 - DatePropertyChangedCallback(dependencyObject, e)

 

        /// <summary>

        /// 일자 속성 변경 콜백 처리하기

        /// </summary>

        /// <param name="dependencyObject">의존 객체</param>

        /// <param name="e">이벤트 인자</param>

        private static void DatePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)

        {

            (dependencyObject as DatePicker).OnDateChanged((DateTime?)e.OldValue, (DateTime?)e.NewValue);

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Instance

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

 

        #region 미리보기 키 DOWN 처리하기 - OnPreviewKeyDown(e)

 

        /// <summary>

        /// 미리보기 키 DOWN 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnPreviewKeyDown(KeyEventArgs e)

        {

            base.OnKeyDown(e);

 

            if(e.Key == Key.PageDown)

            {

                FlipPage(true);

 

                e.Handled = true;

            }

            else if(e.Key == Key.PageUp)

            {

                FlipPage(false);

 

                e.Handled = false;

            }

        }

 

        #endregion

 

        #region 일자 변경시 처리하기 - OnDateChanged(beforeDateTime, afterDateTime)

 

        /// <summary>

        /// 일자 변경시 처리하기

        /// </summary>

        /// <param name="beforeDateTime">변경전 일시</param>

        /// <param name="afterDateTime">변경후 일시</param>

        protected virtual void OnDateChanged(DateTime? beforeDateTime, DateTime? afterDateTime)

        {

            this.nullCheckBox.IsChecked = (afterDateTime == null);

 

            if(afterDateTime != null)

            {

                DateTime newDateTime = (DateTime)afterDateTime;

 

                this.monthYearTextBlock.Text = newDateTime.ToString(DateTimeFormatInfo.CurrentInfo.YearMonthPattern);

 

                if(this.monthUniformGrid != null)

                {

                    this.monthUniformGrid.FirstColumn = (int)(new DateTime(newDateTime.Year, newDateTime.Month, 1).DayOfWeek);

                }

 

                int dayCountInMonth = DateTime.DaysInMonth(newDateTime.Year, newDateTime.Month);

 

                if(dayCountInMonth != this.monthListBox.Items.Count)

                {

                    this.monthListBox.BeginInit();

 

                    this.monthListBox.Items.Clear();

 

                    for(int i = 0; i < dayCountInMonth;  i++)

                    {

                        this.monthListBox.Items.Add((i + 1).ToString());

                    }

 

                    this.monthListBox.EndInit();

                }

 

                this.monthListBox.SelectedIndex = newDateTime.Day - 1;

            }

 

            RoutedPropertyChangedEventArgs<DateTime?> e = new RoutedPropertyChangedEventArgs<DateTime?>

            (

                beforeDateTime,

                afterDateTime,

                DatePicker.DateChangedEvent

            );

 

            e.Source = this;

 

            RaiseEvent(e);

        }

 

        #endregion

 

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

        ////////////////////////////////////////////////////////////////////// Event

 

        #region 사용자 컨트롤 로드시 처리하기 - userControl_Loaded(sender, e)

 

        /// <summary>

        /// 사용자 컨트롤 로드시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void userControl_Loaded(object sender, RoutedEventArgs e)

        {

            this.monthUniformGrid = FindElement<UniformGrid>(this.monthListBox);

 

            if(Date != null)

            {

                DateTime dateTime = (DateTime)Date;

 

                this.monthUniformGrid.FirstColumn = (int)(new DateTime(dateTime.Year, dateTime.Month, 1).DayOfWeek);

            }

        }

 

        #endregion

 

        #region 이전 리피트 버튼 클릭시 처리하기 - previousRepeatButton_Click(sender, e)

 

        /// <summary>

        /// 이전 리피트 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void previousRepeatButton_Click(object sender, RoutedEventArgs e)

        {

            FlipPage(true);

        }

 

        #endregion

 

        #region 다음 리피트 버튼 클릭시 처리하기 - nextRepeatButton_Click(sender, e)

 

        /// <summary>

        /// 다음 리피트 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void nextRepeatButton_Click(object sender, RoutedEventArgs e)

        {

            FlipPage(false);

        }

 

        #endregion

 

        #region 월 리스트 박스 선택 변경시 처리하기 - monthListBox_SelectionChanged(sender, e)

 

        /// <summary>

        /// 월 리스트 박스 선택 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void monthListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)

        {

            if(Date == null)

            {

                return;

            }

 

            DateTime dateTime = (DateTime)Date;

 

            if(this.monthListBox.SelectedIndex != -1)

            {

                Date = new DateTime(dateTime.Year, dateTime.Month, int.Parse(this.monthListBox.SelectedItem as string));

            }

        }

 

        #endregion

 

        #region NULL 체크 박스 체크시 처리하기 - nullCheckBox_Checked(sender, e)

 

        /// <summary>

        /// NULL 체크 박스 체크시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void nullCheckBox_Checked(object sender, RoutedEventArgs e)

        {

            if(Date != null)

            {

                dateTimeSaved = (DateTime)Date;

 

                Date = null;

            }

        }

 

        #endregion

 

        #region NULL 체크 박스 체크 해제시 처리하기 - nullCheckBox_Unchecked(sender, e)

 

        /// <summary>

        /// NULL 체크 박스 체크 해제시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void nullCheckBox_Unchecked(object sender, RoutedEventArgs e)

        {

            Date = dateTimeSaved;

        }

 

        #endregion

 

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

 

        #region 엘리먼트 찾기 - FindElement<T>(sourceDependencyObject)

 

        /// <summary>

        /// 엘리먼트 찾기

        /// </summary>

        /// <typeparam name="TElement">엘리먼트 타입</typeparam>

        /// <param name="sourceDependencyObject">소스 의존 객체</param>

        /// <returns>엘리먼트</returns>

        private TElement FindElement<TElement>(DependencyObject sourceDependencyObject) where TElement : class

        {

            if(sourceDependencyObject is TElement)

            {

                return sourceDependencyObject as TElement;

            }

 

            for(int i = 0; i < VisualTreeHelper.GetChildrenCount(sourceDependencyObject); i++)

            {

                Visual visual = FindElement<TElement>(VisualTreeHelper.GetChild(sourceDependencyObject, i)) as Visual;

 

                if(visual != null)

                {

                    return visual as TElement;

                }

            }

 

            return null;

        }

 

        #endregion

 

        #region 페이지 넘기기 - FlipPage(isPrevious)

 

        /// <summary>

        /// 페이지 넘기기

        /// </summary>

        /// <param name="isPrevious">이전 여부</param>

        private void FlipPage(bool isPrevious)

        {

            if(Date == null)

            {

                return;

            }

 

            DateTime dateTime = (DateTime)Date;

 

            int pageCount = isPrevious ? -1 : 1;

 

            if(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))

            {

                pageCount *= 12;

            }

 

            if(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))

            {

                pageCount = Math.Max(-1200, Math.Min(1200, 120 * pageCount));

            }

 

            int year  = dateTime.Year  + pageCount / 12;

            int month = dateTime.Month + pageCount % 12;

 

            while(month < 1)

            {

                month += 12;

                year  -= 1;

            }

 

            while(month > 12)

            {

                month -= 12;

                year  += 1;

            }

 

            if(year < DateTime.MinValue.Year)

            {

                Date = DateTime.MinValue.Date;

            }

            else if(year > DateTime.MaxValue.Year)

            {

                Date = DateTime.MaxValue.Date;

            }

            else

            {

                Date = new DateTime(year, month, Math.Min(dateTime.Day, DateTime.DaysInMonth(year, month)));

            }

        }

 

        #endregion

    }

}

 

 

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:src="clr-namespace:TestProject"

    SizeToContent="WidthAndHeight"

    ResizeMode="CanMinimize"

    Title="일자 선택기 테스트">

    <StackPanel>

        <src:DatePicker

            x:Name="datePicker"

            Margin="12"

            HorizontalAlignment="Center"

            DateChanged="datePicker_DateChanged" />

        <StackPanel

            Margin="12"

            Orientation="Horizontal">

            <TextBlock Text="바인딩 값 : " />

            <TextBlock Text="{Binding ElementName=datePicker, Path=Date}" />

        </StackPanel>

        <StackPanel

            Margin="12"

            Orientation="Horizontal">

            <TextBlock Text="이벤트 핸들러 값 : " />

            <TextBlock Name="dateTextBlock" />

        </StackPanel>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

 

 

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Media;

 

namespace TestProject

{

    /// <summary>

    /// 메인 윈도우

    /// </summary>

    public partial class MainWindow : Window

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// public

 

        #region 생성자 - MainWindow()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainWindow()

        {

            InitializeComponent();

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// private

 

        #region 일자 선택기 일자 변경시 처리하기 - datePicker_DateChanged(sender, e)

 

        /// <summary>

        /// 일자 선택기 일자 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void datePicker_DateChanged(object sender, RoutedPropertyChangedEventArgs<DateTime?> e)

        {

            if(e.NewValue != null)

            {

                DateTime dateTime = (DateTime)e.NewValue;

 

                this.dateTextBlock.Text = dateTime.ToString("d");

            }

            else

            {

                this.dateTextBlock.Text = string.Empty;

            }

        }

 

        #endregion

    }

}

 

----------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker
TAG

댓글을 달아 주세요