WPF 可选择年月及显示格式的DatePicker控件

转载于 http://www.cnblogs.com/ZXdeveloper/archive/2015/07/21/4665772.html

代码部分:

public class XDatePicker : DatePicker
    {
        static XDatePicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(XDatePicker), new FrameworkPropertyMetadata(typeof(XDatePicker)));
        }


        #region 属性
        static bool IsMonthYear, IsYear;


        #region IsMonthYear
        public static bool GetIsMonthYear(DependencyObject dobj)
        {
            return (bool)dobj.GetValue(IsMonthYearProperty);
        }


        public static void SetIsMonthYear(DependencyObject dobj, bool value)
        {
            dobj.SetValue(IsMonthYearProperty, value);
        }


        public static readonly DependencyProperty IsMonthYearProperty = DependencyProperty.RegisterAttached("IsMonthYear", typeof(bool), typeof(XDatePicker), new PropertyMetadata(OnIsMonthYearChanged));


        private static void OnIsMonthYearChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
        {
            var datePicker = (XDatePicker)dobj;
            IsMonthYear = GetIsMonthYear(dobj);
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action<XDatePicker, DependencyPropertyChangedEventArgs>(SetCalendarEventHandlers), datePicker, e);
        }
        #endregion


        #region IsYear
        public static bool GetIsYear(DependencyObject dobj)
        {
            return (bool)dobj.GetValue(IsYearProperty);
        }


        public static void SetIsYear(DependencyObject dobj, bool value)
        {
            dobj.SetValue(IsYearProperty, value);
        }


        public static readonly DependencyProperty IsYearProperty = DependencyProperty.RegisterAttached("IsYear", typeof(bool), typeof(XDatePicker), new PropertyMetadata(OnIsYearChanged));


        private static void OnIsYearChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
        {
            var datePicker = (XDatePicker)dobj;
            IsYear = GetIsYear(dobj);
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action<XDatePicker, DependencyPropertyChangedEventArgs>(SetCalendarEventHandlers), datePicker, e);
        }
        #endregion


        #region DateFormat
        public static string GetDateFormat(DependencyObject dobj)
        {
            return (string)dobj.GetValue(DateFormatProperty);
        }


        public static void SetDateFormat(DependencyObject dobj, string value)
        {
            dobj.SetValue(DateFormatProperty, value);
        }


        public static readonly DependencyProperty DateFormatProperty = DependencyProperty.RegisterAttached("DateFormat", typeof(string), typeof(XDatePicker), new PropertyMetadata(OnDateFormatChanged));


        private static void OnDateFormatChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
        {
            var datePicker = (XDatePicker)dobj;
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action<XDatePicker>(ApplyDateFormat), datePicker);
        }


        private static void ApplyDateFormat(XDatePicker datePicker)
        {
            var binding = new Binding("SelectedDate")
            {
                RelativeSource = new RelativeSource { AncestorType = typeof(XDatePicker) },
                Converter = new DatePickerDateTimeConverter(),
                ConverterParameter = new Tuple<XDatePicker, string>(datePicker, GetDateFormat(datePicker))
            };
            var textBox = GetTemplateTextBox(datePicker);
            textBox.SetBinding(TextBox.TextProperty, binding);


            textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;
            textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;


            datePicker.CalendarOpened -= DatePickerOnCalendarOpened;
            datePicker.CalendarOpened += DatePickerOnCalendarOpened;
        }


        private static TextBox GetTemplateTextBox(Control control)
        {
            control.ApplyTemplate();
            return (TextBox)control.Template.FindName("PART_TextBox", control);
        }


        private static void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key != Key.Return)
                return;


            e.Handled = true;


            var textBox = (TextBox)sender;
            var datePicker = (XDatePicker)textBox.TemplatedParent;
            var dateStr = textBox.Text;
            var formatStr = GetDateFormat(datePicker);
            datePicker.SelectedDate = DatePickerDateTimeConverter.StringToDateTime(datePicker, formatStr, dateStr);
        }


        private class DatePickerDateTimeConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                var formatStr = ((Tuple<XDatePicker, string>)parameter).Item2;
                var selectedDate = (DateTime?)value;
                return DateTimeToString(formatStr, selectedDate);
            }


            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                var tupleParam = ((Tuple<XDatePicker, string>)parameter);
                var dateStr = (string)value;
                return StringToDateTime(tupleParam.Item1, tupleParam.Item2, dateStr);
            }


            public static string DateTimeToString(string formatStr, DateTime? selectedDate)
            {
                return selectedDate.HasValue ? selectedDate.Value.ToString(formatStr) : null;
            }


            public static DateTime? StringToDateTime(XDatePicker datePicker, string formatStr, string dateStr)
            {
                DateTime date;
                var canParse = DateTime.TryParseExact(dateStr, formatStr, CultureInfo.CurrentCulture, DateTimeStyles.None, out date);


                if (!canParse)
                    canParse = DateTime.TryParse(dateStr, CultureInfo.CurrentCulture, DateTimeStyles.None, out date);


                return canParse ? date : datePicker.SelectedDate;
            }
        }
        #endregion


        private static void SetCalendarEventHandlers(XDatePicker datePicker, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == e.OldValue)
                return;


            if ((bool)e.NewValue)
            {
                datePicker.CalendarOpened += DatePickerOnCalendarOpened;
                datePicker.CalendarClosed += DatePickerOnCalendarClosed;
            }
            else
            {
                datePicker.CalendarOpened -= DatePickerOnCalendarOpened;
                datePicker.CalendarClosed -= DatePickerOnCalendarClosed;
            }
        }


        private static void DatePickerOnCalendarOpened(object sender, RoutedEventArgs routedEventArgs)
        {
            var calendar = GetDatePickerCalendar(sender);
            if (IsMonthYear) calendar.DisplayMode = CalendarMode.Year;
            if (IsYear) calendar.DisplayMode = CalendarMode.Decade;


            var datePicker = (XDatePicker)sender;
            var textBox = GetTemplateTextBox(datePicker);
            var formatStr = GetDateFormat(datePicker);
            textBox.Text = DatePickerDateTimeConverter.DateTimeToString(formatStr, datePicker.SelectedDate);


            calendar.DisplayModeChanged += CalendarOnDisplayModeChanged;
        }


        private static void DatePickerOnCalendarClosed(object sender, RoutedEventArgs routedEventArgs)
        {
            var datePicker = (XDatePicker)sender;
            var calendar = GetDatePickerCalendar(sender);
            datePicker.SelectedDate = calendar.SelectedDate;


            calendar.DisplayModeChanged -= CalendarOnDisplayModeChanged;
        }


        private static void CalendarOnDisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
        {
            var calendar = (System.Windows.Controls.Calendar)sender;
            if (IsMonthYear && calendar.DisplayMode != CalendarMode.Month)
                return;
            if (IsYear && calendar.DisplayMode != CalendarMode.Year)
                return;


            calendar.SelectedDate = GetSelectedCalendarDate(calendar.DisplayDate);


            var datePicker = GetCalendarsDatePicker(calendar);
            datePicker.IsDropDownOpen = false;
        }


        private static System.Windows.Controls.Calendar GetDatePickerCalendar(object sender)
        {
            var datePicker = (XDatePicker)sender;
            var popup = (Popup)datePicker.Template.FindName("PART_Popup", datePicker);
            return ((System.Windows.Controls.Calendar)popup.Child);
        }


        private static XDatePicker GetCalendarsDatePicker(FrameworkElement child)
        {
            var parent = (FrameworkElement)child.Parent;
            if (parent.Name == "PART_Root") return (XDatePicker)parent.TemplatedParent;
            return GetCalendarsDatePicker(parent);
        }


        private static DateTime? GetSelectedCalendarDate(DateTime? selectedDate)
        {
            if (!selectedDate.HasValue) return null;
            return new DateTime(selectedDate.Value.Year, selectedDate.Value.Month, 1);
        }
        #endregion
    }

样式部分:

    <Style TargetType="{x:Type local:XDatePicker}">
        <Setter Property="Height" Value="28"/>
        <Setter Property="Width" Value="100"/>
        <Setter Property="Margin" Value="5,0"/>
        <Setter Property="BorderBrush" Value="gray"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="SelectedDateFormat" Value="Short"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:XDatePicker}">
                    <Border CornerRadius="3" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_Popup"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <DockPanel x:Name="PART_Root">
                            <DockPanel.Resources>
                                <ControlTemplate x:Key="DropDownButtonTemplate" TargetType="{x:Type Button}">
                                    <Border Height="18" Width="18">
                                        <Path x:Name="calendar" Fill="#FF368DC0" RenderTransformOrigin="0.5,0.5" Stretch="Fill"
<span style="white-space:pre">                                          </span>  Data="F1 M 18.0025,57.0081L 18.0025,23.0032L 23.0032,23.0032L 23.0032,20.0028C 23.0033,18.898 23.8988,18.0025 25.0035,18.0025L 29.004,18.0025C 30.1087,18.0025 31.0042,18.898 31.0043,20.0026L 31.0043,23.0032L 45.0063,23.0032L 45.0062,20.0026C 45.0062,18.8978 45.9018,18.0023 47.0065,18.0023L 51.0071,18.0023C 52.1118,18.0023 53.0074,18.8978 53.0074,20.0026L 53.0074,23.0032L 58.0081,23.0032L 58.0081,57.0081L 18.0025,57.0081 Z M 20,54L 56,54L 56,28L 20,28L 20,54 Z M 23,44L 31,44L 31,52L 23,52L 23,44 Z M 34,44L 42,44L 42,52L 34,52L 34,44 Z M 45,44L 53,44L 53,52L 45,52L 45,44 Z M 23,32L 31,32L 31,40L 23,40L 23,32 Z M 34,32L 42,32L 42,40L 34,40L 34,32 Z M 45,32L 53,32L 53,40L 45,40L 45,32 Z M 48.5067,20.0029C 47.6782,20.0029 47.0065,20.6745 47.0065,21.5031L 47.0065,24.5035C 47.0065,25.332 47.6782,26.0037 48.5067,26.0037L 49.5068,26.0037C 50.3354,26.0037 51.0071,25.332 51.0071,24.5035L 51.0071,21.5031C 51.0071,20.6745 50.3354,20.0029 49.5068,20.0029L 48.5067,20.0029 Z M 26.5037,20.0028C 25.6751,20.0028 25.0035,20.6745 25.0035,21.503L 25.0035,24.5035C 25.0035,25.332 25.6751,26.0037 26.5037,26.0037L 27.5038,26.0037C 28.3324,26.0037 29.004,25.332 29.004,24.5035L 29.004,21.503C 29.004,20.6745 28.3324,20.0028 27.5038,20.0028L 26.5037,20.0028 Z "/>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="true">
                                            <Setter TargetName="calendar" Property="Fill" Value="#1BBBFA"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </DockPanel.Resources>
                            <Button x:Name="PART_Button" DockPanel.Dock="Right" Foreground="{TemplateBinding Foreground}" Focusable="False" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,0" Template="{StaticResource DropDownButtonTemplate}" Width="20"/>
                            <Grid Margin="3,0,0,0">
                                <DatePickerTextBox x:Name="PART_TextBox" Focusable="{TemplateBinding Focusable}" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center"/>
                                <Popup x:Name="PART_Popup" IsHitTestVisible="False" Opacity="0" AllowsTransparency="True" Placement="Bottom" PlacementTarget="{Binding ElementName=PART_TextBox}" StaysOpen="False"/>
                            </Grid>
                        </DockPanel>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding Source={x:Static SystemParameters.HighContrast}}" Value="false">
                            <Setter Property="Foreground" TargetName="PART_TextBox" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值