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