C# WPF工具包日期选择器仅限月/年

C# WPF工具包日期选择器仅限月/年,c#,datetime,datepicker,wpftoolkit,C#,Datetime,Datepicker,Wpftoolkit,如上所述,我正在使用工具包的日期选择器,但我想将其限制为仅选择月份和年份,因为在这种情况下,用户不知道或不关心确切的日期。显然,如果数据以日期时间格式存储,则会存储日期,但这与我无关。有没有一个简单的方法来解决这个问题 谢谢如果您可以使用日历控件,您可以 <toolkit:Calendar x:Name="_calendar" DisplayModeChanged="_calendar_DisplayModeChanged" DisplayMode="Year" /> 最近,我对日

如上所述,我正在使用工具包的日期选择器,但我想将其限制为仅选择月份和年份,因为在这种情况下,用户不知道或不关心确切的日期。显然,如果数据以日期时间格式存储,则会存储日期,但这与我无关。有没有一个简单的方法来解决这个问题


谢谢

如果您可以使用日历控件,您可以

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

最近,我对日历有一个特殊的要求,可以只选择月份和年份。所以我之前有一篇关于wpf日期选择器的自定义格式的文章,但在这种情况下,我们需要做一点棘手的回答。首先,您需要创建如下特定控件:

public class DatePickerCo : DatePicker
 
 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);
        }
一旦你已经这样做了。当其他人发布其他帖子时,我不记得该帖子的url,但无论如何,我认为您需要覆盖OnCalendarOpen方法,如下所示:

public class DatePickerCo : DatePicker
 
 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的原因。第二件事是该方法使用一个名为DateTimeHelper.cs的WPFToolkit类。好的,到现在为止,我们刚刚创建了每月提货的功能。但是,一旦您点击月历,我们将继续以月/年格式显示所选日期。为此,我们只需为自定义控件创建一个特定样式,如下所示:

public class DatePickerCo : DatePicker
 
 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);
        }

在我的第一个回答中


我解释了所有关于日期选择器的自定义格式。我希望这对你有帮助。请有任何意见或建议,请让我知道,问候

感谢@Fernando García为我们提供了这个基础

我已经为DatePicker编写了DateFormat和IsMonthYear附加属性,以启用月/年选择

IsMonthYear attached属性将DatePicker的Calendar.DisplayMode限制为年和十年,以防止从CalendarMode.Month进行选择。它还将DatePicker.SelectedDate的日期部分设置为1

DateFormat attached属性不仅限于此场景,它还可以与IsMonthYear分开使用,为DatePicker的显示和输入提供格式字符串

附加属性的示例用法:

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

所附财产为:

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))
            };
        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;
        }
    }
}
公共类DatePickerCalendar
{
公共静态只读从属属性IsMonthYearProperty=
DependencyProperty.RegisterAttached(“IsMonthYear”)、typeof(bool)、typeof(DatePickerCalendar),
新属性元数据(仅在1个月内更改);
公共静态bool GetIsMonthYear(DependencyObject dobj)
{
返回(bool)dobj.GetValue(IsMonthYearProperty);
}
公共静态无效设置IsMonthYear(DependencyObject dobj,布尔值)
{
dobj.SetValue(IsMonthYearProperty,value);
}
私有静态无效OnInMonthyearChanged(DependencyObject dobj、DependencyPropertyChangedEventArgs e)
{
var datePicker=(datePicker)dobj;
Application.Current.Dispatcher
.BeginInvoke(DispatcherPriority.Loaded,
新操作(SetCalendarEventHandlers),
日期选择器(e);
}
私有静态void SetCalendarEventHandler(日期选择器DatePicker,DependencyPropertyChangedEventArgs e)
{
如果(e.NewValue==e.OldValue)
返回;
if((bool)e.NewValue)
{
datePicker.CalendarOpen+=DatePickerOnCalendarOpen;
datePicker.CalendarClosed+=DatePickerOnCalendarClosed;
}
其他的
{
datePicker.CalendarOpen-=DatePickerOnCalendarOpen;
datePicker.CalendarClosed-=DatePickerOnCalendarClosed;
}
}
私有静态无效日期选择器日历已打开(对象发送方,RoutedEventArgs RoutedEventArgs)
{
var calendar=GetDatePickerCalendar(发送方);
calendar.DisplayMode=CalendarMode.Year;
calendar.DisplayModeChanged+=CalendarOnDisplayModeChanged;
}
私有静态无效日期选择器日历已关闭(对象发送方,RoutedEventArgs RoutedEventArgs)
{
var datePicker=(datePicker)发送方;
var calendar=GetDatePickerCalendar(发送方);
datePicker.SelectedDate=calendar.SelectedDate;
calendar.DisplayModeChanged-=CalendarOnDisplayModeChanged;
}
私有静态无效CalendarOnDisplayModeChanged(对象发送方,CalendarModeChangedEventArgs e)
{
var calendar=(日历)发送方;
如果(calendar.DisplayMode!=CalendarMode.Month)
返回;
calendar.SelectedDate=GetSelectedCalendarDate(calendar.DisplayDate);
var datePicker=GetCalendarsDatePicker(日历);
datePicker.IsDropDownOpen=false;
}
私有静态日历GetDatePickerCalendar(对象发送方)
{
var datePicker=(datePicker)发送方;
var popup=(popup)datePicker.Template.FindName(“PART_popup”,datePicker);
返回((日历)popup.Child);
}
私有静态日期选择器GetCalendarsDatePicker(FrameworkElement子级)
{
var parent=(FrameworkElement)child.parent;
if(parent.Name==“PART\u Root”)
return(DatePicker)parent.TemplatedParent;
返回GetCalendarsDatePicker(父级);
}
私有静态日期时间?GetSelectedCalendarDate(日期时间?selectedDate)
{
如果(!selectedDate.HasValue)
返回null;
返回新的日期时间(selectedDate.Value.Year,selectedDate.Value.Month,1);
}
}
DateFormat附加属性为:

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))
            };
        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
{
公共静态只读DependencyProperty DateFormatProperty=
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);
        }
    }
}
<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;
    }
}