Подтвердить что ты не робот

WPF Toolkit DatePicker Только месяц/год

Я использую Toolkit Datepicker, как указано выше, но я хотел бы ограничить его только месяцами и годами, так как в этой ситуации пользователи не знают или не заботятся о точной дате. Обычно с хранящимися данными в формате Datetime будет храниться день, но это меня не касается. Есть ли простой способ связать это?

Спасибо

4b9b3361

Ответ 1

Если вы можете использовать элемент управления календарем, вы могли бы сделать

<toolkit:Calendar x:Name="_calendar" DisplayModeChanged="_calendar_DisplayModeChanged" DisplayMode="Year" />

с этим кодом

Private Sub _calendar_DisplayModeChanged(ByVal sender As System.Object, ByVal e As Microsoft.Windows.Controls.CalendarModeChangedEventArgs)

    If _calendar.DisplayMode = Microsoft.Windows.Controls.CalendarMode.Month Then
        _calendar.DisplayMode = Microsoft.Windows.Controls.CalendarMode.Year
    End If

End Sub

Ответ 2

Спасибо @Fernando García за основу этого.

Я написал свойства DateFormat и IsMonthYear для DatePicker, чтобы включить выбор месяца/года.

Вложенное свойство IsMonthYear ограничивает DatePicker Calendar.DisplayMode в Год и Десятилетие, чтобы предотвратить выбор из CalendarMode.Month. Он также устанавливает дневную часть DatePicker.SelectedDate равным 1.

Вложенное свойство DateFormat не ограничено этим сценарием, его также можно использовать отдельно от IsMonthYear, чтобы предоставить строку формата для отображения и ввода DatePicker.

Пример использования прикрепленных свойств:

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

Вложенное свойство IsMonthYear:

public class DatePickerCalendar
{
    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);
        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;
        if (calendar.DisplayMode != CalendarMode.Month)
            return;

        calendar.SelectedDate = GetSelectedCalendarDate(calendar.DisplayDate);

        var datePicker = GetCalendarsDatePicker(calendar);
        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);
    }
}

И прикрепленное свойство DateFormat:

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 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 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;
        }
    }
}

Ответ 3

Это комбинация Саймона и Др. ABT, в том числе весь дополнительный код и конфликты ссылок между Windows и Microsoft элементами управления исправлены.

Это довольно смешно, что для этого требуется класс из 345 строк, но он должен работать, если вы включаете DatePickerCalendar.cs (с вашим пространством имен), создаете свой проект и используете следующий XAML.

<DatePicker local:DatePickerCalendar.IsMonthYear="True" 
            local:DatePickerDateFormat.DateFormat="MMM-yyyy"
            Text="MMM-yyyy"></DatePicker>

DatePickerCalendar.cs

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Calendar = System.Windows.Controls.Calendar;
using CalendarMode = System.Windows.Controls.CalendarMode;
using CalendarModeChangedEventArgs = System.Windows.Controls.CalendarModeChangedEventArgs;
using DatePicker = System.Windows.Controls.DatePicker;

namespace <YourProject>
{
    public class DatePickerCalendar
    {
        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);
            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;
            if (calendar.DisplayMode != CalendarMode.Month)
                return;

            calendar.SelectedDate = GetSelectedCalendarDate(calendar.DisplayDate);

            var datePicker = GetCalendarsDatePicker(calendar);
            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);
        }
    }

    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)),
                StringFormat = GetDateFormat(datePicker) // This is also new but didnt seem to help
            };

            var textBox = GetTemplateTextBox(datePicker);
            textBox.SetBinding(TextBox.TextProperty, binding);

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

            var dropDownButton = GetTemplateButton(datePicker);

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

            // Handle Dropdownbutton PreviewMouseUp to prevent issue of flickering textboxes
            dropDownButton.PreviewMouseUp -= DropDownButtonPreviewMouseUp;
            dropDownButton.PreviewMouseUp += DropDownButtonPreviewMouseUp;
        }

        private static ButtonBase GetTemplateButton(DatePicker datePicker)
        {
            return (ButtonBase)datePicker.Template.FindName("PART_Button", datePicker);
        }


        /// <summary>
        ///     Prevents a bug in the DatePicker, where clicking the Dropdown open button results in Text being set to default formatting regardless of StringFormat or binding overrides
        /// </summary>
        private static void DropDownButtonPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            var fe = sender as FrameworkElement;
            if (fe == null) return;

            var datePicker = fe.TryFindParent<DatePicker>();
            if (datePicker == null || datePicker.SelectedDate == null) return;

            var dropDownButton = GetTemplateButton(datePicker);

            // Dropdown button was clicked
            if (e.OriginalSource == dropDownButton && datePicker.IsDropDownOpen == false)
            {
                // Open dropdown
                datePicker.SetCurrentValue(DatePicker.IsDropDownOpenProperty, true);

                // Mimic everything else in the standard DatePicker dropdown opening *except* setting textbox value 
                datePicker.SetCurrentValue(DatePicker.DisplayDateProperty, datePicker.SelectedDate.Value);

                // Important otherwise calendar does not work
                dropDownButton.ReleaseMouseCapture();

                // Prevent datePicker.cs from handling this event 
                e.Handled = true;
            }
        }



        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 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 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;
            }


        }

    }



    public static class FEExten
    {
        /// <summary>
        /// Finds a parent of a given item on the visual tree.
        /// </summary>
        /// <typeparam name="T">The type of the queried item.</typeparam>
        /// <param name="child">A direct or indirect child of the
        /// queried item.</param>
        /// <returns>The first parent item that matches the submitted
        /// type parameter. If not matching item can be found, a null
        /// reference is being returned.</returns>
        public static T TryFindParent<T>(this DependencyObject child)
            where T : DependencyObject
        {
            //get parent item
            DependencyObject parentObject = GetParentObject(child);

            //we've reached the end of the tree
            if (parentObject == null) return null;

            //check if the parent matches the type we're looking for
            T parent = parentObject as T;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                //use recursion to proceed with next level
                return TryFindParent<T>(parentObject);
            }
        }

        /// <summary>
        /// This method is an alternative to WPF's
        /// <see cref="VisualTreeHelper.GetParent"/> method, which also
        /// supports content elements. Keep in mind that for content element,
        /// this method falls back to the logical tree of the element!
        /// </summary>
        /// <param name="child">The item to be processed.</param>
        /// <returns>The submitted item parent, if available. Otherwise
        /// null.</returns>
        public static DependencyObject GetParentObject(this DependencyObject child)
        {
            if (child == null) return null;

            //handle content elements separately
            ContentElement contentElement = child as ContentElement;
            if (contentElement != null)
            {
                DependencyObject parent = ContentOperations.GetParent(contentElement);
                if (parent != null) return parent;

                FrameworkContentElement fce = contentElement as FrameworkContentElement;
                return fce != null ? fce.Parent : null;
            }

            //also try searching for parent in framework elements (such as DockPanel, etc)
            FrameworkElement frameworkElement = child as FrameworkElement;
            if (frameworkElement != null)
            {
                DependencyObject parent = frameworkElement.Parent;
                if (parent != null) return parent;
            }

            //if it not a ContentElement/FrameworkElement, rely on VisualTreeHelper
            return VisualTreeHelper.GetParent(child);
        }
    }
}

Скриншот

DVdg5.png

Ответ 4

Недавно у меня было специальное требование для календаря с возможностью выбора только месяца и года. Поэтому у меня есть предыдущее сообщение о пользовательском формате для датпикера wpf, но в этом случае нам нужно сделать небольшой сложный ответ. Сначала вам нужно создать определенный элемент управления следующим образом:

public class DatePickerCo : DatePicker

Как только вы это сделаете. Существует другая должность, когда кто-то другой, я не помню url для этого сообщения, но в любом случае, подумайте, что вам нужно перезаписать метод OnCalendarOpened так:

 
 protected override void OnCalendarOpened(RoutedEventArgs e)
        {
            var popup = this.Template.FindName(
                "PART_Popup", this) as Popup;
            if (popup != null && popup.Child is System.Windows.Controls.Calendar)
            {
                ((System.Windows.Controls.Calendar)popup.Child).DisplayMode = CalendarMode.Year;
            }

            ((System.Windows.Controls.Calendar)popup.Child).DisplayModeChanged += new EventHandler(DatePickerCo_DisplayModeChanged);
        }

Обратите внимание, что мы добавляем последнюю строку с обработчиком события DisplayModeChanged. Это действительно важно для следующих шагов. Итак, следующим шагом будет определение DisplayModeChanged


 private void DatePickerCo_DisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
        {
            var popup = this.Template.FindName(
                "PART_Popup", this) as Popup;
            if (popup != null && popup.Child is System.Windows.Controls.Calendar)
            {
                var _calendar = popup.Child as System.Windows.Controls.Calendar;
                if (_calendar.DisplayMode == CalendarMode.Month)
                {
                    _calendar.DisplayMode = CalendarMode.Year;

                    if (IsDropDownOpen)
                    {
                        this.SelectedDate = GetSelectedMonth(_calendar.DisplayDate);
                        this.IsDropDownOpen = false;
                        ((System.Windows.Controls.Calendar)popup.Child).DisplayModeChanged -= new EventHandler(DatePickerCo_DisplayModeChanged);
                    }
                }

            }
        }

 private DateTime? GetSelectedMonth(DateTime? selectedDate)
        {
            if (selectedDate == null)
            {
                selectedDate = DateTime.Now;
            }

            int monthDifferenceStart = DateTimeHelper.CompareYearMonth(selectedDate.Value, DisplayDateRangeStart);
            int monthDifferenceEnd = DateTimeHelper.CompareYearMonth(selectedDate.Value, DisplayDateRangeEnd);

            if (monthDifferenceStart >= 0 && monthDifferenceEnd  0, "monthDifferenceEnd should be greater than 0!");
                    _selectedMonth = DateTimeHelper.DiscardDayTime(DisplayDateRangeEnd);
                }
            }

            return _selectedMonth;
        }

Здесь есть пара вещей, сначала нужно выбрать месяц, так что причина для создания функции GetSelectedMonth. Во-вторых, этот метод использует класс WPFToolkit с именем DateTimeHelper.cs. Ok До сих пор мы только что создали функциональность для сбора месяца. Но как только вы нажмете на календарь месяца, мы перейдем к отображению выбранной даты в формате месяца/года. Для достижения этого нам просто нужно создать особый стиль для нашего пользовательского элемента управления следующим образом:

                                                       

В моем первом ответе Как изменить формат (например, dd/MMM/yyyy) DateTimePicker в приложении WPF

Я объяснил все с помощью пользовательского формата выбора даты. Надеюсь, это поможет вам. пожалуйста, любой комментарий или предложение, вы просто дайте мне знать, привет!..

Ответ 5

Чтобы добавить к Simon блестящий ответ, я внес некоторые изменения в DatePickerDateFormat.cs, чтобы предотвратить ошибку, когда текстовое поле мгновенно мерцает между dd/MM/yyyy ( исходный формат строки) и MM/yyyy (формат переопределенной строки).

Это происходит из-за того, что DatePicker автоматически устанавливает текст PART_Textbox непосредственно перед раскрытием. Он устанавливает его в текст, отформатированный в строке, и вы можете сделать что-то, что вы можете сделать, - это переопределит вашу привязку.

Чтобы предотвратить это поведение, я добавил этот код в класс DatePickerDateFormat выше.

  • в ApplyDateFormat, получите кнопку выпадающего меню и обработайте PreviewMouseUp

    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)),
                          StringFormat = GetDateFormat(datePicker) // This is also new but didnt seem to help
                      };
    
        var textBox = GetTemplateTextBox(datePicker);
        textBox.SetBinding(TextBox.TextProperty, binding);
    
        textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;
        textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;
    
        var dropDownButton = GetTemplateButton(datePicker);
    
        datePicker.CalendarOpened -= DatePickerOnCalendarOpened;
        datePicker.CalendarOpened += DatePickerOnCalendarOpened;
    
        // Handle Dropdownbutton PreviewMouseUp to prevent issue of flickering textboxes
        dropDownButton.PreviewMouseUp -= DropDownButtonPreviewMouseUp;
        dropDownButton.PreviewMouseUp += DropDownButtonPreviewMouseUp;
    }
    
    private static ButtonBase GetTemplateButton(DatePicker datePicker)
    {
        return (ButtonBase)datePicker.Template.FindName("PART_Button", datePicker);
    }
    

Когда PreviewMouseUp запускается, если есть выбранная дата, переопределите поведение On Dropdown Shown (мы имитируем все, что делает Datepicker, но мы не устанавливаем текст PART_TextBox)

    /// <summary>
    ///     Prevents a bug in the DatePicker, where clicking the Dropdown open button results in Text being set to default formatting regardless of StringFormat or binding overrides
    /// </summary>
    private static void DropDownButtonPreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        var fe = sender as FrameworkElement;
        if (fe == null) return;

        var datePicker = fe.TryFindAncestorOrSelf<DatePicker>();
        if (datePicker == null || datePicker.SelectedDate == null) return;            

        var dropDownButton = GetTemplateButton(datePicker);

        // Dropdown button was clicked
        if (e.OriginalSource == dropDownButton && datePicker.IsDropDownOpen == false)
        {                                
            // Open dropdown
            datePicker.SetCurrentValue(DatePicker.IsDropDownOpenProperty, true);

            // Mimic everything else in the standard DatePicker dropdown opening *except* setting textbox value 
            datePicker.SetCurrentValue(DatePicker.DisplayDateProperty, datePicker.SelectedDate.Value);

            // Important otherwise calendar does not work
            dropDownButton.ReleaseMouseCapture();

            // Prevent datePicker.cs from handling this event 
            e.Handled = true;                
        }
    }

где метод расширения TryFindAncestorOrSelf обрабатывает визуальное дерево, чтобы найти объект типа T. Вы можете найти реализацию этого здесь: http://www.hardcodet.net/2008/02/find-wpf-parent

Надеюсь, это поможет кому-то!

Ответ 6

i перевел Simon ответ на VB, я публикую здесь результаты (просьба дать ему кредиты)

Imports System.Windows.Threading
Imports System.Windows.Controls.Primitives
Imports System.Windows.Controls

''' <summary>     Allows the Date Picker Calendar to select month.
'''                      Use with the attached property IsMonthYear Property.
''' </summary>
''' <remarks> Source : https://stackoverflow.com/info/1798513/wpf-toolkit-datepicker-month-year-only 
'''           Author : </remarks>
Public Class DatePickerCalendar

Public Shared IsMonthYearProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsMonthYear", GetType(System.Boolean), GetType(DatePickerCalendar), New PropertyMetadata(AddressOf OnIsMonthYearChanged))

Public Shared Function GetIsMonthYear(ByVal dobj As DependencyObject) As Boolean
    Return CType(dobj.GetValue(IsMonthYearProperty), Boolean)
End Function

Public Shared Sub SetIsMonthYear(ByVal dobj As DependencyObject, ByVal value As Boolean)
    dobj.SetValue(IsMonthYearProperty, value)
End Sub

Private Shared Sub OnIsMonthYearChanged(ByVal dobj As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim MyDatePicker = CType(dobj, DatePicker)
    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, _
                    New Action(Of DatePicker, DependencyPropertyChangedEventArgs)(AddressOf SetCalendarEventHandlers), MyDatePicker, e)
End Sub

Private Shared Sub SetCalendarEventHandlers(ByVal datePicker As DatePicker, ByVal e As DependencyPropertyChangedEventArgs)
    If (e.NewValue = e.OldValue) Then
        Return
    End If
    If CType(e.NewValue, Boolean) Then
        AddHandler datePicker.CalendarOpened, AddressOf DatePickerOnCalendarOpened
        AddHandler datePicker.CalendarClosed, AddressOf DatePickerOnCalendarClosed
    Else
        RemoveHandler datePicker.CalendarOpened, AddressOf DatePickerOnCalendarOpened
        RemoveHandler datePicker.CalendarClosed, AddressOf DatePickerOnCalendarClosed
    End If
End Sub

Private Shared Sub DatePickerOnCalendarOpened(ByVal sender As Object, ByVal routedEventArgs As RoutedEventArgs)
    Dim MyCalendar = GetDatePickerCalendar(sender)
    MyCalendar.DisplayMode = CalendarMode.Year
    AddHandler MyCalendar.DisplayModeChanged, AddressOf CalendarOnDisplayModeChanged
End Sub

Private Shared Sub DatePickerOnCalendarClosed(ByVal sender As Object, ByVal routedEventArgs As RoutedEventArgs)
    Dim MyDatePicker = CType(sender, DatePicker)
    Dim MyCalendar = GetDatePickerCalendar(sender)
    MyDatePicker.SelectedDate = MyCalendar.SelectedDate
    RemoveHandler MyCalendar.DisplayModeChanged, AddressOf CalendarOnDisplayModeChanged
End Sub

Private Shared Sub CalendarOnDisplayModeChanged(ByVal sender As Object, ByVal e As CalendarModeChangedEventArgs)
    Dim MyCalendar = CType(sender, Calendar)
    If (MyCalendar.DisplayMode <> CalendarMode.Month) Then
        Return
    End If
    MyCalendar.SelectedDate = GetSelectedCalendarDate(MyCalendar.DisplayDate)
    Dim MyDatePicker = GetCalendarsDatePicker(MyCalendar)
    MyDatePicker.IsDropDownOpen = False
End Sub

Private Shared Function GetDatePickerCalendar(ByVal sender As Object) As Calendar
    Dim MyDatePicker = CType(sender, DatePicker)
    Dim MyPopup = CType(MyDatePicker.Template.FindName("PART_Popup", MyDatePicker), Popup)
    Return CType(MyPopup.Child, Calendar)
End Function

Private Shared Function GetCalendarsDatePicker(ByVal child As FrameworkElement) As DatePicker
    Dim MyParent = CType(child.Parent, FrameworkElement)
    If (MyParent.Name = "PART_Root") Then
        Return CType(MyParent.TemplatedParent, DatePicker)
    End If
    Return GetCalendarsDatePicker(MyParent)
End Function

Private Shared Function GetSelectedCalendarDate(ByVal selectedDate As DateTime?) As DateTime?
    If Not selectedDate.HasValue Then
        Return Nothing
    End If
    Return New DateTime(selectedDate.Value.Year, selectedDate.Value.Month, 1)
End Function
End Class

Но для класса формата я не мог заставить его работать.
Я использовал и slighlty изменил (более простой) ответ от petrycol для отображения месяца/года. Источник:
Изменение формата строки WPF DatePicker

    <DatePicker   SelectedDate="{Binding FromDate}"  
                  l:DatePickerCalendar.IsMonthYear="True"
                  x:Name="MonthCalendar" HorizontalAlignment="Center"                   
                 >
        <DatePicker.Resources>
            <!--Source : /info/178838/changing-the-string-format-of-the-wpf-datepicker
                Author : petrycol -->
            <Style TargetType="{x:Type DatePickerTextBox}">
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <TextBox Width="60"    TextAlignment="Center" x:Name="PART_TextBox"
                                     Text="{Binding Path=SelectedDate, StringFormat='MM yy', 
                                     RelativeSource={RelativeSource AncestorType={x:Type DatePicker}},FallbackValue='-- --'}" />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </DatePicker.Resources>
        <!--CalendarOpened="DatePicker_CalendarOpened"-->
    </DatePicker>