在做开发的时候,经常会碰见设定时间日期的,本来silverlight库中有calendar控件,但是windows phone为了压缩空间砍掉了很多控件,calendar控件也自然没有了。不过好在silverlight toolkit包中提供了一个可以取代calendar的控件,比如datetimepicker,timepicker,这些控件放在手机上显然更合适,基本满足了大多数情况下的要求。下图展示的就是datetimepicker控件
话说回来,没有一样东西是万能的,但是偶尔也有一些例外情况而这些控件不能很好的解决,例如我要求实现带“天数,星期,小时”混杂在一起的选项设定时就不行了,为了用户体验的一致性,我们也希望选则页面能像自带的控件这样布局,于是改写就产生了。
其实通过查阅原来控件的源代码可以知道大致是如何实现的,这其中用到一个最主要的控件是LoopingSelector,详细介绍可以看这里:http://www.cnblogs.com/holyenzou/archive/2011/09/13/2174918.html
其实三个类就能搞定这个控件,其中两个类貌似是通用的,格式也是固定的,包括一个抽象类和一个子类
抽象类如下:
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls.Primitives; namespace _365Plus.ItemControls { // abstract the reusable code in a base class // this will allow us to concentrate on the specifics when implementing deriving looping data source classes public abstract class LoopingDataSourceBase : ILoopingSelectorDataSource { private object selectedItem; #region ILoopingSelectorDataSource Members public abstract object GetNext(object relativeTo); public abstract object GetPrevious(object relativeTo); public object SelectedItem { get { return this.selectedItem; } set { // this will use the Equals method if it is overridden for the data source item class if (!object.Equals(this.selectedItem, value)) { // save the previously selected item so that we can use it // to construct the event arguments for the SelectionChanged event object previousSelectedItem = this.selectedItem; this.selectedItem = value; // fire the SelectionChanged event this.OnSelectionChanged(previousSelectedItem, this.selectedItem); } } } public event EventHandler<SelectionChangedEventArgs> SelectionChanged; protected virtual void OnSelectionChanged(object oldSelectedItem, object newSelectedItem) { EventHandler<SelectionChangedEventArgs> handler = this.SelectionChanged; if (handler != null) { handler(this, new SelectionChangedEventArgs(new object[] { oldSelectedItem }, new object[] { newSelectedItem })); } } #endregion } }
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace _365Plus.ItemControls { public class IntLoopingDataSource : LoopingDataSourceBase { private int minValue; private int maxValue; private int increment; public IntLoopingDataSource() { this.MaxValue = 10; this.MinValue = 0; this.Increment = 1; this.SelectedItem = 0; } public int MinValue { get { return this.minValue; } set { if (value >= this.MaxValue) { throw new ArgumentOutOfRangeException("MinValue", "MinValue cannot be equal or greater than MaxValue"); } this.minValue = value; } } public int MaxValue { get { return this.maxValue; } set { if (value <= this.MinValue) { throw new ArgumentOutOfRangeException("MaxValue", "MaxValue cannot be equal or lower than MinValue"); } this.maxValue = value; } } public int Increment { get { return this.increment; } set { if (value < 1) { throw new ArgumentOutOfRangeException("Increment", "Increment cannot be less than or equal to zero"); } this.increment = value; } } public override object GetNext(object relativeTo) { int nextValue = (int)relativeTo + this.Increment; if (nextValue > this.MaxValue) { nextValue = this.MinValue; } return nextValue; } public override object GetPrevious(object relativeTo) { int prevValue = (int)relativeTo - this.Increment; if (prevValue < this.MinValue) { prevValue = this.MaxValue; } return prevValue; } } }
例如我这里就实现了自定义“天数-小时-分钟”的控件
前台xaml代码:
<UserControl x:Class="_365Plus.ItemControls.DateTimeNumberPicker" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" d:DesignHeight="480" d:DesignWidth="480" xmlns:toolkitPrimitives="clr-namespace:Microsoft.Phone.Controls.Primitives;assembly=Microsoft.Phone.Controls.Toolkit"> <Grid x:Name="LayoutRoot"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <toolkitPrimitives:LoopingSelector FontSize="48" HorizontalAlignment="Left" ItemMargin="20,9,9,20" ItemSize="100,100" Grid.Column="0" Name="selectorDay" Width="90" /> <toolkitPrimitives:LoopingSelector FontSize="48" HorizontalAlignment="Left" ItemMargin="20,9,9,20" ItemSize="100,100" Grid.Column="1" Name="selectorHour" Width="90" /> <toolkitPrimitives:LoopingSelector FontSize="48" HorizontalAlignment="Left" ItemMargin="20,9,9,20" ItemSize="100,100" Grid.Column="2" Name="selectorMinute" Width="90" /> <TextBlock FontSize="24" HorizontalAlignment="Right" Grid.Column="0" Text="天" TextWrapping="Wrap" VerticalAlignment="Center" Width="50" /> <TextBlock FontSize="24" HorizontalAlignment="Right" Grid.Column="1" Text="小时" TextWrapping="Wrap" VerticalAlignment="Center" Width="50" /> <TextBlock FontSize="24" HorizontalAlignment="Right" Grid.Column="2" Text="分钟" TextWrapping="Wrap" VerticalAlignment="Center" Width="50" /> </Grid> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls.Primitives; namespace _365Plus.ItemControls { public partial class DateTimeNumberPicker : UserControl { /// <summary> /// 选择天、时、分的控件 /// </summary> public DateTimeNumberPicker() { InitializeComponent(); selectorDay.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 30, SelectedItem = 0 }; selectorHour.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 23, SelectedItem = 0 }; selectorMinute.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 59, SelectedItem = 0 }; selectorDay.IsExpandedChanged += OnSelectorIsExpandedChanged; selectorHour.IsExpandedChanged += OnSelectorIsExpandedChanged; selectorMinute.IsExpandedChanged += OnSelectorIsExpandedChanged; _selectorDay = selectorDay; _selectorHour = selectorHour; _selectorMinute = selectorMinute; } public static readonly DependencyProperty ColorProperty = DependencyProperty.RegisterAttached("MaxDay", typeof(int), typeof(DateTimeNumberPicker), new PropertyMetadata((int)30, DateTimeNumberPicker.OnMaxDayPropertyChanged)); private static void OnMaxDayPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { DateTimeNumberPicker control = d as DateTimeNumberPicker; if (control != null ) { control.selectorDay.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = (int)e.NewValue, SelectedItem = 0 }; } } public int MaxDay { get { return (int)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } } private void OnSelectorIsExpandedChanged(object sender, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { // Ensure that only one selector is expanded at a time selectorDay.IsExpanded = (sender == selectorDay); selectorHour.IsExpanded = (sender == selectorHour); selectorMinute.IsExpanded = (sender == selectorMinute); } } private LoopingSelector _selectorDay; public LoopingSelector SelectorDay { get { return _selectorDay; } //set { _selectorDay = value; } } private LoopingSelector _selectorHour; public LoopingSelector SelectorHour { get { return _selectorHour; } //set { _selectorHour = value; } } private LoopingSelector _selectorMinute; public LoopingSelector SelectorMinute { get { return _selectorMinute; } //set { _selectorMinute = value; } } } }
selectorDay.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 30, SelectedItem = 0 }; selectorHour.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 23, SelectedItem = 0 }; selectorMinute.DataSource = new IntLoopingDataSource() { MinValue = 0, MaxValue = 59, SelectedItem = 0 };
通过这种方法,可以做出很多类似的控件,不过为了布局美观,最好不要超过三栏。