• 前言

     Windows Phone开发过程中不可避免的就是和集合数据打交道,如果之前做过WP App的开发的话,相信你已经看过了各种集合控件的使用、扩展和自定义。这些个内容在这篇博客里都没有,那么我们今天说点儿什么呢。当然也还是围绕WP的集合控件,要不然就和本文的题目不相符了,这篇博客主要讲集合控件的一些基础知识和在使用它们的过程中遇到的种种问题。WP中总共有三种集合控件,分别是ItemsControl、ListBox、LongListSelector。虽然都是集合控件,但它们的出场率绝对有着天壤之别,那么就一一说说它们的异同吧。

  1. ItemsControl

      相信ItemsControl应该算是出场率最低的集合控件了,以至于很多人都不知道有这么个控件的存在。这也不足为其,因为在各大官方非官方的Data Binding的例子中都见不到他的身影,大大降低了直接Copy Paste的概率。当然不能说那些个写Demo的人偏心,确实是由于ItemsControl本身的轻量级和功能简单造成的。

     先来看看ItemsControl的代码。

  1 // Summary:
  2     //     Represents a control that can be used to present a collection of items.
  3     [ContentProperty("Items", true)]
  4     public class ItemsControl : Control
  5     {
  6         // Summary:
  7         //     Identifies the System.Windows.Controls.ItemsControl.DisplayMemberPath dependency
  8         //     property.
  9         //
 10         // Returns:
 11         //     The identifier for the System.Windows.Controls.ItemsControl.DisplayMemberPath
 12         //     dependency property.
 13         public static readonly DependencyProperty DisplayMemberPathProperty;
 14         //
 15         // Summary:
 16         //     Identifies the System.Windows.Controls.ItemsControl.ItemsPanel dependency
 17         //     property.
 18         //
 19         // Returns:
 20         //     The identifier for the System.Windows.Controls.ItemsControl.ItemsPanel dependency
 21         //     property.
 22         public static readonly DependencyProperty ItemsPanelProperty;
 23         //
 24         // Summary:
 25         //     Identifies the System.Windows.Controls.ItemsControl.ItemsSource dependency
 26         //     property.
 27         //
 28         // Returns:
 29         //     The identifier for the System.Windows.Controls.ItemsControl.ItemsSource dependency
 30         //     property.
 31         public static readonly DependencyProperty ItemsSourceProperty;
 32         //
 33         // Summary:
 34         //     Identifies the System.Windows.Controls.ItemsControl.ItemTemplate dependency
 35         //     property.
 36         //
 37         // Returns:
 38         //     The identifier for the System.Windows.Controls.ItemsControl.ItemTemplate
 39         //     dependency property.
 40         public static readonly DependencyProperty ItemTemplateProperty;
 41 
 42         // Summary:
 43         //     Initializes a new instance of the System.Windows.Controls.ItemsControl class.
 44         public ItemsControl();
 45 
 46         // Summary:
 47         //     Gets or sets the name or path of the property that is displayed for each
 48         //     data item.
 49         //
 50         // Returns:
 51         //     The name or path of the property that is displayed for each the data item
 52         //     in the control. The default is an empty string ("").
 53         public string DisplayMemberPath { get; set; }
 54         //
 55         // Summary:
 56         //     Gets the System.Windows.Controls.ItemContainerGenerator associated with this
 57         //     System.Windows.Controls.ItemsControl.
 58         //
 59         // Returns:
 60         //     The System.Windows.Controls.ItemContainerGenerator associated with this System.Windows.Controls.ItemsControl.
 61         public ItemContainerGenerator ItemContainerGenerator { get; }
 62         //
 63         // Summary:
 64         //     Gets the collection used to generate the content of the control.
 65         //
 66         // Returns:
 67         //     The collection that is used to generate the content of the control, if it
 68         //     exists; otherwise, null. The default is an empty collection.
 69         public ItemCollection Items { get; }
 70         //
 71         // Summary:
 72         //     Gets or sets the template that defines the panel that controls the layout
 73         //     of items.
 74         //
 75         // Returns:
 76         //     An System.Windows.Controls.ItemsPanelTemplate that defines the panel to use
 77         //     for the layout of the items. The default value for the System.Windows.Controls.ItemsControl
 78         //     is an System.Windows.Controls.ItemsPanelTemplate that specifies a System.Windows.Controls.StackPanel.
 79         public ItemsPanelTemplate ItemsPanel { get; set; }
 80         //
 81         // Summary:
 82         //     Gets or sets a collection used to generate the content of the System.Windows.Controls.ItemsControl.
 83         //
 84         // Returns:
 85         //     The object that is used to generate the content of the System.Windows.Controls.ItemsControl.
 86         //     The default is null.
 87         public IEnumerable ItemsSource { get; set; }
 88         //
 89         // Summary:
 90         //     Gets or sets the System.Windows.DataTemplate used to display each item.
 91         //
 92         // Returns:
 93         //     The template that specifies the visualization of the data objects. The default
 94         //     is null.
 95         public DataTemplate ItemTemplate { get; set; }
 96 
 97         // Summary:
 98         //     Undoes the effects of the System.Windows.Controls.ItemsControl.PrepareContainerForItemOverride(System.Windows.DependencyObject,System.Object)
 99         //     method.
100         //
101         // Parameters:
102         //   element:
103         //     The container element.
104         //
105         //   item:
106         //     The item.
107         protected virtual void ClearContainerForItemOverride(DependencyObject element, object item);
108         //
109         // Summary:
110         //     Creates or identifies the element that is used to display the given item.
111         //
112         // Returns:
113         //     The element that is used to display the given item.
114         protected virtual DependencyObject GetContainerForItemOverride();
115         //
116         // Summary:
117         //     Returns the System.Windows.Controls.ItemsControl that the specified element
118         //     hosts items for.
119         //
120         // Parameters:
121         //   element:
122         //     The host element.
123         //
124         // Returns:
125         //     The System.Windows.Controls.ItemsControl that the specified element hosts
126         //     items for, or null.
127         public static ItemsControl GetItemsOwner(DependencyObject element);
128         //
129         // Summary:
130         //     Determines if the specified item is (or is eligible to be) its own container.
131         //
132         // Parameters:
133         //   item:
134         //     The item to check.
135         //
136         // Returns:
137         //     true if the item is (or is eligible to be) its own container; otherwise,
138         //     false.
139         protected virtual bool IsItemItsOwnContainerOverride(object item);
140         //
141         // Summary:
142         //     Returns the System.Windows.Controls.ItemsControl that owns the specified
143         //     container element.
144         //
145         // Parameters:
146         //   container:
147         //     The container element to return the System.Windows.Controls.ItemsControl
148         //     for.
149         //
150         // Returns:
151         //     The System.Windows.Controls.ItemsControl that owns the specified container
152         //     element; otherwise, null. The System.Windows.Controls.ItemsControl.ItemsControlFromItemContainer(System.Windows.DependencyObject)
153         //     method returns null if container is not a System.Windows.UIElement or the
154         //     parent is not an System.Windows.Controls.ItemsControl.
155         public static ItemsControl ItemsControlFromItemContainer(DependencyObject container);
156         //
157         // Summary:
158         //     Called when the value of the System.Windows.Controls.ItemsControl.Items property
159         //     changes.
160         //
161         // Parameters:
162         //   e:
163         //     A System.Collections.Specialized.NotifyCollectionChangedEventArgs that contains
164         //     the event data
165         protected virtual void OnItemsChanged(NotifyCollectionChangedEventArgs e);
166         //
167         // Summary:
168         //     Prepares the specified element to display the specified item.
169         //
170         // Parameters:
171         //   element:
172         //     The element used to display the specified item.
173         //
174         //   item:
175         //     The item to display.
176         protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item);
177     }
View Code

 

     没有眼花缭乱的Template,也没有千变万化的VisualStateGroup,感觉完全就是一个基类的命,确实他也是后面要提到的ListBox的基类。但存在即合理嘛,虽然他没有UI虚拟化的功能,但已其轻量的优势完全可以胜任既定的小规模的数据绑定的工作,如果在配合ScrollViewer使用,效果完全不输给后面的两种集合控件。

     Note: ItemsControl 是不具有UI虚拟化功能的,这就意味着您绑定的Data会一次性的全Load出来,您可以在ItemTemplate的任意控件上加个Loaded事件,看看output就会看到您所绑定的数据一个一个的被加载出来,即使他们没有显示在屏幕上也是预先加载到了内存中,这绝对不是一个小的开销,所以说ItemsControl只适合做一些小量的数据绑定工作。

      2.  ListBox

      ListBox绝对算是WP中的集合控件的主角,在无数的下拉刷新、滑动刷新、控件扩展中绝对是少补了ListBox,在大数据量的数据绑定中ListBox绝对是出尽了风头,配合VirtualizingStackPanel使用,UI虚拟化绝对让你的应用变成360°无死角顺畅。但这些都不是我们要说的内容。

      还是先来看一下ListBox的代码。

 

  1 // Summary:
  2     //     Contains a list of selectable items.
  3     [TemplatePart(Name = "ScrollViewer", Type = typeof(ScrollViewer))]
  4     [TemplateVisualState(Name = "InvalidFocused", GroupName = "ValidationStates")]
  5     [TemplateVisualState(Name = "InvalidUnfocused", GroupName = "ValidationStates")]
  6     [TemplateVisualState(Name = "Valid", GroupName = "ValidationStates")]
  7     public class ListBox : Selector
  8     {
  9         // Summary:
 10         //     Identifies the IsSelectionActive dependency property.
 11         //
 12         // Returns:
 13         //     The identifier for the IsSelectionActive dependency property.
 14         public static readonly DependencyProperty IsSelectionActiveProperty;
 15         //
 16         // Summary:
 17         //     Identifies the System.Windows.Controls.ListBox.ItemContainerStyle dependency
 18         //     property.
 19         //
 20         // Returns:
 21         //     The identifier for the System.Windows.Controls.ListBox.ItemContainerStyle
 22         //     dependency property.
 23         public static readonly DependencyProperty ItemContainerStyleProperty;
 24         //
 25         // Summary:
 26         //     Identifies the System.Windows.Controls.ListBox.SelectionMode dependency property.
 27         //
 28         // Returns:
 29         //     The identifier for the System.Windows.Controls.ListBox.SelectionMode dependency
 30         //     property.
 31         public static readonly DependencyProperty SelectionModeProperty;
 32 
 33         // Summary:
 34         //     Initializes a new instance of the System.Windows.Controls.ListBox class.
 35         public ListBox();
 36 
 37         // Summary:
 38         //     Gets or sets the style that is used when rendering the item containers.
 39         //
 40         // Returns:
 41         //     The style applied to the item containers. The default is null.
 42         public Style ItemContainerStyle { get; set; }
 43         //
 44         // Summary:
 45         //     Gets the list of currently selected items for the System.Windows.Controls.ListBox
 46         //     control.
 47         //
 48         // Returns:
 49         //     The list of currently selected items for the System.Windows.Controls.ListBox.
 50         public IList SelectedItems { get; }
 51         //
 52         // Summary:
 53         //     Gets or sets the selection behavior for the System.Windows.Controls.ListBox
 54         //     control.
 55         //
 56         // Returns:
 57         //     One of the System.Windows.Controls.SelectionMode values.
 58         public SelectionMode SelectionMode { get; set; }
 59 
 60         // Summary:
 61         //     Creates or identifies the element used to display a specified item.
 62         //
 63         // Returns:
 64         //     A System.Windows.Controls.ListBoxItem corresponding to a specified item.
 65         protected override DependencyObject GetContainerForItemOverride();
 66         //
 67         // Summary:
 68         //     Determines if the specified item is (or is eligible to be) its own item container.
 69         //
 70         // Parameters:
 71         //   item:
 72         //     The specified item.
 73         //
 74         // Returns:
 75         //     true if the item is its own item container; otherwise, false.
 76         protected override bool IsItemItsOwnContainerOverride(object item);
 77         //
 78         // Summary:
 79         //     Builds the visual tree for the System.Windows.Controls.ListBox control when
 80         //     a new template is applied.
 81         public override void OnApplyTemplate();
 82         //
 83         // Summary:
 84         //     Returns a System.Windows.Automation.Peers.ListBoxAutomationPeer for the Windows Phone
 85         //     automation infrastructure.
 86         //
 87         // Returns:
 88         //     A System.Windows.Automation.Peers.ListBoxAutomationPeer for the System.Windows.Controls.ListBox
 89         //     object.
 90         protected override AutomationPeer OnCreateAutomationPeer();
 91         //
 92         // Summary:
 93         //     Provides handling for the System.Windows.UIElement.GotFocus event.
 94         //
 95         // Parameters:
 96         //   e:
 97         //     The event data.
 98         protected override void OnGotFocus(RoutedEventArgs e);
 99         //
100         // Summary:
101         //     Provides handling for the System.Windows.Controls.ItemContainerGenerator.ItemsChanged
102         //     event.
103         //
104         // Parameters:
105         //   e:
106         //     A System.Collections.Specialized.NotifyCollectionChangedEventArgs that contains
107         //     the event data.
108         protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e);
109         //
110         // Summary:
111         //     Provides handling for the System.Windows.UIElement.KeyDown event that occurs
112         //     when a key is pressed while the control has focus.
113         //
114         // Parameters:
115         //   e:
116         //     The event data.
117         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Straightforward switch-based key handling method that barely triggers the warning")]
118         protected override void OnKeyDown(KeyEventArgs e);
119         //
120         // Summary:
121         //     Provides handling for the System.Windows.UIElement.LostFocus event.
122         //
123         // Parameters:
124         //   e:
125         //     The event data.
126         protected override void OnLostFocus(RoutedEventArgs e);
127         //
128         // Summary:
129         //     Causes the object to scroll into view.
130         //
131         // Parameters:
132         //   item:
133         //     The object to scroll.
134         public void ScrollIntoView(object item);
135         //
136         // Summary:
137         //     Selects all the items in the System.Windows.Controls.ListBox.
138         //
139         // Exceptions:
140         //   System.NotSupportedException:
141         //     System.Windows.Controls.ListBox.SelectionMode is set to System.Windows.Controls.SelectionMode.Single
142         public void SelectAll();
143     }
View Code

 

      相比ItemsControl的极简,ListBox就要热闹多了。首先在Template中您就能看到大大的ScrollViewer的存在,原来ListBox的上下滑动就是通过ScrollViewer实现的。之前我们有个功能是要在用户上下滑动的时候触发一个事件,但如何判断ListBox控件是否在滑动呢?我们可以先来看看ScrollViewer的模板。

 1 // Summary:
 2     //     Represents a scrollable area that can contain other visible elements.
 3     [TemplatePart(Name = "HorizontalScrollBar", Type = typeof(ScrollBar))]
 4     [TemplatePart(Name = "ScrollContentPresenter", Type = typeof(ScrollContentPresenter))]
 5     [TemplatePart(Name = "VerticalScrollBar", Type = typeof(ScrollBar))]
 6     [TemplateVisualState(Name = "CompressionBottom", GroupName = "VerticalCompressionStates")]
 7     [TemplateVisualState(Name = "CompressionLeft", GroupName = "HorizontalCompressionStates")]
 8     [TemplateVisualState(Name = "CompressionRight", GroupName = "HorizontalCompressionStates")]
 9     [TemplateVisualState(Name = "CompressionTop", GroupName = "VerticalCompressionStates")]
10     [TemplateVisualState(Name = "NoHorizontalCompression", GroupName = "HorizontalCompressionStates")]
11     [TemplateVisualState(Name = "NotScrolling", GroupName = "ScrollStates")]
12     [TemplateVisualState(Name = "NoVerticalCompression", GroupName = "VerticalCompressionStates")]
13     [TemplateVisualState(Name = "Scrolling", GroupName = "ScrollStates")]
14     public sealed class ScrollViewer : ContentControl
View Code

      似乎我们看到了ScrollingNotScrolling这两个VisualState,既然有这两个状态那我们就可以通过hook VisualStateGroupCurrentStateChanging事件来实现这个功能。

 1 // Summary:
 2     //     Contains mutually exclusive System.Windows.VisualState objects and System.Windows.VisualTransition
 3     //     objects that are used to go from one state to another.
 4     [ContentProperty("States", true)]
 5     public sealed class VisualStateGroup : DependencyObject
 6     {
 7         // Summary:
 8         //     Initializes a new instance of the System.Windows.VisualStateGroup class.
 9         public VisualStateGroup();
10 
11         // Summary:
12         //     Gets the most recently set System.Windows.VisualState from a successful call
13         //     to the System.Windows.VisualStateManager.GoToState(System.Windows.Controls.Control,System.String,System.Boolean)
14         //     method.
15         //
16         // Returns:
17         //     The most recently set System.Windows.VisualState from a successful call to
18         //     the System.Windows.VisualStateManager.GoToState(System.Windows.Controls.Control,System.String,System.Boolean)
19         //     method.
20         public VisualState CurrentState { get; }
21         //
22         // Summary:
23         //     Gets the name of the System.Windows.VisualStateGroup.
24         //
25         // Returns:
26         //     The name of the System.Windows.VisualStateGroup.
27         public string Name { get; }
28         //
29         // Summary:
30         //     Gets the collection of mutually exclusive System.Windows.VisualState objects.
31         //
32         // Returns:
33         //     The collection of mutually exclusive System.Windows.VisualState objects.
34         public IList States { get; }
35         //
36         // Summary:
37         //     Gets the collection of System.Windows.VisualTransition objects.
38         //
39         // Returns:
40         //     The collection of System.Windows.VisualTransition objects.
41         public IList Transitions { get; }
42 
43         // Summary:
44         //     Occurs after a control transitions into a different state.
45         public event EventHandler<VisualStateChangedEventArgs> CurrentStateChanged;
46         //
47         // Summary:
48         //     Occurs when a control begins transitioning into a different state.
49 
50         public event EventHandler<VisualStateChangedEventArgs> CurrentStateChanging;
51     }
52 public static void HookScrollingEvents(DependencyObject listBox)
53         {
54             if (DesignerProperties.IsInDesignTool) return;
55             var scrollViewerVisualStateGroup = GetScrollStates(listBox);
56             if (scrollViewerVisualStateGroup != null)
57             {
58                 scrollViewerVisualStateGroup.CurrentStateChanging += ScrollingStateChanging;
59             }
60         }
61 
62 static VisualStateGroup GetScrollStates(DependencyObject root)
63         {
64             try
65             {
66                 var scrollViewer = FindItem<ScrollViewer>(root, elt => elt.Name == "ScrollViewer");
67                 if (scrollViewer == null) return null;
68                 var element = VisualTreeHelper.GetChild(scrollViewer, 0) as FrameworkElement;
69                 return element == null ? null : VisualStateManager.GetVisualStateGroups(element).Cast<VisualStateGroup>().SingleOrDefault(elt => elt.Name == "ScrollStates");
70             }
71             catch { }
72             return null;
73         }
74 
75 public static T FindItem<T>(DependencyObject parent, Func<T, bool> filter) where T : DependencyObject
76         {
77             var childCount = VisualTreeHelper.GetChildrenCount(parent);
78             for (var i = 0; i < childCount; i++)
79             {
80                 var elt = VisualTreeHelper.GetChild(parent, i);
81                 if (elt is T && filter((T)elt)) return (T)elt;
82                 var result = FindItem(elt, filter);
83                 if (result != null) return result;
84             }
85             return null;
86         }
87 
88 private static void ScrollingStateChanging(object sender, VisualStateChangedEventArgs e)
89         {
90             if (e.NewState.Name == "Scrolling")
91             {
92                 //your logic
93             }
94             else
95             {
96                 //your logic
97             }
98         }
View Code

      这招是通过大名鼎鼎的LazyListBox学来的,作者也给出了解释为什么要这样做。

        There are two reasons for doing this. The first is that you want to avoid doing any work on the UI thread while the list is being scrolled, otherwise it won't be responsive to the user's gestures or you might see blank items in your list if the UI thread (which is creating the content for the items) can't keep up with the render thread (which is animating them). The other is that you want to avoid doing any work for items that are not visible to the user (see the next section) but the computation for what is visible and what is not visible is expensive, and per the previous sentence we don't want to do that expensive work while the list is scrolling. So we need to wait for the list to stop scrolling before we can compute the visible items.

        3. LongListSelector

        这里要讲的LongListSelector的是SDK中原生的控件,区别于toolkit中的LongListSelector

       LongListSelector不仅具有ListBox的UI虚拟化功能,更增加了DataTemplate的重用。光从感觉上就比ListBox要瞬间高大上了许多,msdn也在不遗余力的宣传用LLS替换ListBox。替换工作很容易就能做到,之前用的也很顺手,下面我们要讲的是我在使用LLS的过程中遇到的一个bug,迫使不得不换回了原先的ListBox

       还是先来看看LLS的代码。

  1 // Summary:
  2     //     Displays a list of selectable items with a mechanism for users to jump to
  3     //     a specific section of the list.
  4     [StyleTypedProperty(Property = "JumpListStyle", StyleTargetType = typeof(LongListSelector))]
  5     [TemplatePart(Name = "VerticalScrollBar", Type = typeof(ScrollBar))]
  6     [TemplatePart(Name = "ViewportControl", Type = typeof(ViewportControl))]
  7     [TemplateVisualState(Name = "NotScrolling", GroupName = "ScrollStates")]
  8     [TemplateVisualState(Name = "Scrolling", GroupName = "ScrollStates")]
  9     public class LongListSelector : Control, INotifyPropertyChanged
 10     {
 11         // Summary:
 12         //     Identifies the Microsoft.Phone.Controls.LongListSelector.GridCellSize dependency
 13         //     property.
 14         //
 15         // Returns:
 16         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.GridCellSize
 17         //     dependency property.
 18         public static readonly DependencyProperty GridCellSizeProperty;
 19         //
 20         // Summary:
 21         //     Identifies the Microsoft.Phone.Controls.LongListSelector.GroupFooterTemplate
 22         //     dependency property.
 23         //
 24         // Returns:
 25         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.GroupFooterTemplate
 26         //     dependency property.
 27         public static readonly DependencyProperty GroupFooterTemplateProperty;
 28         //
 29         // Summary:
 30         //     Identifies the Microsoft.Phone.Controls.LongListSelector.GroupHeaderTemplate
 31         //     dependency property.
 32         //
 33         // Returns:
 34         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.GroupHeaderTemplate
 35         //     dependency property.
 36         public static readonly DependencyProperty GroupHeaderTemplateProperty;
 37         //
 38         // Summary:
 39         //     Identifies the Microsoft.Phone.Controls.LongListSelector.HideEmptyGroups
 40         //     dependency property.
 41         //
 42         // Returns:
 43         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.HideEmptyGroups
 44         //     dependency property.
 45         public static readonly DependencyProperty HideEmptyGroupsProperty;
 46         //
 47         // Summary:
 48         //     Identifies the Microsoft.Phone.Controls.LongListSelector.IsGroupingEnabled
 49         //     dependency property.
 50         //
 51         // Returns:
 52         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.IsGroupingEnabled
 53         //     dependency property.
 54         public static readonly DependencyProperty IsGroupingEnabledProperty;
 55         //
 56         // Summary:
 57         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ItemsSource dependency
 58         //     property.
 59         //
 60         // Returns:
 61         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ItemsSource
 62         //     dependency property.
 63         public static readonly DependencyProperty ItemsSourceProperty;
 64         //
 65         // Summary:
 66         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ItemTemplate dependency
 67         //     property.
 68         //
 69         // Returns:
 70         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ItemTemplate
 71         //     dependency property.
 72         public static readonly DependencyProperty ItemTemplateProperty;
 73         //
 74         // Summary:
 75         //     Identifies the Microsoft.Phone.Controls.LongListSelector.JumpListStyle dependency
 76         //     property.
 77         //
 78         // Returns:
 79         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.JumpListStyle
 80         //     dependency property.
 81         public static readonly DependencyProperty JumpListStyleProperty;
 82         //
 83         // Summary:
 84         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ListFooter dependency
 85         //     property.
 86         //
 87         // Returns:
 88         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ListFooter
 89         //     dependency property.
 90         public static readonly DependencyProperty ListFooterProperty;
 91         //
 92         // Summary:
 93         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ListFooterTemplate
 94         //     dependency property.
 95         //
 96         // Returns:
 97         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ListFooterTemplate
 98         //     dependency property.
 99         public static readonly DependencyProperty ListFooterTemplateProperty;
100         //
101         // Summary:
102         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ListHeader dependency
103         //     property.
104         //
105         // Returns:
106         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ListHeader
107         //     dependency property.
108         public static readonly DependencyProperty ListHeaderProperty;
109         //
110         // Summary:
111         //     Identifies the Microsoft.Phone.Controls.LongListSelector.ListHeaderTemplate
112         //     dependency property.
113         //
114         // Returns:
115         //     The identifier for the Microsoft.Phone.Controls.LongListSelector.ListHeaderTemplate
116         //     dependency property.
117         public static readonly DependencyProperty ListHeaderTemplateProperty;
118 
119         // Summary:
120         //     Initializes a new instance of the Microsoft.Phone.Controls.LongListSelector
121         //     class.
122         public LongListSelector();
123 
124         // Summary:
125         //     Gets or sets the size used when displaying an item in the Microsoft.Phone.Controls.LongListSelector.
126         //
127         // Returns:
128         //     The size used when displaying an item.
129         public Size GridCellSize { get; set; }
130         //
131         // Summary:
132         //     Gets or sets the template for the group footer in the Microsoft.Phone.Controls.LongListSelector.
133         //
134         // Returns:
135         //     The System.Windows.DataTemplate that provides the templates for the group
136         //     footer in the Microsoft.Phone.Controls.LongListSelector.
137         public DataTemplate GroupFooterTemplate { get; set; }
138         //
139         // Summary:
140         //     Gets or sets the template for the group header in the Microsoft.Phone.Controls.LongListSelector.
141         //
142         // Returns:
143         //     The System.Windows.DataTemplate for the group header in the Microsoft.Phone.Controls.LongListSelector.
144         public DataTemplate GroupHeaderTemplate { get; set; }
145         //
146         // Summary:
147         //     Gets or sets a value that indicates whether to hide empty groups in the Microsoft.Phone.Controls.LongListSelector.
148         //
149         // Returns:
150         //     true if empty groups are hidden; otherwise false.Default is false.
151         public bool HideEmptyGroups { get; set; }
152         //
153         // Summary:
154         //     Gets or sets a value that indicates whether grouping is enabled in the Microsoft.Phone.Controls.LongListSelector.
155         //
156         // Returns:
157         //     true if grouping is enabled; otherwise false.
158         public bool IsGroupingEnabled { get; set; }
159         //
160         // Summary:
161         //     Gets or sets a collection used to generate the content of the Microsoft.Phone.Controls.LongListSelector.
162         //
163         // Returns:
164         //     A collection that is used to generate the content of the Microsoft.Phone.Controls.LongListSelector.
165         public IList ItemsSource { get; set; }
166         //
167         // Summary:
168         //     Gets or sets the template for the items in the items view
169         //
170         // Returns:
171         //     The System.Windows.DataTemplate for the items in the items view.
172         public DataTemplate ItemTemplate { get; set; }
173         //
174         // Summary:
175         //     Gets or sets the System.Windows.Style for jump list in the Microsoft.Phone.Controls.LongListSelector.
176         //
177         // Returns:
178         //     The System.Windows.Style for the jump list in the Microsoft.Phone.Controls.LongListSelector.
179         public Style JumpListStyle { get; set; }
180         //
181         // Summary:
182         //     Gets or sets a value that specifies if the Microsoft.Phone.Controls.LongListSelector
183         //     is in a list mode or grid mode from the Microsoft.Phone.Controls.LongListSelectorLayoutMode
184         //     enum.
185         //
186         // Returns:
187         //     A Microsoft.Phone.Controls.LongListSelectorLayoutMode value that specifies
188         //     if the Microsoft.Phone.Controls.LongListSelector is in a list mode or grid
189         //     mode.
190         public LongListSelectorLayoutMode LayoutMode { get; set; }
191         //
192         // Summary:
193         //     Gets or sets the object that is displayed at the foot of the Microsoft.Phone.Controls.LongListSelector.
194         //
195         // Returns:
196         //     The System.Object that is displayed at the foot of the Microsoft.Phone.Controls.LongListSelector.
197         public object ListFooter { get; set; }
198         //
199         // Summary:
200         //     Gets or sets the System.Windows.DataTemplatefor an item to display at the
201         //     foot of the Microsoft.Phone.Controls.LongListSelector.
202         //
203         // Returns:
204         //     The System.Windows.DataTemplate for an item to display at the foot of the
205         //     Microsoft.Phone.Controls.LongListSelector.
206         public DataTemplate ListFooterTemplate { get; set; }
207         //
208         // Summary:
209         //     Gets or sets the object to display at the head of the Microsoft.Phone.Controls.LongListSelector.
210         //
211         // Returns:
212         //     The System.Object that is displayed at the head of the Microsoft.Phone.Controls.LongListSelector.
213         public object ListHeader { get; set; }
214         //
215         // Summary:
216         //     Gets or sets the System.Windows.DataTemplatefor an item to display at the
217         //     head of the Microsoft.Phone.Controls.LongListSelector.
218         //
219         // Returns:
220         //     The System.Windows.DataTemplate for an item to display at the head of the
221         //     Microsoft.Phone.Controls.LongListSelector.
222         public DataTemplate ListHeaderTemplate { get; set; }
223         //
224         // Summary:
225         //     Gets the state of manipulation handling on the Microsoft.Phone.Controls.LongListSelector
226         //     control.
227         //
228         // Returns:
229         //     The state of manipulation handling on the Microsoft.Phone.Controls.LongListSelector
230         //     control.
231         public ManipulationState ManipulationState { get; }
232         //
233         // Summary:
234         //     Gets the currently selected item in the Microsoft.Phone.Controls.LongListSelector.
235         //
236         // Returns:
237         //     The System.Object that represents the currently selected item in the Microsoft.Phone.Controls.LongListSelector.
238         public object SelectedItem { get; set; }
239 
240         // Summary:
241         //     Occurs when a new item is realized.
242         public event EventHandler<ItemRealizationEventArgs> ItemRealized;
243         //
244         // Summary:
245         //     Occurs when an item in the Microsoft.Phone.Controls.LongListSelector is unrealized.
246         public event EventHandler<ItemRealizationEventArgs> ItemUnrealized;
247         //
248         // Summary:
249         //     Occurs when the jump list is closed.
250         public event EventHandler JumpListClosed;
251         //
252         // Summary:
253         //     Occurs when a jump list is opened.
254         public event EventHandler JumpListOpening;
255         //
256         // Summary:
257         //     Occurs when Microsoft.Phone.Controls.ManipulationState changes.
258         public event EventHandler ManipulationStateChanged;
259         //
260         // Summary:
261         //     Occurs when a property value changes.
262         public event PropertyChangedEventHandler PropertyChanged;
263         //
264         // Summary:
265         //     Occurs when the currently selected item changes.
266         public event SelectionChangedEventHandler SelectionChanged;
267 
268         // Summary:
269         //     Provides the behavior for the Measure pass of layout.
270         //
271         // Parameters:
272         //   availableSize:
273         //     The available size that this object can give to child objects. Infinity (System.Double.PositiveInfinity)
274         //     can be specified as a value to indicate that the object will size to whatever
275         //     content is available.
276         //
277         // Returns:
278         //     The size that this object determines it needs during layout, based on its
279         //     calculations of the allocated sizes for child objects; or based on other
280         //     considerations, such as a fixed container size.
281         protected override Size MeasureOverride(Size availableSize);
282         //
283         // Summary:
284         //     Builds the visual tree for the Microsoft.Phone.Controls.LongListSelector
285         //     control when a new template is applied.
286         public override void OnApplyTemplate();
287         //
288         // Summary:
289         //     Scrolls to a specified item in the Microsoft.Phone.Controls.LongListSelector.
290         //
291         // Parameters:
292         //   item:
293         //     The list item to scroll to.
294         public void ScrollTo(object item);
295     }
View Code

       直接暴露了ScrollingNoteScrolling这两个状态在外面,可滑动区域的控件也从ScrollViewer换成了ViewportControl,一切看起来都是这么的自然,以至于感觉和ListBox没有太大的区别,下面我们就来看看最近遇到的这个bug。先看下面的代码。

 1 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 2             <phone:LongListSelector x:Name="list" SelectionChanged="list_SelectionChanged" />
 3 </Grid>
 4 
 5 using System;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Collections.ObjectModel;
 9 using Microsoft.Phone.Controls;
10 
11 namespace ListCheck
12 {
13     public partial class MainPage : PhoneApplicationPage
14     {
15         ObservableCollection<string> clist;
16 
17         // Constructor
18         public MainPage()
19         {
20             InitializeComponent();
21 
22             clist = new ObservableCollection<string>();
23 
24             clist.Add("1");
25             clist.Add("2");
26             clist.Add("3");
27             clist.Add("4");
28             clist.Add("5");
29             clist.Add("6");
30             clist.Add("7");
31             clist.Add("8");
32 
33             list.ItemsSource = clist;
34         }
35 
36         private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
37         {
38             clist.Remove((string)e.AddedItems[0]);
39         }
40     }
41 }
View Code

      代码应该很好理解,就是在页面放一个LLS控件,然后通过后台绑定数据,然后通过SelectionChanged事件来删除Item。如果这个时候我们从上面一个一个往下删除的时候就会发生一个意想不到的Exception,System.ArgumentException: Value does not fall within the expected range. 如果你按照上面的步骤做了,相信会很好复现。

      通过Google了这个问题之后,发现这个bug不光是我一个人碰到,很多人也有同样的问题。Exception是通过LLS的MeasureOverride方法抛出来的,解决办法是通过重写MeasureOverride方法,并try/catch来规避原有的问题。看似是可以解决这个问题,但在实际应用过程中try/catch之后的界面很大几率被打乱,所以这个方法行不通。

 1 public class LongListSelectorEx : LongListSelector
 2 {
 3 
 4         protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
 5         {
 6             try
 7             {
 8                 return base.MeasureOverride(availableSize);
 9             }
10             catch (ArgumentException)
11             {
12                 return this.DesiredSize;
13             }
14         }
15 }
View Code

     还有一种解决办法是通过设置LLS的ItemsSource,在删除Item之前先把ItemsSource设成null,删除之后再重新赋值,这个方法可以解决这个问题,但在数据量很大的时候这种做法显然很鸡肋。后来无奈还是换回到ListBox

1 list.ItemsSource = null;
2 clist.Remove((string)e.AddedItems[0]);
3 list.ItemsSource = clist;
View Code

 

  • 总结

     WP提供了三种集合控件,存在即合理嘛,总有一种适合您的需求。希望您看完之后能像耐心挑选数据结构一样,耐心选择使用哪种集合控件。

     这篇博客讲的东西都很基础,其实我觉得基础很重要。相比之下见到了不少人上来就是,大数据,多少多少万用户量。这些都是不切实际的做法,基础的东西是最容易被忽略的,我相信大部分问题的产生都是因为基础不扎实。只有积跬步才能至千里。

 

 

 

posted on 2014-03-28 15:56  艾克赛尔  阅读(782)  评论(2编辑  收藏  举报