其实在上一篇自定义datetimepicker中已经提到了LoopingSelector,这是一个带有滚动效果的控件,要比list那种控件美观的多,用户体验也要好不少。微软自带的控件中是没有这个控件的,在silverlight中才有,下载最新的toolkit,然后引用
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls.Primitives;assembly=Microsoft.Phone.Controls.Toolkit"
<toolkit:LoopingSelector Grid.Column="0" x:Name="selectorDouble" ItemMargin="2,3,3,2" ItemSize="100,100" FontSize="33"/>
不过很多时候,我发现loopingselector显示的是简单的整型数字,日期控件中就是如此,显然这是远远不够的,我们应该能让他显示浮点型,字符串类型,甚至还包括图片,效果就像下面一样
这些资料其实网上都有说,WP7 LoopingSelector in depth给出了详细的实现过程,具体看这里:http://windowsphonegeek.com/articles/WP7-LoopingSelector-in-depth--Part3-Advanced-data-binding
针对我目前所做的项目,我只需要显示农历中的月份和天数即可,参考了一下作者的源码,发现实现很简单,甚至不需要像之前自定义datetimepicker控件那样弄出两个单独的类,这些类本可以放在一起,甚至可以针对任意类型,所以实现浮点型还是字符串,图片等等不在话下。
最关键的是下面两个函数,其他一切都好办
// 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 } public class ListLoopingDataSource<T> : LoopingDataSourceBase { private LinkedList<T> linkedList; private List<LinkedListNode<T>> sortedList; private IComparer<T> comparer; private NodeComparer nodeComparer; public ListLoopingDataSource() { } public IEnumerable<T> Items { get { return this.linkedList; } set { this.SetItemCollection(value); } } private void SetItemCollection(IEnumerable<T> collection) { this.linkedList = new LinkedList<T>(collection); this.sortedList = new List<LinkedListNode<T>>(this.linkedList.Count); // initialize the linked list with items from the collections LinkedListNode<T> currentNode = this.linkedList.First; while (currentNode != null) { this.sortedList.Add(currentNode); currentNode = currentNode.Next; } IComparer<T> comparer = this.comparer; if (comparer == null) { // if no comparer is set use the default one if available if (typeof(IComparable<T>).IsAssignableFrom(typeof(T))) { comparer = Comparer<T>.Default; } else { throw new InvalidOperationException("There is no default comparer for this type of item. You must set one."); } } this.nodeComparer = new NodeComparer(comparer); this.sortedList.Sort(this.nodeComparer); } public IComparer<T> Comparer { get { return this.comparer; } set { this.comparer = value; } } public override object GetNext(object relativeTo) { // find the index of the node using binary search in the sorted list int index = this.sortedList.BinarySearch(new LinkedListNode<T>((T)relativeTo), this.nodeComparer); if (index < 0) { return default(T); } // get the actual node from the linked list using the index LinkedListNode<T> node = this.sortedList[index].Next; if (node == null) { // if there is no next node get the first one node = this.linkedList.First; } return node.Value; } public override object GetPrevious(object relativeTo) { int index = this.sortedList.BinarySearch(new LinkedListNode<T>((T)relativeTo), this.nodeComparer); if (index < 0) { return default(T); } LinkedListNode<T> node = this.sortedList[index].Previous; if (node == null) { // if there is no previous node get the last one node = this.linkedList.Last; } return node.Value; } private class NodeComparer : IComparer<LinkedListNode<T>> { private IComparer<T> comparer; public NodeComparer(IComparer<T> comparer) { this.comparer = comparer; } #region IComparer<LinkedListNode<T>> Members public int Compare(LinkedListNode<T> x, LinkedListNode<T> y) { return this.comparer.Compare(x.Value, y.Value); } #endregion }
然后我们就可以在页面初始化的时候给loopingselector设定数据源,然后绑定一下就OK,在作者的例子中给出了一次全部定义和每次添加一个的方法
public MainPage() { InitializeComponent(); string[] cityNames = new string[] { "London", "New York", "Barcelona", "Madrid", "Berlin", "Bonn", "Munich", "Las Vegas" }; this.selectorString.DataSource = new ListLoopingDataSource<string>() { Items = cityNames, SelectedItem = "Madrid" }; List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; this.selectorInt.DataSource = new ListLoopingDataSource<int>() { Items = numbers, SelectedItem = 5 }; List<DateTime> dates = new List<DateTime>(); DateTime selectedDate = DateTime.Now; dates.Add(selectedDate); dates.Add(DateTime.Now.AddMonths(1)); dates.Add(DateTime.Now.AddMonths(2)); dates.Add(DateTime.Now.AddMonths(3)); dates.Add(DateTime.Now.AddMonths(4)); dates.Add(DateTime.Now.AddMonths(5)); dates.Add(DateTime.Now.AddMonths(6)); this.selectorDateTime.DataSource = new ListLoopingDataSource<DateTime>() { Items = dates, SelectedItem = selectedDate }; List<double> doubleNumbers = new List<double> { 1.1, 2.15, 3.66, 4.457, 5.036, 6.7, 7.4, 8.54, 9.11 }; this.selectorDouble.DataSource = new ListLoopingDataSource<double>() { Items = doubleNumbers, SelectedItem = 6.7 }; }
最终实现的效果如下
其实我觉得不需要做成用户控件,干脆做成页面比较方便,toolkit中自带的datetimepicker其实都是一个页面,与其做个控件,然后再做个页面上面放这个控件,真不如一步到位实现。