/Release/Silverlight3/Source/Controls.Input.Toolkit/TimeInput/TimeUpDown.cs

# · C# · 1396 lines · 820 code · 147 blank · 429 comment · 107 complexity · 9e1ac0608f659371dede78da77ec825d MD5 · raw file

  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System.ComponentModel;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Windows.Controls.Primitives;
  10. using System.Windows.Input;
  11. using System.Windows.Interop;
  12. using System.Windows.Media;
  13. using System.Windows.Automation.Peers;
  14. using System.Windows.Automation;
  15. namespace System.Windows.Controls
  16. {
  17. /// <summary>
  18. /// Represents a control that uses a spinner and textbox to allow a user to
  19. /// input time.
  20. /// </summary>
  21. /// <remarks>TimeInput supports only the following formatting characters:
  22. /// 'h', 'm', 's', 'H', 't'. All other characters are filtered out:
  23. /// 'd', 'f', 'F', 'g', 'K', 'M', 'y', 'z'.</remarks>
  24. /// <QualityBand>Preview</QualityBand>
  25. [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)]
  26. [TemplateVisualState(Name = VisualStates.StateMouseOver, GroupName = VisualStates.GroupCommon)]
  27. [TemplateVisualState(Name = VisualStates.StatePressed, GroupName = VisualStates.GroupCommon)]
  28. [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)]
  29. [TemplateVisualState(Name = VisualStates.StateFocused, GroupName = VisualStates.GroupFocus)]
  30. [TemplateVisualState(Name = VisualStates.StateUnfocused, GroupName = VisualStates.GroupFocus)]
  31. [TemplateVisualState(Name = TimeHintOpenedUpStateName, GroupName = GroupTimeHint)]
  32. [TemplateVisualState(Name = TimeHintOpenedDownStateName, GroupName = GroupTimeHint)]
  33. [TemplateVisualState(Name = TimeHintClosedStateName, GroupName = GroupTimeHint)]
  34. [TemplateVisualState(Name = ValidTimeStateName, GroupName = GroupTimeParsingStates)]
  35. [TemplateVisualState(Name = InvalidTimeStateName, GroupName = GroupTimeParsingStates)]
  36. [TemplateVisualState(Name = EmptyTimeStateName, GroupName = GroupTimeParsingStates)]
  37. [TemplatePart(Name = ElementTextName, Type = typeof(TextBox))]
  38. [TemplatePart(Name = ElementSpinnerName, Type = typeof(Spinner))]
  39. [TemplatePart(Name = ElementTimeHintPopupName, Type = typeof(Popup))]
  40. [StyleTypedProperty(Property = SpinnerStyleName, StyleTargetType = typeof(ButtonSpinner))]
  41. public class TimeUpDown : UpDownBase<DateTime?>, IUpdateVisualState, ITimeInput
  42. {
  43. /// <summary>
  44. /// StringFormat used in the TimeHint.
  45. /// </summary>
  46. private const string TimeHintFormat = "<{0}>";
  47. #region TemplatePart names
  48. /// <summary>
  49. /// The name for the TimeHint element.
  50. /// </summary>
  51. private const string ElementTimeHintPopupName = "TimeHintPopup";
  52. #endregion TemplatePart names
  53. #region Visual state names
  54. /// <summary>
  55. /// The group name "TimeHintStates".
  56. /// </summary>
  57. internal const string GroupTimeHint = "TimeHintStates";
  58. /// <summary>
  59. /// The group name "ParsingStates".
  60. /// </summary>
  61. internal const string GroupTimeParsingStates = "ParsingStates";
  62. /// <summary>
  63. /// The state name "TimeHintOpenedUp" indicates that the hint is being
  64. /// shown on the top of the control.
  65. /// </summary>
  66. internal const string TimeHintOpenedUpStateName = "TimeHintOpenedUp";
  67. /// <summary>
  68. /// The state name "TimeHintOpenedDown" indicates that the hint is
  69. /// being shown at the bottom of the control.
  70. /// </summary>
  71. internal const string TimeHintOpenedDownStateName = "TimeHintOpenedDown";
  72. /// <summary>
  73. /// The state name "TimeHintClosed" indicates that no hint is being
  74. /// shown.
  75. /// </summary>
  76. internal const string TimeHintClosedStateName = "TimeHintClosed";
  77. /// <summary>
  78. /// The state name "ValidTime" that indicates that currently the textbox
  79. /// text parses to a valid Time.
  80. /// </summary>
  81. internal const string ValidTimeStateName = "ValidTime";
  82. /// <summary>
  83. /// The state name "InvalidTime" that indicates that currently the textbox
  84. /// text does not allow parsing.
  85. /// </summary>
  86. internal const string InvalidTimeStateName = "InvalidTime";
  87. /// <summary>
  88. /// The state name "EmptyTime" that indicates that currently the textbox
  89. /// text would parse to a Null.
  90. /// </summary>
  91. internal const string EmptyTimeStateName = "EmptyTime";
  92. #endregion
  93. #region TemplateParts
  94. /// <summary>
  95. /// Gets or sets the time hint popup part.
  96. /// </summary>
  97. /// <value>The time hint popup part.</value>
  98. private Popup TimeHintPopupPart
  99. {
  100. get { return _timeHintPopupPart; }
  101. set
  102. {
  103. // the content of the TimeHint is not going to be changed
  104. // on the fly. We can subscribe to mouse events from here.
  105. if (_timeHintPopupPart != null && _timeHintPopupPart.Child != null)
  106. {
  107. _timeHintPopupPart.Child.MouseLeftButtonDown -= OnTimeHintMouseLeftButtonDown;
  108. }
  109. _timeHintPopupPart = value;
  110. if (_timeHintPopupPart != null)
  111. {
  112. if (_timeHintPopupPart.Child != null)
  113. {
  114. _timeHintPopupPart.Child.MouseLeftButtonDown += OnTimeHintMouseLeftButtonDown;
  115. }
  116. // Defer opening of the Popup to avoid an exception
  117. Dispatcher.BeginInvoke(() => _timeHintPopupPart.IsOpen = true);
  118. }
  119. }
  120. }
  121. /// <summary>
  122. /// BackingField for TimeHintPopupPart.
  123. /// </summary>
  124. private Popup _timeHintPopupPart;
  125. #endregion TemplateParts
  126. /// <summary>
  127. /// Helper class that centralizes the coercion logic across all
  128. /// TimeInput controls.
  129. /// </summary>
  130. private readonly TimeCoercionHelper _timeCoercionHelper;
  131. /// <summary>
  132. /// The text that was last parsed. Used in comparisons.
  133. /// </summary>
  134. private string _lastParsedText;
  135. /// <summary>
  136. /// Gets or sets a value indicating whether this instance is showing a
  137. /// TimeHint visual.
  138. /// </summary>
  139. /// <value><c>True</c> if this instance is showing the TimeHint; otherwise, <c>false</c>.</value>
  140. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Follows picker pattern.")]
  141. private bool IsShowTimeHint
  142. {
  143. get { return _isShowTimeHint; }
  144. set
  145. {
  146. if (value)
  147. {
  148. // TimeHint popup needs to be shown
  149. if (Text != null && TimeHintPopupPart != null && TimeHintPopupPart.Child != null)
  150. {
  151. // will need to work with the child of the popup
  152. FrameworkElement child = TimeHintPopupPart.Child as FrameworkElement;
  153. if (child != null)
  154. {
  155. // Popup does not determine width correctly
  156. child.Width = Text.ActualWidth;
  157. // defaulting to no TimeHint
  158. _timeHintExpandDirection = null;
  159. // determine Direction
  160. // prefer opening on top.
  161. MatrixTransform mt;
  162. try
  163. {
  164. // will fail if popup is not in visual tree yet
  165. mt = TransformToVisual(null) as MatrixTransform;
  166. }
  167. catch
  168. {
  169. IsShowTimeHint = false;
  170. return;
  171. }
  172. // disregard height setting on the TimeHint since it will be animated.
  173. // using desired height to determine if the TimeHint is capable
  174. // of being shown on top.
  175. // this is not perfect, since the storyboard might animate
  176. // to a different height, but that is not the common scenario.
  177. child.Height = Double.NaN;
  178. child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
  179. if (mt != null)
  180. {
  181. if (mt.Matrix.OffsetY > child.DesiredSize.Height)
  182. {
  183. // will use the up animation
  184. _timeHintExpandDirection = ExpandDirection.Up;
  185. // position the popup at the top of the content
  186. TimeHintPopupPart.VerticalAlignment = VerticalAlignment.Top;
  187. }
  188. else
  189. {
  190. // need to determine if there is space below the picker
  191. Content hostContent = Application.Current.Host.Content;
  192. if (hostContent.ActualHeight - (mt.Matrix.OffsetY + ActualHeight) > child.DesiredSize.Height)
  193. {
  194. _timeHintExpandDirection = ExpandDirection.Down;
  195. TimeHintPopupPart.VerticalAlignment = VerticalAlignment.Bottom;
  196. }
  197. else
  198. {
  199. IsShowTimeHint = false;
  200. return;
  201. }
  202. }
  203. }
  204. }
  205. }
  206. }
  207. _isShowTimeHint = value;
  208. UpdateVisualState(true);
  209. }
  210. }
  211. /// <summary>
  212. /// The direction in which the TimeHint will expand.
  213. /// </summary>
  214. private ExpandDirection? _timeHintExpandDirection;
  215. /// <summary>
  216. /// BackingField for IsShowTimeHint.
  217. /// </summary>
  218. private bool _isShowTimeHint;
  219. /// <summary>
  220. /// Gets the actual minimum. If a Minimum is set, use that, otherwise
  221. /// use the start of the day.
  222. /// </summary>
  223. private DateTime ActualMinimum
  224. {
  225. get
  226. {
  227. return Minimum.HasValue ?
  228. (Value.HasValue ? Value.Value.Date.Add(Minimum.Value.TimeOfDay) : Minimum.Value)
  229. : (Value.HasValue ? Value.Value.Date : DateTime.Now.Date);
  230. }
  231. }
  232. /// <summary>
  233. /// Gets the actual maximum. If a Maximum is set, use that, otherwise
  234. /// use the end of the day.
  235. /// </summary>
  236. /// <value>The actual maximum.</value>
  237. private DateTime ActualMaximum
  238. {
  239. get
  240. {
  241. return Maximum.HasValue
  242. ? (Value.HasValue ? Value.Value.Date.Add(Maximum.Value.TimeOfDay) : Maximum.Value)
  243. : (Value.HasValue
  244. ? Value.Value.Date.Add(TimeSpan.FromDays(1).Subtract(TimeSpan.FromSeconds(1)))
  245. : DateTime.Now.Date.AddDays(1).Subtract(TimeSpan.FromSeconds(1)));
  246. }
  247. }
  248. /// <summary>
  249. /// Gets or sets the currently selected time.
  250. /// </summary>
  251. [TypeConverter(typeof(TimeTypeConverter))]
  252. public override DateTime? Value
  253. {
  254. get { return base.Value; }
  255. set { base.Value = value; }
  256. }
  257. /// <summary>
  258. /// A value indicating whether a dependency property change handler
  259. /// should ignore the next change notification. This is used to reset
  260. /// the value of properties without performing any of the actions in
  261. /// their change handlers.
  262. /// </summary>
  263. private bool _ignoreValueChange;
  264. #region public DateTime? Minimum
  265. /// <summary>
  266. /// Gets or sets the minimum time considered valid by the control.
  267. /// </summary>
  268. /// <remarks>Setting the minimum property is applicable for the following
  269. /// features: Parsing a new value from the textbox, spinning a new value
  270. /// and programmatically specifying a value.</remarks>
  271. [TypeConverter(typeof(TimeTypeConverter))]
  272. public DateTime? Minimum
  273. {
  274. get { return (DateTime?)GetValue(MinimumProperty); }
  275. set { SetValue(MinimumProperty, value); }
  276. }
  277. /// <summary>
  278. /// Identifies the Minimum dependency property.
  279. /// </summary>
  280. public static readonly DependencyProperty MinimumProperty =
  281. DependencyProperty.Register(
  282. "Minimum",
  283. typeof(DateTime?),
  284. typeof(TimeUpDown),
  285. new PropertyMetadata(null, OnMinimumPropertyChanged));
  286. /// <summary>
  287. /// MinimumProperty property changed handler.
  288. /// </summary>
  289. /// <param name="d">TimeUpDown that changed its Minimum.</param>
  290. /// <param name="e">Event arguments.</param>
  291. private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  292. {
  293. TimeUpDown source = (TimeUpDown)d;
  294. DateTime? oldValue = (DateTime?)e.OldValue;
  295. DateTime? newValue = (DateTime?)e.NewValue;
  296. source._ignoreValueChange = true;
  297. source._timeCoercionHelper.ProcessMinimumChange(newValue);
  298. source._ignoreValueChange = false;
  299. source.OnMinimumChanged(oldValue, newValue);
  300. source.SetValidSpinDirection();
  301. }
  302. /// <summary>
  303. /// Called when the Minimum property value has changed.
  304. /// </summary>
  305. /// <param name="oldValue">Old value of the Minimum property.</param>
  306. /// <param name="newValue">New value of the Minimum property.</param>
  307. protected virtual void OnMinimumChanged(DateTime? oldValue, DateTime? newValue)
  308. {
  309. }
  310. #endregion public DateTime? Minimum
  311. #region public DateTime? Maximum
  312. /// <summary>
  313. /// Gets or sets the maximum time considered valid by the control.
  314. /// </summary>
  315. /// <remarks>Setting the Maximum property is applicable for the following
  316. /// features: Parsing a new value from the textbox, spinning a new value
  317. /// and programmatically specifying a value. </remarks>
  318. [TypeConverter(typeof(TimeTypeConverter))]
  319. public DateTime? Maximum
  320. {
  321. get { return (DateTime?)GetValue(MaximumProperty); }
  322. set { SetValue(MaximumProperty, value); }
  323. }
  324. /// <summary>
  325. /// Identifies the Maximum dependency property.
  326. /// </summary>
  327. public static readonly DependencyProperty MaximumProperty =
  328. DependencyProperty.Register(
  329. "Maximum",
  330. typeof(DateTime?),
  331. typeof(TimeUpDown),
  332. new PropertyMetadata(null, OnMaximumPropertyChanged));
  333. /// <summary>
  334. /// MaximumProperty property changed handler.
  335. /// </summary>
  336. /// <param name="d">TimeUpDown that changed its Maximum.</param>
  337. /// <param name="e">Event arguments.</param>
  338. private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  339. {
  340. TimeUpDown source = (TimeUpDown)d;
  341. DateTime? oldValue = (DateTime?)e.OldValue;
  342. DateTime? newValue = (DateTime?)e.NewValue;
  343. source._ignoreValueChange = true;
  344. source._timeCoercionHelper.ProcessMaximumChange(newValue);
  345. source._ignoreValueChange = false;
  346. source.OnMaximumChanged(oldValue, newValue);
  347. source.SetValidSpinDirection();
  348. }
  349. /// <summary>
  350. /// Called when the Maximum property value has changed.
  351. /// </summary>
  352. /// <param name="oldValue">Old value of the Maximum property.</param>
  353. /// <param name="newValue">New value of the Maximum property.</param>
  354. protected virtual void OnMaximumChanged(DateTime? oldValue, DateTime? newValue)
  355. {
  356. }
  357. #endregion public DateTime? Maximum
  358. #region public TimeParserCollection TimeParsers
  359. /// <summary>
  360. /// Gets or sets a collection of TimeParsers that are used when parsing
  361. /// text to time.
  362. /// </summary>
  363. [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "Can be set from xaml.")]
  364. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Can be set from xaml.")]
  365. public TimeParserCollection TimeParsers
  366. {
  367. get { return GetValue(TimeParsersProperty) as TimeParserCollection; }
  368. set { SetValue(TimeParsersProperty, value); }
  369. }
  370. /// <summary>
  371. /// Identifies the TimeParsers dependency property.
  372. /// </summary>
  373. public static readonly DependencyProperty TimeParsersProperty =
  374. DependencyProperty.Register(
  375. "TimeParsers",
  376. typeof(TimeParserCollection),
  377. typeof(TimeUpDown),
  378. new PropertyMetadata(OnTimeParsersPropertyChanged));
  379. /// <summary>
  380. /// TimeParsersProperty property changed handler.
  381. /// </summary>
  382. /// <param name="d">TimeUpDown that changed its TimeParsers.</param>
  383. /// <param name="e">Event arguments.</param>
  384. private static void OnTimeParsersPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  385. {
  386. }
  387. /// <summary>
  388. /// Gets the actual TimeParsers that will be used for parsing by the control.
  389. /// </summary>
  390. /// <remarks>Includes the TimeParsers introduced in the TimeGlobalizationInfo.</remarks>
  391. [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "Can be set from xaml.")]
  392. public TimeParserCollection ActualTimeParsers
  393. {
  394. get
  395. {
  396. return new TimeParserCollection(ActualTimeGlobalizationInfo.GetActualTimeParsers(TimeParsers == null ? null : TimeParsers.ToList()));
  397. }
  398. }
  399. #endregion public TimeParserCollection TimeParsers
  400. #region public ITimeFormat Format
  401. /// <summary>
  402. /// Gets or sets the Format used by the control.
  403. /// From XAML Use either "Short", "Long" or a custom format.
  404. /// Custom formats can only contain "H", "h", "m", "s" or "t".
  405. /// For example: use 'hh:mm:ss' is used to format time as "13:45:30".
  406. /// </summary>
  407. [TypeConverter(typeof(TimeFormatConverter))]
  408. public ITimeFormat Format
  409. {
  410. get { return GetValue(FormatProperty) as ITimeFormat; }
  411. set { SetValue(FormatProperty, value); }
  412. }
  413. /// <summary>
  414. /// Identifies the Format dependency property.
  415. /// </summary>
  416. public static readonly DependencyProperty FormatProperty =
  417. DependencyProperty.Register(
  418. "Format",
  419. typeof(ITimeFormat),
  420. typeof(TimeUpDown),
  421. new PropertyMetadata(OnFormatPropertyChanged));
  422. /// <summary>
  423. /// FormatProperty property changed handler.
  424. /// </summary>
  425. /// <param name="d">TimePicker that changed its Format.</param>
  426. /// <param name="e">Event arguments.</param>
  427. private static void OnFormatPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  428. {
  429. TimeUpDown source = (TimeUpDown)d;
  430. if (e.NewValue != null)
  431. {
  432. // no need for cache any more.
  433. source._actualFormat = null;
  434. }
  435. source.SetTextBoxText();
  436. }
  437. /// <summary>
  438. /// Gets the actual format that will be used to display Time in the
  439. /// TimeUpDown. If no format is specified, ShortTimeFormat is used.
  440. /// </summary>
  441. /// <value>The actual display format.</value>
  442. public ITimeFormat ActualFormat
  443. {
  444. get
  445. {
  446. if (Format == null)
  447. {
  448. if (_actualFormat == null)
  449. {
  450. _actualFormat = new ShortTimeFormat();
  451. }
  452. return _actualFormat;
  453. }
  454. else
  455. {
  456. return Format;
  457. }
  458. }
  459. }
  460. /// <summary>
  461. /// BackingField for ActualFormat.
  462. /// </summary>
  463. private ITimeFormat _actualFormat;
  464. #endregion public ITimeFormat Format
  465. #region public CultureInfo Culture
  466. /// <summary>
  467. /// Gets or sets the culture that will be used by the control for
  468. /// parsing and formatting.
  469. /// </summary>
  470. public CultureInfo Culture
  471. {
  472. get { return (CultureInfo)GetValue(CultureProperty); }
  473. set { SetValue(CultureProperty, value); }
  474. }
  475. /// <summary>
  476. /// Identifies the Culture dependency property.
  477. /// </summary>
  478. public static readonly DependencyProperty CultureProperty =
  479. DependencyProperty.Register(
  480. "Culture",
  481. typeof(CultureInfo),
  482. typeof(TimeUpDown),
  483. new PropertyMetadata(null, OnCulturePropertyChanged));
  484. /// <summary>
  485. /// CultureProperty property changed handler.
  486. /// </summary>
  487. /// <param name="d">TimeUpDown that changed its Culture.</param>
  488. /// <param name="e">Event arguments.</param>
  489. private static void OnCulturePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  490. {
  491. TimeUpDown source = (TimeUpDown)d;
  492. source.ActualTimeGlobalizationInfo.Culture = e.NewValue as CultureInfo;
  493. source.SetTextBoxText();
  494. }
  495. #endregion public CultureInfo Culture
  496. /// <summary>
  497. /// Gets the actual culture used by the control for formatting and parsing.
  498. /// </summary>
  499. /// <value>The actual culture.</value>
  500. public CultureInfo ActualCulture
  501. {
  502. get
  503. {
  504. return ActualTimeGlobalizationInfo.ActualCulture;
  505. }
  506. }
  507. #region public TimeGlobalizationInfo TimeGlobalizationInfo
  508. /// <summary>
  509. /// Gets or sets the strategy object that determines how the control
  510. /// interacts with DateTime and CultureInfo.
  511. /// </summary>
  512. public TimeGlobalizationInfo TimeGlobalizationInfo
  513. {
  514. get { return (TimeGlobalizationInfo)GetValue(TimeGlobalizationInfoProperty); }
  515. set { SetValue(TimeGlobalizationInfoProperty, value); }
  516. }
  517. /// <summary>
  518. /// Identifies the TimeGlobalizationInfo dependency property.
  519. /// </summary>
  520. public static readonly DependencyProperty TimeGlobalizationInfoProperty =
  521. DependencyProperty.Register(
  522. "TimeGlobalizationInfo",
  523. typeof(TimeGlobalizationInfo),
  524. typeof(TimeUpDown),
  525. new PropertyMetadata(OnTimeGlobalizationInfoPropertyChanged));
  526. /// <summary>
  527. /// TimeGlobalizationInfoProperty property changed handler.
  528. /// </summary>
  529. /// <param name="d">TimeUpDown that changed its TimeGlobalizationInfo.</param>
  530. /// <param name="e">Event arguments.</param>
  531. private static void OnTimeGlobalizationInfoPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  532. {
  533. TimeUpDown source = (TimeUpDown)d;
  534. TimeGlobalizationInfo newValue = e.NewValue as TimeGlobalizationInfo;
  535. if (newValue != null)
  536. {
  537. newValue.Culture = source.Culture;
  538. source._actualTimeGlobalizationInfo = null; // no need for default any more.
  539. }
  540. }
  541. /// <summary>
  542. /// Gets the actual TimeGlobalization info used by the control.
  543. /// </summary>
  544. /// <remarks>If TimeGlobalizationInfo is not set, will return
  545. /// default TimeGlobalizationInfo instance.</remarks>
  546. public TimeGlobalizationInfo ActualTimeGlobalizationInfo
  547. {
  548. get
  549. {
  550. TimeGlobalizationInfo info = TimeGlobalizationInfo;
  551. if (info == null)
  552. {
  553. if (_actualTimeGlobalizationInfo == null)
  554. {
  555. // set the default strategy object
  556. _actualTimeGlobalizationInfo = new TimeGlobalizationInfo();
  557. }
  558. return _actualTimeGlobalizationInfo;
  559. }
  560. else
  561. {
  562. return info;
  563. }
  564. }
  565. }
  566. /// <summary>
  567. /// BackingField for ActualTimeGlobalizationInfo.
  568. /// </summary>
  569. private TimeGlobalizationInfo _actualTimeGlobalizationInfo;
  570. #endregion public TimeGlobalizationInfo TimeGlobalizationInfo
  571. #region public bool IsCyclic
  572. /// <summary>
  573. /// Gets or sets a value indicating whether the TimeUpDown control will
  574. /// cycle through values when trying to spin the first and last item.
  575. /// </summary>
  576. public bool IsCyclic
  577. {
  578. get { return (bool)GetValue(IsCyclicProperty); }
  579. set { SetValue(IsCyclicProperty, value); }
  580. }
  581. /// <summary>
  582. /// Identifies the IsCyclic dependency property.
  583. /// </summary>
  584. public static readonly DependencyProperty IsCyclicProperty =
  585. DependencyProperty.Register(
  586. "IsCyclic",
  587. typeof(bool),
  588. typeof(TimeUpDown),
  589. new PropertyMetadata(true, OnIsCyclicPropertyChanged));
  590. /// <summary>
  591. /// IsCyclicProperty property changed handler.
  592. /// </summary>
  593. /// <param name="d">DomainUpDown instance that changed its IsCyclic value.</param>
  594. /// <param name="e">Event arguments.</param>
  595. private static void OnIsCyclicPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  596. {
  597. TimeUpDown source = (TimeUpDown)d;
  598. source.SetValidSpinDirection();
  599. }
  600. #endregion public bool IsCyclic
  601. #region public object TimeHintContent
  602. /// <summary>
  603. /// Gets the text used to guide the user when entering time.
  604. /// </summary>
  605. public object TimeHintContent
  606. {
  607. get { return GetValue(TimeHintContentProperty) as object; }
  608. private set
  609. {
  610. _allowHintContentChange = true;
  611. SetValue(TimeHintContentProperty, value);
  612. _allowHintContentChange = false;
  613. }
  614. }
  615. /// <summary>
  616. /// Identifies the TimeHintContent dependency property.
  617. /// </summary>
  618. public static readonly DependencyProperty TimeHintContentProperty =
  619. DependencyProperty.Register(
  620. "TimeHintContent",
  621. typeof(object),
  622. typeof(TimeUpDown),
  623. new PropertyMetadata(String.Empty, OnTimeHintContentPropertyChanged));
  624. /// <summary>
  625. /// TimeHintContentProperty property changed handler.
  626. /// </summary>
  627. /// <param name="d">TimeUpDown that changed its TimeHintContent.</param>
  628. /// <param name="e">Event arguments.</param>
  629. private static void OnTimeHintContentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  630. {
  631. TimeUpDown source = (TimeUpDown) d;
  632. if (!source._allowHintContentChange)
  633. {
  634. // set old value
  635. source.TimeHintContent = e.OldValue;
  636. throw new InvalidOperationException(Properties.Resources.TimeUpDown_OnTimeHintContentChanged);
  637. }
  638. }
  639. /// <summary>
  640. /// Represents the formatted DateTime that is used in the TimeHint hint.
  641. /// </summary>
  642. private string _timeHintDate;
  643. /// <summary>
  644. /// Indicates whether the control should not proceed with selecting all
  645. /// text.
  646. /// </summary>
  647. private bool _isIgnoreSelectionOfAllText;
  648. /// <summary>
  649. /// BackingField for AllowHintContentChange.
  650. /// </summary>
  651. private bool _allowHintContentChange;
  652. #endregion public object TimeHintContent
  653. /// <summary>
  654. /// Initializes a new instance of the <see cref="TimeUpDown"/> class.
  655. /// </summary>
  656. public TimeUpDown()
  657. {
  658. DefaultStyleKey = typeof(TimeUpDown);
  659. _timeCoercionHelper = new TimeCoercionHelper(this);
  660. }
  661. /// <summary>
  662. /// Builds the visual tree for the TimeUpDown control when a new
  663. /// template is applied.
  664. /// </summary>
  665. public override void OnApplyTemplate()
  666. {
  667. if (Text != null)
  668. {
  669. Text.SelectionChanged -= SelectionChanged;
  670. Text.TextChanged -= InputChanged;
  671. Text.LostFocus -= OnTextLostFocus;
  672. }
  673. base.OnApplyTemplate();
  674. if (Text != null)
  675. {
  676. Text.SelectionChanged += SelectionChanged;
  677. Text.TextChanged += InputChanged;
  678. Text.LostFocus += OnTextLostFocus;
  679. }
  680. TimeHintPopupPart = GetTemplateChild(ElementTimeHintPopupName) as Popup;
  681. SetValidSpinDirection();
  682. }
  683. /// <summary>
  684. /// Provides handling for the ValueChanging event.
  685. /// </summary>
  686. /// <param name="e">Event args.</param>
  687. protected override void OnValueChanging(RoutedPropertyChangingEventArgs<DateTime?> e)
  688. {
  689. if (_ignoreValueChange)
  690. {
  691. e.InCoercion = true;
  692. OnValueChanged(new RoutedPropertyChangedEventArgs<DateTime?>(e.OldValue, e.NewValue));
  693. return;
  694. }
  695. // change is from value itself.
  696. bool success = _timeCoercionHelper.CoerceValue(e.OldValue, e.NewValue);
  697. if (success)
  698. {
  699. e.InCoercion = false;
  700. e.NewValue = Value;
  701. base.OnValueChanging(e);
  702. }
  703. else
  704. {
  705. e.InCoercion = true;
  706. }
  707. }
  708. /// <summary>
  709. /// Raises the ValueChanged event when Value property has changed.
  710. /// </summary>
  711. /// <param name="e">Event args.</param>
  712. protected override void OnValueChanged(RoutedPropertyChangedEventArgs<DateTime?> e)
  713. {
  714. base.OnValueChanged(e);
  715. // preselect all text, so that when we tab into the control, everything is selected
  716. if (FocusManager.GetFocusedElement() != Text)
  717. {
  718. _isIgnoreSelectionOfAllText = false;
  719. SelectAllText();
  720. _isIgnoreSelectionOfAllText = true;
  721. }
  722. SetValidSpinDirection();
  723. TimeUpDownAutomationPeer peer = FrameworkElementAutomationPeer.FromElement(this) as TimeUpDownAutomationPeer;
  724. if (peer != null)
  725. {
  726. // automation peer cannot handle nulls.
  727. string oldFormatted = ActualTimeGlobalizationInfo.FormatTime(e.OldValue, ActualFormat);
  728. string newFormatted = ActualTimeGlobalizationInfo.FormatTime(e.NewValue, ActualFormat);
  729. peer.RaisePropertyChangedEvent(ValuePatternIdentifiers.ValueProperty, oldFormatted, newFormatted);
  730. }
  731. }
  732. /// <summary>
  733. /// Called by ApplyValue to parse user input.
  734. /// </summary>
  735. /// <param name="text">User input.</param>
  736. /// <returns>Value parsed from user input.</returns>
  737. protected override DateTime? ParseValue(string text)
  738. {
  739. DateTime? parsed = ActualTimeGlobalizationInfo.ParseTime(text, ActualFormat, TimeParsers);
  740. if (parsed.HasValue && Value.HasValue)
  741. {
  742. // use the datepart of Value, and the timepart of parsed.
  743. return Value.Value.Date.Add(parsed.Value.TimeOfDay);
  744. }
  745. return parsed;
  746. }
  747. /// <summary>
  748. /// Renders the value property into the textbox text.
  749. /// </summary>
  750. /// <returns>Formatted Value.</returns>
  751. protected internal override string FormatValue()
  752. {
  753. _lastParsedText = ActualTimeGlobalizationInfo.FormatTime(Value, ActualFormat);
  754. return _lastParsedText;
  755. }
  756. /// <summary>
  757. /// Called by OnSpin when the spin direction is SpinDirection.Increase.
  758. /// </summary>
  759. protected override void OnIncrement()
  760. {
  761. if (Text != null)
  762. {
  763. int caretPosition = Text.SelectionStart;
  764. TimeSpan ts = ActualTimeGlobalizationInfo.GetTimeUnitAtTextPosition(Text.Text, caretPosition, ActualFormat);
  765. // new value could be on a new day.
  766. DateTime newValue = ActualTimeGlobalizationInfo.OnIncrement(Value.Value, ts);
  767. if (newValue > ActualMaximum)
  768. {
  769. if (IsCyclic)
  770. {
  771. // if there is no maximum set, we can skip over it
  772. // otherwise set the time to the minimum
  773. if (Maximum.HasValue)
  774. {
  775. newValue = ActualMinimum;
  776. }
  777. }
  778. else
  779. {
  780. newValue = ActualMaximum;
  781. }
  782. }
  783. Value = newValue;
  784. // check if old caretPosition is still valid for this TimeSpan
  785. if (ts == ActualTimeGlobalizationInfo.GetTimeUnitAtTextPosition(Text.Text, caretPosition, ActualFormat))
  786. {
  787. Text.SelectionStart = caretPosition;
  788. }
  789. else
  790. {
  791. int newCaretPosition = ActualTimeGlobalizationInfo.GetTextPositionForTimeUnit(Text.Text, ts, ActualFormat);
  792. Text.SelectionStart = newCaretPosition > -1 ? newCaretPosition : caretPosition;
  793. }
  794. }
  795. }
  796. /// <summary>
  797. /// Called by OnSpin when the spin direction is SpinDirection.Increase.
  798. /// </summary>
  799. protected override void OnDecrement()
  800. {
  801. if (Text != null)
  802. {
  803. int caretPosition = Text.SelectionStart;
  804. TimeSpan ts = ActualTimeGlobalizationInfo.GetTimeUnitAtTextPosition(Text.Text, caretPosition, ActualFormat);
  805. // new value could be on a new day.
  806. DateTime newValue = ActualTimeGlobalizationInfo.OnDecrement(Value.Value, ts);
  807. if (newValue < ActualMinimum)
  808. {
  809. if (IsCyclic)
  810. {
  811. // if there is no maximum set, we can skip over it
  812. // otherwise set the time to the minimum
  813. if (Minimum.HasValue)
  814. {
  815. newValue = ActualMaximum;
  816. }
  817. }
  818. else
  819. {
  820. newValue = ActualMinimum;
  821. }
  822. }
  823. Value = newValue;
  824. // check if old caretPosition is still valid for this TimeSpan
  825. if (ts == ActualTimeGlobalizationInfo.GetTimeUnitAtTextPosition(Text.Text, caretPosition, ActualFormat))
  826. {
  827. Text.SelectionStart = caretPosition;
  828. }
  829. else
  830. {
  831. int newCaretPosition = ActualTimeGlobalizationInfo.GetTextPositionForTimeUnit(Text.Text, ts, ActualFormat);
  832. Text.SelectionStart = newCaretPosition > -1 ? newCaretPosition : caretPosition;
  833. }
  834. }
  835. }
  836. /// <summary>
  837. /// Sets the valid spin direction based on the position of the caret,
  838. /// the value and the minimum and maximum.
  839. /// </summary>
  840. private void SetValidSpinDirection()
  841. {
  842. ValidSpinDirections spinDirections = ValidSpinDirections.None;
  843. if (Text != null && FocusManager.GetFocusedElement() == Text && !string.IsNullOrEmpty(Text.Text))
  844. {
  845. DateTime? parsed = null;
  846. try
  847. {
  848. parsed = ParseValue(Text.Text);
  849. }
  850. catch (ArgumentException)
  851. {
  852. // swallow
  853. }
  854. if (!parsed.HasValue)
  855. {
  856. // are not able to parse current input to something nice.
  857. spinDirections = ValidSpinDirections.None;
  858. }
  859. else
  860. {
  861. if (IsCyclic)
  862. {
  863. spinDirections = ValidSpinDirections.Decrease | ValidSpinDirections.Increase;
  864. }
  865. else
  866. {
  867. TimeSpan span = ActualTimeGlobalizationInfo.GetTimeUnitAtTextPosition(
  868. Text.Text,
  869. Text.SelectionStart,
  870. ActualFormat);
  871. if (ActualTimeGlobalizationInfo.OnIncrement(parsed.Value, span) <= ActualMaximum)
  872. {
  873. spinDirections = spinDirections | ValidSpinDirections.Increase;
  874. }
  875. if (ActualTimeGlobalizationInfo.OnDecrement(parsed.Value, span) >= ActualMinimum)
  876. {
  877. spinDirections = spinDirections | ValidSpinDirections.Decrease;
  878. }
  879. }
  880. }
  881. }
  882. if (Spinner != null)
  883. {
  884. Spinner.ValidSpinDirection = spinDirections;
  885. }
  886. }
  887. /// <summary>
  888. /// Handles the SelectionChanged event from TextBox.
  889. /// </summary>
  890. /// <param name="sender">The sender.</param>
  891. /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/>
  892. /// instance containing the event data.</param>
  893. private void SelectionChanged(object sender, RoutedEventArgs e)
  894. {
  895. SetValidSpinDirection();
  896. }
  897. /// <summary>
  898. /// Handles the TextChanged event.
  899. /// </summary>
  900. /// <param name="sender">The sender.</param>
  901. /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/>
  902. /// instance containing the event data.</param>
  903. private void InputChanged(object sender, TextChangedEventArgs e)
  904. {
  905. DetermineHint();
  906. }
  907. /// <summary>
  908. /// Determines the value of the hint property.
  909. /// </summary>
  910. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Follows the pattern where parsing may fail.")]
  911. private void DetermineHint()
  912. {
  913. // when in a designer, ignore this logic.
  914. if (DesignerProperties.GetIsInDesignMode(this))
  915. {
  916. return;
  917. }
  918. // possibly called while textbox does not have focus. Remove the TimeHint.
  919. if (FocusManager.GetFocusedElement() != Text)
  920. {
  921. IsShowTimeHint = false;
  922. return;
  923. }
  924. // might show a different date as a guide. However, check if that is within range.
  925. Func<DateTime, DateTime> CoerceDate = time =>
  926. {
  927. if (time.TimeOfDay > ActualMaximum.TimeOfDay)
  928. {
  929. return ActualMaximum;
  930. }
  931. if (time.TimeOfDay < ActualMinimum.TimeOfDay)
  932. {
  933. return ActualMinimum;
  934. }
  935. return time;
  936. };
  937. DateTime fallbackTime = CoerceDate(DateTime.Now);
  938. // prepare custom parsing hook
  939. UpDownParsingEventArgs<DateTime?> parsingArgs = new UpDownParsingEventArgs<DateTime?>(Text.Text);
  940. DateTime? parsed;
  941. if (!ActualTimeGlobalizationInfo.TryParseTime(Text.Text, ActualFormat, TimeParsers, out parsed))
  942. {
  943. // unable to parse, default to showing DateTime.Now as guide.
  944. // allow custom parsing, with an empty Value.
  945. try
  946. {
  947. OnParsing(parsingArgs);
  948. if (parsingArgs.Handled && parsingArgs.Value.HasValue)
  949. {
  950. // will only use custom parsing if parsed to non-null.
  951. fallbackTime = CoerceDate(parsingArgs.Value.Value);
  952. }
  953. }
  954. catch (Exception)
  955. {
  956. // swallow any exception during custom parsing.
  957. }
  958. IsShowTimeHint = true;
  959. VisualStateManager.GoToState(this, InvalidTimeStateName, true);
  960. // the date that will be shown in the TimeHint.
  961. _timeHintDate = ActualTimeGlobalizationInfo.FormatTime(fallbackTime, ActualFormat);
  962. // show the date, correctly formatted and watermarked
  963. TimeHintContent = string.Format(CultureInfo.InvariantCulture, TimeHintFormat, _timeHintDate);
  964. }
  965. else
  966. {
  967. // allow custom parsing using our pre parsed value.
  968. parsingArgs.Value = parsed;
  969. try
  970. {
  971. OnParsing(parsingArgs);
  972. // if parsing was successful, use that value
  973. if (parsingArgs.Handled)
  974. {
  975. parsed = parsingArgs.Value;
  976. }
  977. }
  978. catch (Exception)
  979. {
  980. // swallow any exception during custom parsing.
  981. }
  982. if (parsed.HasValue)
  983. {
  984. // we were able to parse, does the value fall within range?
  985. if ((parsed.Value.TimeOfDay > ActualMaximum.TimeOfDay) ||
  986. (parsed.Value.TimeOfDay < ActualMinimum.TimeOfDay))
  987. {
  988. // value does not fall within range.
  989. IsShowTimeHint = true;
  990. VisualStateManager.GoToState(this, InvalidTimeStateName, true);
  991. // there is a better fallback time available though
  992. fallbackTime = CoerceDate(parsed.Value);
  993. // the date that will be shown in the TimeHint.
  994. _timeHintDate = ActualTimeGlobalizationInfo.FormatTime(fallbackTime, ActualFormat);
  995. // show the date, correctly formatted and watermarked
  996. TimeHintContent = string.Format(CultureInfo.InvariantCulture, TimeHintFormat, _timeHintDate);
  997. }
  998. else
  999. {
  1000. // value falls within range
  1001. // the date that will be shown in the TimeHint.
  1002. _timeHintDate = ActualTimeGlobalizationInfo.FormatTime(parsed.Value, ActualFormat);
  1003. // show TimeHint if the formatted date does not exactly match input.
  1004. IsShowTimeHint = !Text.Text.Equals(_timeHintDate, StringComparison.OrdinalIgnoreCase);
  1005. VisualStateManager.GoToState(this, ValidTimeStateName, true);
  1006. TimeHintContent = _timeHintDate;
  1007. }
  1008. }
  1009. else
  1010. {
  1011. // parsed to a null
  1012. IsShowTimeHint = true;
  1013. // the date that will be shown in the TimeHint.
  1014. _timeHintDate = ActualTimeGlobalizationInfo.FormatTime(fallbackTime, ActualFormat);
  1015. VisualStateManager.GoToState(this, EmptyTimeStateName, true);
  1016. TimeHintContent = string.Format(CultureInfo.InvariantCulture, TimeHintFormat, _timeHintDate);
  1017. }
  1018. }
  1019. }
  1020. /// <summary>
  1021. /// Handles the Left Mouse Button Down event of the TimeHint.
  1022. /// </summary>
  1023. /// <param name="sender">The sender.</param>
  1024. /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/>
  1025. /// instance containing the event data.</param>
  1026. private void OnTimeHintMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  1027. {
  1028. IsShowTimeHint = false;
  1029. // will commit value in TimeHint, if possible, using the exact string
  1030. // that was used in the TimeHint.
  1031. ApplyValue(_timeHintDate);
  1032. }
  1033. /// <summary>
  1034. /// Provides handling for the KeyDown event.
  1035. /// </summary>
  1036. /// <param name="e">Key event args.</param>
  1037. /// <remarks>
  1038. /// Only support up and down arrow keys.
  1039. /// </remarks>
  1040. protected override void OnKeyDown(KeyEventArgs e)
  1041. {
  1042. if (IsEditable == false)
  1043. {
  1044. e.Handled = true;
  1045. return;
  1046. }
  1047. if (Text.Text != _lastParsedText)
  1048. {
  1049. if (e.Key == Key.Up || e.Key == Key.Down)
  1050. {
  1051. int caretPosition = Text.SelectionStart;
  1052. ApplyValue(Text.Text);
  1053. e.Handled = true;
  1054. if (caretPosition >= 0 && caretPosition < Text.Text.Length)
  1055. {
  1056. // there are situations where the caretposition
  1057. // is not correct. However, this is the 99% case.
  1058. Text.SelectionStart = caretPosition;
  1059. if (e.Key == Key.Up)
  1060. {
  1061. OnIncrement();
  1062. }
  1063. else
  1064. {
  1065. OnDecrement();
  1066. }
  1067. }
  1068. }
  1069. }
  1070. base.OnKeyDown(e);
  1071. }
  1072. /// <summary>
  1073. /// Provides handling for the GotFocus event.
  1074. /// </summary>
  1075. /// <param name="e">The data for the event.</param>
  1076. protected override void OnGotFocus(RoutedEventArgs e)
  1077. {
  1078. if (Interaction.AllowGotFocus(e))
  1079. {
  1080. DetermineHint();
  1081. SetValidSpinDirection();
  1082. Interaction.OnGotFocusBase();
  1083. base.OnGotFocus(e);
  1084. }
  1085. }
  1086. /// <summary>
  1087. /// Provides handling for the LostFocus event.
  1088. /// </summary>
  1089. /// <param name="e">The data for the event.</param>
  1090. protected override void OnLostFocus(RoutedEventArgs e)
  1091. {
  1092. if (Interaction.AllowLostFocus(e))
  1093. {
  1094. IsShowTimeHint = false;
  1095. SetValidSpinDirection();
  1096. Interaction.OnLostFocusBase();
  1097. base.OnLostFocus(e);
  1098. }
  1099. }
  1100. /// <summary>
  1101. /// Provides handling for the MouseEnter event.
  1102. /// </summary>
  1103. /// <param name="e">The data for the event.</param>
  1104. protected override void OnMouseEnter(MouseEventArgs e)
  1105. {
  1106. if (Interaction.AllowMouseEnter(e))
  1107. {
  1108. Interaction.OnMouseEnterBase();
  1109. base.OnMouseEnter(e);
  1110. }
  1111. }
  1112. /// <summary>
  1113. /// Provides handling for the MouseLeave event.
  1114. /// </summary>
  1115. /// <param name="e">The data for the event.</param>
  1116. protected override void OnMouseLeave(MouseEventArgs e)
  1117. {
  1118. if (Interaction.AllowMouseLeave(e))
  1119. {
  1120. Interaction.OnMouseLeaveBase();
  1121. base.OnMouseLeave(e);
  1122. }
  1123. }
  1124. /// <summary>
  1125. /// Provides handling for the MouseLeftButtonDown event.
  1126. /// </summary>
  1127. /// <param name="e">The data for the event.</param>
  1128. protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
  1129. {
  1130. if (Interaction.AllowMouseLeftButtonDown(e))
  1131. {
  1132. Interaction.OnMouseLeftButtonDownBase();
  1133. base.OnMouseLeave(e);
  1134. }
  1135. }
  1136. /// <summary>
  1137. /// Called before the MouseLeftButtonUp event occurs.
  1138. /// </summary>
  1139. /// <param name="e">The data for the event.</param>
  1140. protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
  1141. {
  1142. if (Interaction.AllowMouseLeftButtonUp(e))
  1143. {
  1144. Interaction.OnMouseLeftButtonUpBase();
  1145. base.OnMouseLeftButtonUp(e);
  1146. }
  1147. }
  1148. #region TextSelection and tabbing behavior
  1149. /// <summary>
  1150. /// Selects all text.
  1151. /// </summary>
  1152. protected override void SelectAllText()
  1153. {
  1154. // TimeUpDown only selects all text when coming in from a tab.
  1155. if (!_isIgnoreSelectionOfAllText)
  1156. {
  1157. base.SelectAllText();
  1158. }
  1159. }
  1160. /// <summary>
  1161. /// Event handler for Text template part's LostFocus event.
  1162. /// We use this event to compare current TextBox.Text with cached previous
  1163. /// value to decide whether user has typed in a new value.
  1164. /// </summary>
  1165. /// <param name="sender">The Text template part.</param>
  1166. /// <param name="e">Event args.</param>
  1167. private void OnTextLostFocus(object sender, RoutedEventArgs e)
  1168. {
  1169. _isIgnoreSelectionOfAllText = false;
  1170. SelectAllText();
  1171. _isIgnoreSelectionOfAllText = true;
  1172. }
  1173. #endregion TextSelection and tabbing behavior
  1174. /// <summary>
  1175. /// Update current visual state.
  1176. /// </summary>
  1177. /// <param name="useTransitions">True to use transitions when updating the visual state, false to
  1178. /// snap directly to the new visual state.</param>
  1179. internal override void UpdateVisualState(bool useTransitions)
  1180. {
  1181. if (!DesignerProperties.GetIsInDesignMode(this))
  1182. {
  1183. if (_timeHintExpandDirection == null || !IsShowTimeHint)
  1184. {
  1185. VisualStateManager.GoToState(this, TimeHintClosedStateName, useTransitions);
  1186. }
  1187. else
  1188. {
  1189. VisualStateManager.GoToState(this, _timeHintExpandDirection == ExpandDirection.Up ? TimeHintOpenedUpStateName : TimeHintOpenedDownStateName, useTransitions);
  1190. }
  1191. }
  1192. // handle common states
  1193. base.UpdateVisualState(useTransitions);
  1194. }
  1195. /// <summary>
  1196. /// Update the visual state of the control.
  1197. /// </summary>
  1198. /// <param name="useTransitions">
  1199. /// A value indicating whether to automatically generate transitions to
  1200. /// the new state, or instantly transition to the new state.
  1201. /// </param>
  1202. void IUpdateVisualState.UpdateVisualState(bool useTransitions)
  1203. {
  1204. UpdateVisualState(useTransitions);
  1205. }
  1206. /// <summary>
  1207. /// When implemented in a derived class, returns class-specific
  1208. /// <see cref="T:System.Windows.Automation.Peers.AutomationPeer"/> implementations
  1209. /// for the Silverlight automation infrastructure.
  1210. /// </summary>
  1211. /// <returns>
  1212. /// The class-specific <see cref="T:System.Windows.Automation.Peers.AutomationPeer"/>
  1213. /// subclass to return.
  1214. /// </returns>
  1215. protected override AutomationPeer OnCreateAutomationPeer()
  1216. {
  1217. return new TimeUpDownAutomationPeer(this);
  1218. }
  1219. #region ITimeInput Members
  1220. /// <summary>
  1221. /// Gets or sets the Value property.
  1222. /// </summary>
  1223. /// <value></value>
  1224. DateTime? ITimeInput.Value
  1225. {
  1226. get { return Value; }
  1227. set { Value = value; }
  1228. }
  1229. /// <summary>
  1230. /// Gets or sets the minimum time.
  1231. /// </summary>
  1232. /// <value>The minimum time.</value>
  1233. DateTime? ITimeInput.Minimum
  1234. {
  1235. get { return Minimum; }
  1236. set { Minimum = value; }
  1237. }
  1238. /// <summary>
  1239. /// Gets or sets the maximum time.
  1240. /// </summary>
  1241. /// <value>The maximum time.</value>
  1242. DateTime? ITimeInput.Maximum
  1243. {
  1244. get { return Maximum; }
  1245. set { Maximum = value; }
  1246. }
  1247. #endregion
  1248. }
  1249. }