web-dev-qa-db-ja.com

WPFツールキットDatePicker月/年のみ

上記のようにツールキットのDatepickerを使用していますが、この状況ではユーザーが正確な日付を知らないか気にしないので、月と年の選択のみに制限したいと思います。データが日時に保存されていることは明らかですフォーマットは保存された日になりますが、それは私には関係ありません。これを結び付ける簡単な方法はありますか?

ありがとう

20
ChrisFletcher

代わりにCalendarコントロールを使用できる場合

<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
14
kenwarner

これの根拠を提供してくれた@FernandoGarcíaに感謝します。

DatePickerのDateFormatおよびIsMonthYear添付プロパティを記述して、月/年の選択を有効にしました。

IsMonthYear添付プロパティは、DatePickerのCalendar.DisplayModeをYearおよびDecadeに制限して、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'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;
        }
    }
}
26
Simon

これは Simon'sDr。ABT's の回答の組み合わせであり、必要なすべての追加コードを含み、WindowsMicrosoft間の参照の競合があります。コントロールが修正されました。

これを実現するには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'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;
            }


        }

    }



    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's 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's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
            return VisualTreeHelper.GetParent(child);
        }
    }
}

スクリーンショット

12
Charles Clayton

最近、カレンダーに特定の要件があり、月と年のみを選択するオプションがありました。それで、wpf datepickerのカスタム形式に関する以前の投稿がありますが、この場合は少しトリッキーな答えをする必要があります。まず、次のような特定のコントロールを作成する必要があります。

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のハンドラーを含む最後の行を追加していることに注意してください。これは次のステップで非常に重要です。 Okしたがって、次のステップは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;
        }

ここには2つの点があります。最初に月を選択する必要があるため、関数GetSelectedMonthを作成するのはそのためです。 2つ目は、メソッドがDateTimeHelper.csというWPFToolkitのクラスを使用することです。 Okこれまでは、月を選択するための機能を作成してきましたが、月のカレンダーをクリックすると、選択した日付が月/年の形式で表示されます。そのために、次のように、カスタムコントロールに特定のスタイルを作成するだけです。

私の最初の答え WPFアプリケーションでDateTimePickerの形式(例:dd/MMM/yyyy)を変更する方法

日付ピッカーのカスタム形式ですべてについて説明しました。これがお役に立てば幸いです。コメントや提案をお願いします。よろしくお願いします。

5

Simon's brilliant answer に追加するために、DatePickerDateFormat.csにいくつかの変更を加えて、テキストボックスがdd/MM/yyyy(元の文字列形式)とMM/yyyy(オーバーライドされた)の間で瞬間的にちらつくバグを防ぎました文字列形式)。

これは、ドロップダウンが開かれる直前に、DatePickerがPART_Textboxテキストを内部的に設定するために発生します。それは文字列形式のテキストに設定され、あなたがそれについてできることはありません-それはあなたのバインディングを上書きします。

この動作を防ぐために、このコードを 上記のDatePickerDateFormatクラス に追加しました。

  1. 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が発生したときに、選択された日付がある場合は、ドロップダウン表示時の動作をオーバーライドします(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

これが誰かを助けることを願っています!

4
Dr. ABT

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/questions/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

しかし、フォーマットクラスの場合、機能させることができませんでした。
月/年の表示にpetrycolからの(より単純な)回答を使用して簡単に変更しました。出典:
WPF DatePickerの文字列形式の変更

    <DatePicker   SelectedDate="{Binding FromDate}"  
                  l:DatePickerCalendar.IsMonthYear="True"
                  x:Name="MonthCalendar" HorizontalAlignment="Center"                   
                 >
        <DatePicker.Resources>
            <!--Source : https://stackoverflow.com/questions/3819832/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>
3
GameAlchemist

多くのコードを必要としない代替ソリューションを提供したいと思います。 DatePickersのCalendarOpenedイベントにバインドできます。そのイベントでは、Calendarの表示モードをDecadeに設定し、DisplayModeChangedのイベントハンドラーをCalendarに追加できます。次に、DisplayModeChangedイベントハンドラーで、モードが月の場合はカレンダーを閉じ、SelectedDateを現在のDisplayDateに設定することもできます。これは私の目的のためにうまくいきました

XAML

<DatePicker
Name="YearPicker"
Grid.Column="1"
SelectedDate="{Binding SelectedDate, Mode=TwoWay}"
CalendarOpened="DatePicker_Opened">
<DatePicker.Resources>
    <Style TargetType="DatePickerTextBox">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <TextBox x:Name="PART_TextBox"
                            Text="{Binding Path=SelectedDate, StringFormat = {}{0:MM-yyyy}, 
                            RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DatePicker.Resources>

次に、背後にあるコード

private void DatePicker_Opened(object sender, RoutedEventArgs e)
{
    DatePicker datepicker = (DatePicker)sender;
    Popup popup = (Popup)datepicker.Template.FindName("PART_Popup", datepicker);
    Calendar cal = (Calendar)popup.Child;
    cal.DisplayModeChanged += Calender_DisplayModeChanged;
    cal.DisplayMode = CalendarMode.Decade;
}

private void Calender_DisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
{
    Calendar calendar = (Calendar)sender;
    if (calendar.DisplayMode == CalendarMode.Month)
    {
        calendar.SelectedDate = calendar.DisplayDate;
        YearPicker.IsDropDownOpen = false;
    }
}

これが役に立てば幸いです!

2
abaga129