わんくま東京勉強会 #64 アフターフォロー (6) : ComboBox の代替 - LoopingSelector
日付や時刻と云った選択肢がループする様なケースでは、 ListPicker の代わりに LoopingSelector を利用する事が出来ます。
今回紹介するコントロールも全て Toolkit に含まれるコントロールのため、使用する際は Toolkit が必要になります。
DatePicker / TimePicker
よく使われるであろう日付選択と時刻選択については、それぞれ専用のコントロールが用意されています。
このコントロールを利用する事で、全画面での数値選択の UI を含めて利用可能です。
Custom LoopingSelector
日付や時刻以外にも、選択肢用のデータクラスと表示用のテンプレートを用意する事で、任意のデータを LoopingSelector で表示する事が可能です。
データ格納用のジェネリッククラス
まず、以下の様なクラスを用意します。
(これらのクラスをひとつずつ用意しておけば使い回しが可能です)
- LoopingDataSourceBase.cs
using System; using System.Windows.Controls; using Microsoft.Phone.Controls.Primitives; namespace WankumaTokyo64Sample.Controls { 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 { if (!object.Equals(this.selectedItem, value)) { object previousSelectedItem = this.selectedItem; this.selectedItem = value; 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 } }
- ListLoopingDataSource.cs
using System; using System.Collections.Generic; namespace WankumaTokyo64Sample.Controls { 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); 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 (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) { int index = this.sortedList.BinarySearch(new LinkedListNode<T>((T)relativeTo), this.nodeComparer); if (index < 0) { return default(T); } LinkedListNode<T> node = this.sortedList[index].Next; if (node == null) { 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) { 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 } } }
画面
XAML で見た目を設定します。
- LoopingSelectorPage.xaml
<toolspr:LoopingSelector x:Name="ObjectSelector" ItemSize="335,128"> <toolspr:LoopingSelector.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="72" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Image Grid.Column="0" Width="64" Height="64" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding ImagePath}" /> <Grid Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}" /> <tools:WrapPanel Grid.Row="1" VerticalAlignment="Center"> <TextBlock Text="{Binding StartDate, StringFormat=MM/dd}" Style="{StaticResource PhoneTextSmallStyle}" /> <TextBlock Text="〜" Style="{StaticResource PhoneTextSmallStyle}" /> <TextBlock Text="{Binding EndDate, StringFormat=MM/dd}" Style="{StaticResource PhoneTextSmallStyle}" /> </tools:WrapPanel> </Grid> </Grid> </DataTemplate> </toolspr:LoopingSelector.ItemTemplate> </toolspr:LoopingSelector>
データソースの設定
コードからデータソースを設定します。
- LoopingSelectorPage.xaml.cs
var items = new List<ObjectLoopingSelectorSampleItem> { new ObjectLoopingSelectorSampleItem { Name = @"おひつじ座", StartDate = new DateTime(2011, 3, 21), EndDate = new DateTime(2011, 4, 19), ImagePath = "/images/seiza1.png" }, new ObjectLoopingSelectorSampleItem { Name = @"おうし座", StartDate = new DateTime(2011, 4, 20), EndDate = new DateTime(2011, 5, 20), ImagePath = "/images/seiza2.png" }, new ObjectLoopingSelectorSampleItem { Name = @"ふたご座", StartDate = new DateTime(2011, 5, 21), EndDate = new DateTime(2011, 6, 21), ImagePath = "/images/seiza3.png" }, new ObjectLoopingSelectorSampleItem { Name = @"かに座", StartDate = new DateTime(2011, 6, 22), EndDate = new DateTime(2011, 7, 22), ImagePath = "/images/seiza4.png" }, new ObjectLoopingSelectorSampleItem { Name = @"しし座", StartDate = new DateTime(2011, 7, 23), EndDate = new DateTime(2011, 8, 22), ImagePath = "/images/seiza5.png" }, new ObjectLoopingSelectorSampleItem { Name = @"おとめ座", StartDate = new DateTime(2011, 8, 23), EndDate = new DateTime(2011, 9, 22), ImagePath = "/images/seiza6.png" }, new ObjectLoopingSelectorSampleItem { Name = @"てんびん座", StartDate = new DateTime(2011, 9, 23), EndDate = new DateTime(2011, 10, 23), ImagePath = "/images/seiza7.png" }, new ObjectLoopingSelectorSampleItem { Name = @"さそり座", StartDate = new DateTime(2011, 10, 24), EndDate = new DateTime(2011, 11, 22), ImagePath = "/images/seiza8.png" }, new ObjectLoopingSelectorSampleItem { Name = @"いて座", StartDate = new DateTime(2011, 11, 23), EndDate = new DateTime(2011, 12, 21), ImagePath = "/images/seiza9.png" }, new ObjectLoopingSelectorSampleItem { Name = @"やぎ座", StartDate = new DateTime(2011, 12, 22), EndDate = new DateTime(2012, 1, 20), ImagePath = "/images/seiza10.png" }, new ObjectLoopingSelectorSampleItem { Name = @"みずがめ座", StartDate = new DateTime(2012, 1, 21), EndDate = new DateTime(2012, 2, 18), ImagePath = "/images/seiza11.png" }, new ObjectLoopingSelectorSampleItem { Name = @"うお座", StartDate = new DateTime(2012, 2, 19), EndDate = new DateTime(2012, 3, 20), ImagePath = "/images/seiza12.png" } }; ObjectSelector.DataSource = new ListLoopingDataSource<ObjectLoopingSelectorSampleItem> { Items = items, SelectedItem = items[0] };