转载于 http://www.cnblogs.com/ZXdeveloper/archive/2015/07/21/4665772.html
代码部分:
public class XDatePicker : DatePicker
{
static XDatePicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(XDatePicker), new FrameworkPropertyMetadata(typeof(XDatePicker)));
}
#region 属性
static bool IsMonthYear, IsYear;
#region IsMonthYear
public static bool GetIsMonthYear(DependencyObject dobj)
{
return (bool)dobj.GetValue(IsMonthYearProperty);
}
public static void SetIsMonthYear(DependencyObject dobj, bool value)
{
dobj.SetValue(IsMonthYearProperty, value);
}
public static readonly DependencyProperty IsMonthYearProperty = DependencyProperty.RegisterAttached("IsMonthYear", typeof(bool), typeof(XDatePicker), new PropertyMetadata(OnIsMonthYearChanged));
private static void OnIsMonthYearChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
{
var datePicker = (XDatePicker)dobj;
IsMonthYear = GetIsMonthYear(dobj);
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(SetCalendarEventHandlers), datePicker, e);
}
#endregion
#region IsYear
public static bool GetIsYear(DependencyObject dobj)
{
return (bool)dobj.GetValue(IsYearProperty);
}
public static void SetIsYear(DependencyObject dobj, bool value)
{
dobj.SetValue(IsYearProperty, value);
}
public static readonly DependencyProperty IsYearProperty = DependencyProperty.RegisterAttached("IsYear", typeof(bool), typeof(XDatePicker), new PropertyMetadata(OnIsYearChanged));
private static void OnIsYearChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
{
var datePicker = (XDatePicker)dobj;
IsYear = GetIsYear(dobj);
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(SetCalendarEventHandlers), datePicker, e);
}
#endregion
#region DateFormat
public static string GetDateFormat(DependencyObject dobj)
{
return (string)dobj.GetValue(DateFormatProperty);
}
public static void SetDateFormat(DependencyObject dobj, string value)
{
dobj.SetValue(DateFormatProperty, value);
}
public static readonly DependencyProperty DateFormatProperty = DependencyProperty.RegisterAttached("DateFormat", typeof(string), typeof(XDatePicker), new PropertyMetadata(OnDateFormatChanged));
private static void OnDateFormatChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs e)
{
var datePicker = (XDatePicker)dobj;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyDateFormat), datePicker);
}
private static void ApplyDateFormat(XDatePicker datePicker)
{
var binding = new Binding("SelectedDate")
{
RelativeSource = new RelativeSource { AncestorType = typeof(XDatePicker) },
Converter = new DatePickerDateTimeConverter(),
ConverterParameter = new Tuplestring>(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;
e.Handled = true;
var textBox = (TextBox)sender;
var datePicker = (XDatePicker)textBox.TemplatedParent;
var dateStr = textBox.Text;
var formatStr = GetDateFormat(datePicker);
datePicker.SelectedDate = DatePickerDateTimeConverter.StringToDateTime(datePicker, formatStr, dateStr);
}
private class DatePickerDateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var formatStr = ((Tuplestring>)parameter).Item2;
var selectedDate = (DateTime?)value;
return DateTimeToString(formatStr, selectedDate);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var tupleParam = ((Tuplestring>)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(XDatePicker 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;
}
}
#endregion
private static void SetCalendarEventHandlers(XDatePicker datePicker, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == e.OldValue)
return;
if ((bool)e.NewValue)
{
datePicker.CalendarOpened += DatePickerOnCalendarOpened;
datePicker.CalendarClosed += DatePickerOnCalendarClosed;
}
else
{
datePicker.CalendarOpened -= DatePickerOnCalendarOpened;
datePicker.CalendarClosed -= DatePickerOnCalendarClosed;
}
}
private static void DatePickerOnCalendarOpened(object sender, RoutedEventArgs routedEventArgs)
{
var calendar = GetDatePickerCalendar(sender);
if (IsMonthYear) calendar.DisplayMode = CalendarMode.Year;
if (IsYear) calendar.DisplayMode = CalendarMode.Decade;
var datePicker = (XDatePicker)sender;
var textBox = GetTemplateTextBox(datePicker);
var formatStr = GetDateFormat(datePicker);
textBox.Text = DatePickerDateTimeConverter.DateTimeToString(formatStr, datePicker.SelectedDate);
calendar.DisplayModeChanged += CalendarOnDisplayModeChanged;
}
private static void DatePickerOnCalendarClosed(object sender, RoutedEventArgs routedEventArgs)
{
var datePicker = (XDatePicker)sender;
var calendar = GetDatePickerCalendar(sender);
datePicker.SelectedDate = calendar.SelectedDate;
calendar.DisplayModeChanged -= CalendarOnDisplayModeChanged;
}
private static void CalendarOnDisplayModeChanged(object sender, CalendarModeChangedEventArgs e)
{
var calendar = (System.Windows.Controls.Calendar)sender;
if (IsMonthYear && calendar.DisplayMode != CalendarMode.Month)
return;
if (IsYear && calendar.DisplayMode != CalendarMode.Year)
return;
calendar.SelectedDate = GetSelectedCalendarDate(calendar.DisplayDate);
var datePicker = GetCalendarsDatePicker(calendar);
datePicker.IsDropDownOpen = false;
}
private static System.Windows.Controls.Calendar GetDatePickerCalendar(object sender)
{
var datePicker = (XDatePicker)sender;
var popup = (Popup)datePicker.Template.FindName("PART_Popup", datePicker);
return ((System.Windows.Controls.Calendar)popup.Child);
}
private static XDatePicker GetCalendarsDatePicker(FrameworkElement child)
{
var parent = (FrameworkElement)child.Parent;
if (parent.Name == "PART_Root") return (XDatePicker)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);
}
#endregion
}
样式部分:
<Style TargetType="{x:Type local:XDatePicker}">
<Setter Property="Height" Value="28"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Margin" Value="5,0"/>
<Setter Property="BorderBrush" Value="gray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="SelectedDateFormat" Value="Short"/>
<Setter Property="Template">
<Setter.Value>
"{x:Type local:XDatePicker}">
"3" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
"CommonStates">
"Normal"/>
"Disabled">
"0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_Popup"/>
"PART_Root">
"DropDownButtonTemplate" TargetType="{x:Type Button}">
"18" Width="18">
<Path x:Name="calendar" Fill="#FF368DC0" RenderTransformOrigin="0.5,0.5" Stretch="Fill"
"white-space:pre"> Data="F1 M 18.0025,57.0081L 18.0025,23.0032L 23.0032,23.0032L 23.0032,20.0028C 23.0033,18.898 23.8988,18.0025 25.0035,18.0025L 29.004,18.0025C 30.1087,18.0025 31.0042,18.898 31.0043,20.0026L 31.0043,23.0032L 45.0063,23.0032L 45.0062,20.0026C 45.0062,18.8978 45.9018,18.0023 47.0065,18.0023L 51.0071,18.0023C 52.1118,18.0023 53.0074,18.8978 53.0074,20.0026L 53.0074,23.0032L 58.0081,23.0032L 58.0081,57.0081L 18.0025,57.0081 Z M 20,54L 56,54L 56,28L 20,28L 20,54 Z M 23,44L 31,44L 31,52L 23,52L 23,44 Z M 34,44L 42,44L 42,52L 34,52L 34,44 Z M 45,44L 53,44L 53,52L 45,52L 45,44 Z M 23,32L 31,32L 31,40L 23,40L 23,32 Z M 34,32L 42,32L 42,40L 34,40L 34,32 Z M 45,32L 53,32L 53,40L 45,40L 45,32 Z M 48.5067,20.0029C 47.6782,20.0029 47.0065,20.6745 47.0065,21.5031L 47.0065,24.5035C 47.0065,25.332 47.6782,26.0037 48.5067,26.0037L 49.5068,26.0037C 50.3354,26.0037 51.0071,25.332 51.0071,24.5035L 51.0071,21.5031C 51.0071,20.6745 50.3354,20.0029 49.5068,20.0029L 48.5067,20.0029 Z M 26.5037,20.0028C 25.6751,20.0028 25.0035,20.6745 25.0035,21.503L 25.0035,24.5035C 25.0035,25.332 25.6751,26.0037 26.5037,26.0037L 27.5038,26.0037C 28.3324,26.0037 29.004,25.332 29.004,24.5035L 29.004,21.503C 29.004,20.6745 28.3324,20.0028 27.5038,20.0028L 26.5037,20.0028 Z "/>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="calendar" Property="Fill" Value="#1BBBFA"/>
Trigger>
<Button x:Name="PART_Button" DockPanel.Dock="Right" Foreground="{TemplateBinding Foreground}" Focusable="False" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,0" Template="{StaticResource DropDownButtonTemplate}" Width="20"/>
<Grid Margin="3,0,0,0">
"PART_TextBox" Focusable="{TemplateBinding Focusable}" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center"/>
"PART_Popup" IsHitTestVisible="False" Opacity="0" AllowsTransparency="True" Placement="Bottom" PlacementTarget="{Binding ElementName=PART_TextBox}" StaysOpen="False"/>
Grid>
"{Binding Source={x:Static SystemParameters.HighContrast}}" Value="false">
<Setter Property="Foreground" TargetName="PART_TextBox" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"/>
Setter.Value>
Setter>
Style>