You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
15 KiB
510 lines
15 KiB
/************************************************************************
|
|
|
|
Extended WPF Toolkit
|
|
|
|
Copyright (C) 2010-2012 Xceed Software Inc.
|
|
|
|
This program is provided to you under the terms of the Microsoft Public
|
|
License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
|
|
|
|
This program can be provided to you by Xceed Software Inc. under a
|
|
proprietary commercial license agreement for use in non-Open Source
|
|
projects. The commercial version of Extended WPF Toolkit also includes
|
|
priority technical support, commercial updates, and many additional
|
|
useful WPF controls if you license Xceed Business Suite for WPF.
|
|
|
|
Visit http://xceed.com and follow @datagrid on Twitter.
|
|
|
|
**********************************************************************/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Globalization;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
|
|
namespace Xceed.Wpf.Toolkit
|
|
{
|
|
[TemplatePart( Name = PART_TimeListItems, Type = typeof( ListBox ) )]
|
|
public class TimePicker : Control
|
|
{
|
|
private const string PART_TimeListItems = "PART_TimeListItems";
|
|
|
|
#region Members
|
|
|
|
ListBox _timeListBox;
|
|
private DateTimeFormatInfo DateTimeFormatInfo
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
internal static readonly TimeSpan EndTimeDefaultValue = new TimeSpan( 23, 59, 0 );
|
|
internal static readonly TimeSpan StartTimeDefaultValue = new TimeSpan( 0, 0, 0 );
|
|
internal static readonly TimeSpan TimeIntervalDefaultValue = new TimeSpan( 1, 0, 0 );
|
|
|
|
#endregion //Members
|
|
|
|
#region Properties
|
|
|
|
#region AllowSpin
|
|
|
|
public static readonly DependencyProperty AllowSpinProperty = DependencyProperty.Register( "AllowSpin", typeof( bool ), typeof( TimePicker ), new UIPropertyMetadata( true ) );
|
|
public bool AllowSpin
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( AllowSpinProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( AllowSpinProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //AllowSpin
|
|
|
|
#region EndTime
|
|
|
|
public static readonly DependencyProperty EndTimeProperty = DependencyProperty.Register( "EndTime", typeof( TimeSpan ), typeof( TimePicker ), new UIPropertyMetadata( EndTimeDefaultValue, new PropertyChangedCallback( OnEndTimeChanged ), new CoerceValueCallback( OnCoerceEndTime ) ) );
|
|
|
|
private static object OnCoerceEndTime( DependencyObject o, object value )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
return timePicker.OnCoerceEndTime( ( TimeSpan )value );
|
|
else
|
|
return value;
|
|
}
|
|
|
|
private static void OnEndTimeChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnEndTimeChanged( ( TimeSpan )e.OldValue, ( TimeSpan )e.NewValue );
|
|
}
|
|
|
|
protected virtual TimeSpan OnCoerceEndTime( TimeSpan value )
|
|
{
|
|
// TODO: Keep the proposed value within the desired range.
|
|
return value;
|
|
}
|
|
|
|
protected virtual void OnEndTimeChanged( TimeSpan oldValue, TimeSpan newValue )
|
|
{
|
|
if( _timeListBox != null )
|
|
_timeListBox.ItemsSource = GenerateTimeListItemsSource();
|
|
}
|
|
|
|
public TimeSpan EndTime
|
|
{
|
|
// IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
|
|
get
|
|
{
|
|
return ( TimeSpan )GetValue( EndTimeProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( EndTimeProperty, value );
|
|
}
|
|
}
|
|
|
|
|
|
#endregion //EndTime
|
|
|
|
#region Format
|
|
|
|
public static readonly DependencyProperty FormatProperty = DependencyProperty.Register( "Format", typeof( TimeFormat ), typeof( TimePicker ), new UIPropertyMetadata( TimeFormat.ShortTime, OnFormatChanged ) );
|
|
public TimeFormat Format
|
|
{
|
|
get
|
|
{
|
|
return ( TimeFormat )GetValue( FormatProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( FormatProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnFormatChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnFormatChanged( ( TimeFormat )e.OldValue, ( TimeFormat )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnFormatChanged( TimeFormat oldValue, TimeFormat newValue )
|
|
{
|
|
|
|
}
|
|
|
|
#endregion //Format
|
|
|
|
#region FormatString
|
|
|
|
public static readonly DependencyProperty FormatStringProperty = DependencyProperty.Register( "FormatString", typeof( string ), typeof( TimePicker ), new UIPropertyMetadata( default( String ), OnFormatStringChanged ) );
|
|
public string FormatString
|
|
{
|
|
get
|
|
{
|
|
return ( string )GetValue( FormatStringProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( FormatStringProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnFormatStringChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnFormatStringChanged( ( string )e.OldValue, ( string )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnFormatStringChanged( string oldValue, string newValue )
|
|
{
|
|
if( string.IsNullOrEmpty( newValue ) )
|
|
throw new ArgumentException( "CustomFormat should be specified.", FormatString );
|
|
}
|
|
|
|
#endregion //FormatString
|
|
|
|
#region IsOpen
|
|
|
|
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register( "IsOpen", typeof( bool ), typeof( TimePicker ), new UIPropertyMetadata( false ) );
|
|
public bool IsOpen
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( IsOpenProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( IsOpenProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //IsOpen
|
|
|
|
#region ShowButtonSpinner
|
|
|
|
public static readonly DependencyProperty ShowButtonSpinnerProperty = DependencyProperty.Register( "ShowButtonSpinner", typeof( bool ), typeof( TimePicker ), new UIPropertyMetadata( true ) );
|
|
public bool ShowButtonSpinner
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( ShowButtonSpinnerProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( ShowButtonSpinnerProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //ShowButtonSpinner
|
|
|
|
#region StartTime
|
|
|
|
public static readonly DependencyProperty StartTimeProperty = DependencyProperty.Register( "StartTime", typeof( TimeSpan ), typeof( TimePicker ), new UIPropertyMetadata( StartTimeDefaultValue, new PropertyChangedCallback( OnStartTimeChanged ), new CoerceValueCallback( OnCoerceStartTime ) ) );
|
|
|
|
private static object OnCoerceStartTime( DependencyObject o, object value )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
return timePicker.OnCoerceStartTime( ( TimeSpan )value );
|
|
else
|
|
return value;
|
|
}
|
|
|
|
private static void OnStartTimeChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnStartTimeChanged( ( TimeSpan )e.OldValue, ( TimeSpan )e.NewValue );
|
|
}
|
|
|
|
protected virtual TimeSpan OnCoerceStartTime( TimeSpan value )
|
|
{
|
|
// TODO: Keep the proposed value within the desired range.
|
|
return value;
|
|
}
|
|
|
|
protected virtual void OnStartTimeChanged( TimeSpan oldValue, TimeSpan newValue )
|
|
{
|
|
if( _timeListBox != null )
|
|
_timeListBox.ItemsSource = GenerateTimeListItemsSource();
|
|
}
|
|
|
|
public TimeSpan StartTime
|
|
{
|
|
// IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
|
|
get
|
|
{
|
|
return ( TimeSpan )GetValue( StartTimeProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( StartTimeProperty, value );
|
|
}
|
|
}
|
|
|
|
|
|
#endregion //StartTime
|
|
|
|
#region TimeInterval
|
|
|
|
public static readonly DependencyProperty TimeIntervalProperty = DependencyProperty.Register( "TimeInterval", typeof( TimeSpan ), typeof( TimePicker ), new UIPropertyMetadata( TimeIntervalDefaultValue, OnTimeIntervalChanged ) );
|
|
public TimeSpan TimeInterval
|
|
{
|
|
get
|
|
{
|
|
return ( TimeSpan )GetValue( TimeIntervalProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( TimeIntervalProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnTimeIntervalChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnTimeIntervalChanged( ( TimeSpan )e.OldValue, ( TimeSpan )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnTimeIntervalChanged( TimeSpan oldValue, TimeSpan newValue )
|
|
{
|
|
if( _timeListBox != null )
|
|
_timeListBox.ItemsSource = GenerateTimeListItemsSource();
|
|
}
|
|
|
|
#endregion //TimeInterval
|
|
|
|
#region Value
|
|
|
|
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof( DateTime? ), typeof( TimePicker ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged ) );
|
|
public DateTime? Value
|
|
{
|
|
get
|
|
{
|
|
return ( DateTime? )GetValue( ValueProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( ValueProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnValueChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
TimePicker timePicker = o as TimePicker;
|
|
if( timePicker != null )
|
|
timePicker.OnValueChanged( ( DateTime? )e.OldValue, ( DateTime? )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnValueChanged( DateTime? oldValue, DateTime? newValue )
|
|
{
|
|
//TODO: refactor this
|
|
if( newValue.HasValue && _timeListBox != null )
|
|
{
|
|
var items = _timeListBox.ItemsSource;
|
|
foreach( TimeItem item in items )
|
|
{
|
|
if( item.Time == newValue.Value.TimeOfDay )
|
|
{
|
|
int index = _timeListBox.Items.IndexOf( item );
|
|
if( _timeListBox.SelectedIndex != index )
|
|
_timeListBox.SelectedIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RoutedPropertyChangedEventArgs<object> args = new RoutedPropertyChangedEventArgs<object>( oldValue, newValue );
|
|
args.RoutedEvent = ValueChangedEvent;
|
|
RaiseEvent( args );
|
|
}
|
|
|
|
#endregion //Value
|
|
|
|
#region Watermark
|
|
|
|
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register( "Watermark", typeof( object ), typeof( TimePicker ), new UIPropertyMetadata( null ) );
|
|
public object Watermark
|
|
{
|
|
get
|
|
{
|
|
return ( object )GetValue( WatermarkProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( WatermarkProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //Watermark
|
|
|
|
#region WatermarkTemplate
|
|
|
|
public static readonly DependencyProperty WatermarkTemplateProperty = DependencyProperty.Register( "WatermarkTemplate", typeof( DataTemplate ), typeof( TimePicker ), new UIPropertyMetadata( null ) );
|
|
public DataTemplate WatermarkTemplate
|
|
{
|
|
get
|
|
{
|
|
return ( DataTemplate )GetValue( WatermarkTemplateProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( WatermarkTemplateProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //WatermarkTemplate
|
|
|
|
#endregion //Properties
|
|
|
|
#region Constructors
|
|
|
|
static TimePicker()
|
|
{
|
|
DefaultStyleKeyProperty.OverrideMetadata( typeof( TimePicker ), new FrameworkPropertyMetadata( typeof( TimePicker ) ) );
|
|
}
|
|
|
|
public TimePicker()
|
|
{
|
|
DateTimeFormatInfo = DateTimeFormatInfo.GetInstance( CultureInfo.CurrentCulture );
|
|
Keyboard.AddKeyDownHandler( this, OnKeyDown );
|
|
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler( this, OnMouseDownOutsideCapturedElement );
|
|
}
|
|
|
|
#endregion //Constructors
|
|
|
|
#region Base Class Overrides
|
|
|
|
public override void OnApplyTemplate()
|
|
{
|
|
base.OnApplyTemplate();
|
|
|
|
if( _timeListBox != null )
|
|
_timeListBox.SelectionChanged -= TimeListBox_SelectionChanged;
|
|
|
|
_timeListBox = GetTemplateChild( PART_TimeListItems ) as ListBox;
|
|
|
|
if( _timeListBox != null )
|
|
{
|
|
_timeListBox.SelectionChanged += TimeListBox_SelectionChanged;
|
|
_timeListBox.ItemsSource = GenerateTimeListItemsSource();
|
|
}
|
|
}
|
|
|
|
#endregion //Base Class Overrides
|
|
|
|
#region Event Handlers
|
|
|
|
private void OnKeyDown( object sender, KeyEventArgs e )
|
|
{
|
|
switch( e.Key )
|
|
{
|
|
case Key.Escape:
|
|
case Key.Tab:
|
|
{
|
|
CloseTimePicker();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnMouseDownOutsideCapturedElement( object sender, MouseButtonEventArgs e )
|
|
{
|
|
CloseTimePicker();
|
|
}
|
|
|
|
void TimeListBox_SelectionChanged( object sender, SelectionChangedEventArgs e )
|
|
{
|
|
if( e.AddedItems.Count > 0 )
|
|
{
|
|
TimeItem selectedTimeListItem = ( TimeItem )e.AddedItems[ 0 ];
|
|
var time = selectedTimeListItem.Time;
|
|
;
|
|
var date = Value ?? DateTime.MinValue;
|
|
|
|
Value = new DateTime( date.Year, date.Month, date.Day, time.Hours, time.Minutes, time.Seconds, time.Milliseconds );
|
|
}
|
|
|
|
CloseTimePicker();
|
|
}
|
|
|
|
#endregion //Event Handlers
|
|
|
|
#region Events
|
|
|
|
//Due to a bug in Visual Studio, you cannot create event handlers for nullable args in XAML, so I have to use object instead.
|
|
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent( "ValueChanged", RoutingStrategy.Bubble, typeof( RoutedPropertyChangedEventHandler<object> ), typeof( TimePicker ) );
|
|
public event RoutedPropertyChangedEventHandler<object> ValueChanged
|
|
{
|
|
add
|
|
{
|
|
AddHandler( ValueChangedEvent, value );
|
|
}
|
|
remove
|
|
{
|
|
RemoveHandler( ValueChangedEvent, value );
|
|
}
|
|
}
|
|
|
|
#endregion //Events
|
|
|
|
#region Methods
|
|
|
|
private void CloseTimePicker()
|
|
{
|
|
if( IsOpen )
|
|
IsOpen = false;
|
|
ReleaseMouseCapture();
|
|
}
|
|
|
|
public IEnumerable GenerateTimeListItemsSource()
|
|
{
|
|
TimeSpan time = StartTime;
|
|
TimeSpan endTime = EndTime;
|
|
|
|
if( endTime <= time )
|
|
{
|
|
endTime = EndTimeDefaultValue;
|
|
time = StartTimeDefaultValue;
|
|
}
|
|
|
|
TimeSpan timeInterval = TimeInterval;
|
|
|
|
if( time != null && endTime != null && timeInterval != null && timeInterval.Ticks > 0 )
|
|
{
|
|
while( time <= endTime )
|
|
{
|
|
yield return new TimeItem( DateTime.MinValue.Add( time ).ToString( GetTimeFormat(), CultureInfo.CurrentCulture ), time );
|
|
time = time.Add( timeInterval );
|
|
}
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
private string GetTimeFormat()
|
|
{
|
|
switch( Format )
|
|
{
|
|
case TimeFormat.Custom:
|
|
return FormatString;
|
|
case TimeFormat.LongTime:
|
|
return DateTimeFormatInfo.LongTimePattern;
|
|
case TimeFormat.ShortTime:
|
|
return DateTimeFormatInfo.ShortTimePattern;
|
|
default:
|
|
return DateTimeFormatInfo.ShortTimePattern;
|
|
}
|
|
}
|
|
|
|
#endregion //Methods
|
|
}
|
|
}
|
|
|