WPF DatePicker只顯示年和月 修改:可以只顯示年


最近的項目,查詢時只需要年和月,不需要日,因此需要對原有的DatePicker進行修改,查詢了網上的內容,最終從一篇帖子里看到了添加附加屬性的方法,地址是http://stackoverflow.com/questions/1798513/wpf-toolkit-datepicker-month-year-only

原文是用了兩個類,其中一個是為了讓DatePicker下的Calendar只顯示年月,不顯示日,另一個類是為了讓DatePicker格式化為yyyy-MM格式,但是從文章中可以看出,有人提出了,用格式化類進行格式化時,DatePicker控件會閃動一下,當然不影響使用。如果不想使用文章中提到的類進行格式化,也可以用我上一篇文章中的方法進行格式化,不會出現閃動的情況。

只顯示年月的類(2016-12-20修改,可以只顯示年)

public class DatePickerCalendar
    {

        public static bool GetIsYear(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsYearProperty);
        }

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

        // Using a DependencyProperty as the backing store for IsYear.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsYearProperty =
            DependencyProperty.RegisterAttached("IsYear", typeof(bool), typeof(DatePickerCalendar), new PropertyMetadata(false, new PropertyChangedCallback(OnIsMonthYearChanged)));



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

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

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

        private static void OnIsMonthYearChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
        {
            var datePicker = (DatePicker)dobj;

            Application.Current.Dispatcher
                .BeginInvoke(DispatcherPriority.Loaded,
                             new Action<DatePicker, DependencyPropertyChangedEventArgs>(SetCalendarEventHandlers),
                             datePicker, e);
        }

        private static void SetCalendarEventHandlers(DatePicker 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 (GetIsYear(sender as DatePicker))
            {
                calendar.DisplayMode = CalendarMode.Decade;
            }
            else
            {
                calendar.DisplayMode = CalendarMode.Year;
            }

            calendar.DisplayModeChanged += CalendarOnDisplayModeChanged;
        }

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

            calendar.DisplayModeChanged -= CalendarOnDisplayModeChanged;
        }

        private static void CalendarOnDisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
        {
            var calendar = (Calendar)sender;
            var datePicker = GetCalendarsDatePicker(calendar);

            bool mode = (GetIsYear(datePicker) && calendar.DisplayMode != CalendarMode.Year) || (GetIsMonthYear(datePicker) && calendar.DisplayMode != CalendarMode.Month);

            if (mode)
                return;

            calendar.SelectedDate = GetSelectedCalendarDate(calendar.DisplayDate);

            datePicker.IsDropDownOpen = false;
        }

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

        private static DatePicker GetCalendarsDatePicker(FrameworkElement child)
        {
            var parent = (FrameworkElement)child.Parent;
            if (parent.Name == "PART_Root")
                return (DatePicker)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);
        }
    }
DatePickerCalendar

調用方式

<DatePicker Width="200" Height="30" local:DatePickerCalendar.IsMonthYear="True"/>

展示效果

從圖上就能看到左右不一樣,左側的是添加附加屬性的,點擊以后直接顯示月,后側沒用的則顯示到日,雖然都格式化為了yyyy-MM,但是Calendar如果顯示到日的話,用戶體驗不好。

========================================================================================================================

2015年8月4日 記:

今天在項目中發現一個問題,就是采用我上一篇帖子中的三句話進行DatePicker時間的格式化顯示,但是由於用的是Thread,所以是在線程里進行格式化的,因此,影響到了同事做的其他模塊,因為大部分都是格式化為yyyy-MM-dd,但是由於我的一個頁面是格式化為了yyyy-MM,而其中的一個同事並沒有在他的構造函數里使用三句格式化他的DatePicker,從而導致他其他的時間計算出現問題,因此在保證原有功能不變的情況下,需要顯示yyyy-MM,就需要用到原帖子中的另一個類。

DatePickerDateFormat

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

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

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

        private static void OnDateFormatChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
        {
            var datePicker = (DatePicker)dobj;

            Application.Current.Dispatcher.BeginInvoke(
                DispatcherPriority.Loaded, new Action<DatePicker>(ApplyDateFormat), datePicker);
        }

        private static void ApplyDateFormat(DatePicker datePicker)
        {
            var binding = new Binding("SelectedDate")
            {
                RelativeSource = new RelativeSource { AncestorType = typeof(DatePicker) },
                Converter = new DatePickerDateTimeConverter(),
                ConverterParameter = new Tuple<DatePicker, 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;

            /* DatePicker subscribes to its TextBox's KeyDown event to set its SelectedDate if Key.Return was
             * pressed. When this happens its text will be the result of its internal date parsing until it
             * loses focus or another date is selected. A workaround is to stop the KeyDown event bubbling up
             * and handling setting the DatePicker.SelectedDate. */

            e.Handled = true;

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

        private static void DatePickerOnCalendarOpened(object sender, RoutedEventArgs e)
        {
            /* When DatePicker's TextBox is not focused and its Calendar is opened by clicking its calendar button
             * its text will be the result of its internal date parsing until its TextBox is focused and another
             * date is selected. A workaround is to set this string when it is opened. */

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

        private class DatePickerDateTimeConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                var formatStr = ((Tuple<DatePicker, 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<DatePicker, 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(DatePicker 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;
            }
        }
    }
DatePickerDateFormat

總的調用方法 

<DatePicker conver:DatePickerCalendar.IsMonthYear="True" conver:DatePickerDateFormat.DateFormat="yyyy-MM" />

 ========================================================================================================================

2016年12月20日

修改原來的代碼,增加IsYear附加屬性,用法和IsMonthYear一樣,設置為True則只顯示年份


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM