在做開發的時候,經常會碰見設定時間日期的,本來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>
後台cs代碼:
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 };
實現效果如下
通過這種方法,可以做出很多類似的控制項,不過為了布局美觀,最好不要超過三欄。