• 前言

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

  1. ItemsControl

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

先来看看ItemsControl的代码。

 // Summary:
// Represents a control that can be used to present a collection of items.
[ContentProperty("Items", true)]
public class ItemsControl : Control
{
// Summary:
// Identifies the System.Windows.Controls.ItemsControl.DisplayMemberPath dependency
// property.
//
// Returns:
// The identifier for the System.Windows.Controls.ItemsControl.DisplayMemberPath
// dependency property.
public static readonly DependencyProperty DisplayMemberPathProperty;
//
// Summary:
// Identifies the System.Windows.Controls.ItemsControl.ItemsPanel dependency
// property.
//
// Returns:
// The identifier for the System.Windows.Controls.ItemsControl.ItemsPanel dependency
// property.
public static readonly DependencyProperty ItemsPanelProperty;
//
// Summary:
// Identifies the System.Windows.Controls.ItemsControl.ItemsSource dependency
// property.
//
// Returns:
// The identifier for the System.Windows.Controls.ItemsControl.ItemsSource dependency
// property.
public static readonly DependencyProperty ItemsSourceProperty;
//
// Summary:
// Identifies the System.Windows.Controls.ItemsControl.ItemTemplate dependency
// property.
//
// Returns:
// The identifier for the System.Windows.Controls.ItemsControl.ItemTemplate
// dependency property.
public static readonly DependencyProperty ItemTemplateProperty; // Summary:
// Initializes a new instance of the System.Windows.Controls.ItemsControl class.
public ItemsControl(); // Summary:
// Gets or sets the name or path of the property that is displayed for each
// data item.
//
// Returns:
// The name or path of the property that is displayed for each the data item
// in the control. The default is an empty string ("").
public string DisplayMemberPath { get; set; }
//
// Summary:
// Gets the System.Windows.Controls.ItemContainerGenerator associated with this
// System.Windows.Controls.ItemsControl.
//
// Returns:
// The System.Windows.Controls.ItemContainerGenerator associated with this System.Windows.Controls.ItemsControl.
public ItemContainerGenerator ItemContainerGenerator { get; }
//
// Summary:
// Gets the collection used to generate the content of the control.
//
// Returns:
// The collection that is used to generate the content of the control, if it
// exists; otherwise, null. The default is an empty collection.
public ItemCollection Items { get; }
//
// Summary:
// Gets or sets the template that defines the panel that controls the layout
// of items.
//
// Returns:
// An System.Windows.Controls.ItemsPanelTemplate that defines the panel to use
// for the layout of the items. The default value for the System.Windows.Controls.ItemsControl
// is an System.Windows.Controls.ItemsPanelTemplate that specifies a System.Windows.Controls.StackPanel.
public ItemsPanelTemplate ItemsPanel { get; set; }
//
// Summary:
// Gets or sets a collection used to generate the content of the System.Windows.Controls.ItemsControl.
//
// Returns:
// The object that is used to generate the content of the System.Windows.Controls.ItemsControl.
// The default is null.
public IEnumerable ItemsSource { get; set; }
//
// Summary:
// Gets or sets the System.Windows.DataTemplate used to display each item.
//
// Returns:
// The template that specifies the visualization of the data objects. The default
// is null.
public DataTemplate ItemTemplate { get; set; } // Summary:
// Undoes the effects of the System.Windows.Controls.ItemsControl.PrepareContainerForItemOverride(System.Windows.DependencyObject,System.Object)
// method.
//
// Parameters:
// element:
// The container element.
//
// item:
// The item.
protected virtual void ClearContainerForItemOverride(DependencyObject element, object item);
//
// Summary:
// Creates or identifies the element that is used to display the given item.
//
// Returns:
// The element that is used to display the given item.
protected virtual DependencyObject GetContainerForItemOverride();
//
// Summary:
// Returns the System.Windows.Controls.ItemsControl that the specified element
// hosts items for.
//
// Parameters:
// element:
// The host element.
//
// Returns:
// The System.Windows.Controls.ItemsControl that the specified element hosts
// items for, or null.
public static ItemsControl GetItemsOwner(DependencyObject element);
//
// Summary:
// Determines if the specified item is (or is eligible to be) its own container.
//
// Parameters:
// item:
// The item to check.
//
// Returns:
// true if the item is (or is eligible to be) its own container; otherwise,
// false.
protected virtual bool IsItemItsOwnContainerOverride(object item);
//
// Summary:
// Returns the System.Windows.Controls.ItemsControl that owns the specified
// container element.
//
// Parameters:
// container:
// The container element to return the System.Windows.Controls.ItemsControl
// for.
//
// Returns:
// The System.Windows.Controls.ItemsControl that owns the specified container
// element; otherwise, null. The System.Windows.Controls.ItemsControl.ItemsControlFromItemContainer(System.Windows.DependencyObject)
// method returns null if container is not a System.Windows.UIElement or the
// parent is not an System.Windows.Controls.ItemsControl.
public static ItemsControl ItemsControlFromItemContainer(DependencyObject container);
//
// Summary:
// Called when the value of the System.Windows.Controls.ItemsControl.Items property
// changes.
//
// Parameters:
// e:
// A System.Collections.Specialized.NotifyCollectionChangedEventArgs that contains
// the event data
protected virtual void OnItemsChanged(NotifyCollectionChangedEventArgs e);
//
// Summary:
// Prepares the specified element to display the specified item.
//
// Parameters:
// element:
// The element used to display the specified item.
//
// item:
// The item to display.
protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item);
}

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

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

2.  ListBox

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

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

 // Summary:
// Contains a list of selectable items.
[TemplatePart(Name = "ScrollViewer", Type = typeof(ScrollViewer))]
[TemplateVisualState(Name = "InvalidFocused", GroupName = "ValidationStates")]
[TemplateVisualState(Name = "InvalidUnfocused", GroupName = "ValidationStates")]
[TemplateVisualState(Name = "Valid", GroupName = "ValidationStates")]
public class ListBox : Selector
{
// Summary:
// Identifies the IsSelectionActive dependency property.
//
// Returns:
// The identifier for the IsSelectionActive dependency property.
public static readonly DependencyProperty IsSelectionActiveProperty;
//
// Summary:
// Identifies the System.Windows.Controls.ListBox.ItemContainerStyle dependency
// property.
//
// Returns:
// The identifier for the System.Windows.Controls.ListBox.ItemContainerStyle
// dependency property.
public static readonly DependencyProperty ItemContainerStyleProperty;
//
// Summary:
// Identifies the System.Windows.Controls.ListBox.SelectionMode dependency property.
//
// Returns:
// The identifier for the System.Windows.Controls.ListBox.SelectionMode dependency
// property.
public static readonly DependencyProperty SelectionModeProperty; // Summary:
// Initializes a new instance of the System.Windows.Controls.ListBox class.
public ListBox(); // Summary:
// Gets or sets the style that is used when rendering the item containers.
//
// Returns:
// The style applied to the item containers. The default is null.
public Style ItemContainerStyle { get; set; }
//
// Summary:
// Gets the list of currently selected items for the System.Windows.Controls.ListBox
// control.
//
// Returns:
// The list of currently selected items for the System.Windows.Controls.ListBox.
public IList SelectedItems { get; }
//
// Summary:
// Gets or sets the selection behavior for the System.Windows.Controls.ListBox
// control.
//
// Returns:
// One of the System.Windows.Controls.SelectionMode values.
public SelectionMode SelectionMode { get; set; } // Summary:
// Creates or identifies the element used to display a specified item.
//
// Returns:
// A System.Windows.Controls.ListBoxItem corresponding to a specified item.
protected override DependencyObject GetContainerForItemOverride();
//
// Summary:
// Determines if the specified item is (or is eligible to be) its own item container.
//
// Parameters:
// item:
// The specified item.
//
// Returns:
// true if the item is its own item container; otherwise, false.
protected override bool IsItemItsOwnContainerOverride(object item);
//
// Summary:
// Builds the visual tree for the System.Windows.Controls.ListBox control when
// a new template is applied.
public override void OnApplyTemplate();
//
// Summary:
// Returns a System.Windows.Automation.Peers.ListBoxAutomationPeer for the Windows Phone
// automation infrastructure.
//
// Returns:
// A System.Windows.Automation.Peers.ListBoxAutomationPeer for the System.Windows.Controls.ListBox
// object.
protected override AutomationPeer OnCreateAutomationPeer();
//
// Summary:
// Provides handling for the System.Windows.UIElement.GotFocus event.
//
// Parameters:
// e:
// The event data.
protected override void OnGotFocus(RoutedEventArgs e);
//
// Summary:
// Provides handling for the System.Windows.Controls.ItemContainerGenerator.ItemsChanged
// event.
//
// Parameters:
// e:
// A System.Collections.Specialized.NotifyCollectionChangedEventArgs that contains
// the event data.
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e);
//
// Summary:
// Provides handling for the System.Windows.UIElement.KeyDown event that occurs
// when a key is pressed while the control has focus.
//
// Parameters:
// e:
// The event data.
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Straightforward switch-based key handling method that barely triggers the warning")]
protected override void OnKeyDown(KeyEventArgs e);
//
// Summary:
// Provides handling for the System.Windows.UIElement.LostFocus event.
//
// Parameters:
// e:
// The event data.
protected override void OnLostFocus(RoutedEventArgs e);
//
// Summary:
// Causes the object to scroll into view.
//
// Parameters:
// item:
// The object to scroll.
public void ScrollIntoView(object item);
//
// Summary:
// Selects all the items in the System.Windows.Controls.ListBox.
//
// Exceptions:
// System.NotSupportedException:
// System.Windows.Controls.ListBox.SelectionMode is set to System.Windows.Controls.SelectionMode.Single
public void SelectAll();
}

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

 // Summary:
// Represents a scrollable area that can contain other visible elements.
[TemplatePart(Name = "HorizontalScrollBar", Type = typeof(ScrollBar))]
[TemplatePart(Name = "ScrollContentPresenter", Type = typeof(ScrollContentPresenter))]
[TemplatePart(Name = "VerticalScrollBar", Type = typeof(ScrollBar))]
[TemplateVisualState(Name = "CompressionBottom", GroupName = "VerticalCompressionStates")]
[TemplateVisualState(Name = "CompressionLeft", GroupName = "HorizontalCompressionStates")]
[TemplateVisualState(Name = "CompressionRight", GroupName = "HorizontalCompressionStates")]
[TemplateVisualState(Name = "CompressionTop", GroupName = "VerticalCompressionStates")]
[TemplateVisualState(Name = "NoHorizontalCompression", GroupName = "HorizontalCompressionStates")]
[TemplateVisualState(Name = "NotScrolling", GroupName = "ScrollStates")]
[TemplateVisualState(Name = "NoVerticalCompression", GroupName = "VerticalCompressionStates")]
[TemplateVisualState(Name = "Scrolling", GroupName = "ScrollStates")]
public sealed class ScrollViewer : ContentControl

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

 // Summary:
// Contains mutually exclusive System.Windows.VisualState objects and System.Windows.VisualTransition
// objects that are used to go from one state to another.
[ContentProperty("States", true)]
public sealed class VisualStateGroup : DependencyObject
{
// Summary:
// Initializes a new instance of the System.Windows.VisualStateGroup class.
public VisualStateGroup(); // Summary:
// Gets the most recently set System.Windows.VisualState from a successful call
// to the System.Windows.VisualStateManager.GoToState(System.Windows.Controls.Control,System.String,System.Boolean)
// method.
//
// Returns:
// The most recently set System.Windows.VisualState from a successful call to
// the System.Windows.VisualStateManager.GoToState(System.Windows.Controls.Control,System.String,System.Boolean)
// method.
public VisualState CurrentState { get; }
//
// Summary:
// Gets the name of the System.Windows.VisualStateGroup.
//
// Returns:
// The name of the System.Windows.VisualStateGroup.
public string Name { get; }
//
// Summary:
// Gets the collection of mutually exclusive System.Windows.VisualState objects.
//
// Returns:
// The collection of mutually exclusive System.Windows.VisualState objects.
public IList States { get; }
//
// Summary:
// Gets the collection of System.Windows.VisualTransition objects.
//
// Returns:
// The collection of System.Windows.VisualTransition objects.
public IList Transitions { get; } // Summary:
// Occurs after a control transitions into a different state.
public event EventHandler<VisualStateChangedEventArgs> CurrentStateChanged;
//
// Summary:
// Occurs when a control begins transitioning into a different state. public event EventHandler<VisualStateChangedEventArgs> CurrentStateChanging;
}
public static void HookScrollingEvents(DependencyObject listBox)
{
if (DesignerProperties.IsInDesignTool) return;
var scrollViewerVisualStateGroup = GetScrollStates(listBox);
if (scrollViewerVisualStateGroup != null)
{
scrollViewerVisualStateGroup.CurrentStateChanging += ScrollingStateChanging;
}
} static VisualStateGroup GetScrollStates(DependencyObject root)
{
try
{
var scrollViewer = FindItem<ScrollViewer>(root, elt => elt.Name == "ScrollViewer");
if (scrollViewer == null) return null;
var element = VisualTreeHelper.GetChild(scrollViewer, ) as FrameworkElement;
return element == null ? null : VisualStateManager.GetVisualStateGroups(element).Cast<VisualStateGroup>().SingleOrDefault(elt => elt.Name == "ScrollStates");
}
catch { }
return null;
} public static T FindItem<T>(DependencyObject parent, Func<T, bool> filter) where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = ; i < childCount; i++)
{
var elt = VisualTreeHelper.GetChild(parent, i);
if (elt is T && filter((T)elt)) return (T)elt;
var result = FindItem(elt, filter);
if (result != null) return result;
}
return null;
} private static void ScrollingStateChanging(object sender, VisualStateChangedEventArgs e)
{
if (e.NewState.Name == "Scrolling")
{
//your logic
}
else
{
//your logic
}
}

这招是通过大名鼎鼎的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的代码。

 // Summary:
// Displays a list of selectable items with a mechanism for users to jump to
// a specific section of the list.
[StyleTypedProperty(Property = "JumpListStyle", StyleTargetType = typeof(LongListSelector))]
[TemplatePart(Name = "VerticalScrollBar", Type = typeof(ScrollBar))]
[TemplatePart(Name = "ViewportControl", Type = typeof(ViewportControl))]
[TemplateVisualState(Name = "NotScrolling", GroupName = "ScrollStates")]
[TemplateVisualState(Name = "Scrolling", GroupName = "ScrollStates")]
public class LongListSelector : Control, INotifyPropertyChanged
{
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.GridCellSize dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.GridCellSize
// dependency property.
public static readonly DependencyProperty GridCellSizeProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.GroupFooterTemplate
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.GroupFooterTemplate
// dependency property.
public static readonly DependencyProperty GroupFooterTemplateProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.GroupHeaderTemplate
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.GroupHeaderTemplate
// dependency property.
public static readonly DependencyProperty GroupHeaderTemplateProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.HideEmptyGroups
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.HideEmptyGroups
// dependency property.
public static readonly DependencyProperty HideEmptyGroupsProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.IsGroupingEnabled
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.IsGroupingEnabled
// dependency property.
public static readonly DependencyProperty IsGroupingEnabledProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ItemsSource dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ItemsSource
// dependency property.
public static readonly DependencyProperty ItemsSourceProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ItemTemplate dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ItemTemplate
// dependency property.
public static readonly DependencyProperty ItemTemplateProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.JumpListStyle dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.JumpListStyle
// dependency property.
public static readonly DependencyProperty JumpListStyleProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ListFooter dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ListFooter
// dependency property.
public static readonly DependencyProperty ListFooterProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ListFooterTemplate
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ListFooterTemplate
// dependency property.
public static readonly DependencyProperty ListFooterTemplateProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ListHeader dependency
// property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ListHeader
// dependency property.
public static readonly DependencyProperty ListHeaderProperty;
//
// Summary:
// Identifies the Microsoft.Phone.Controls.LongListSelector.ListHeaderTemplate
// dependency property.
//
// Returns:
// The identifier for the Microsoft.Phone.Controls.LongListSelector.ListHeaderTemplate
// dependency property.
public static readonly DependencyProperty ListHeaderTemplateProperty; // Summary:
// Initializes a new instance of the Microsoft.Phone.Controls.LongListSelector
// class.
public LongListSelector(); // Summary:
// Gets or sets the size used when displaying an item in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The size used when displaying an item.
public Size GridCellSize { get; set; }
//
// Summary:
// Gets or sets the template for the group footer in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Windows.DataTemplate that provides the templates for the group
// footer in the Microsoft.Phone.Controls.LongListSelector.
public DataTemplate GroupFooterTemplate { get; set; }
//
// Summary:
// Gets or sets the template for the group header in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Windows.DataTemplate for the group header in the Microsoft.Phone.Controls.LongListSelector.
public DataTemplate GroupHeaderTemplate { get; set; }
//
// Summary:
// Gets or sets a value that indicates whether to hide empty groups in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// true if empty groups are hidden; otherwise false.Default is false.
public bool HideEmptyGroups { get; set; }
//
// Summary:
// Gets or sets a value that indicates whether grouping is enabled in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// true if grouping is enabled; otherwise false.
public bool IsGroupingEnabled { get; set; }
//
// Summary:
// Gets or sets a collection used to generate the content of the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// A collection that is used to generate the content of the Microsoft.Phone.Controls.LongListSelector.
public IList ItemsSource { get; set; }
//
// Summary:
// Gets or sets the template for the items in the items view
//
// Returns:
// The System.Windows.DataTemplate for the items in the items view.
public DataTemplate ItemTemplate { get; set; }
//
// Summary:
// Gets or sets the System.Windows.Style for jump list in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Windows.Style for the jump list in the Microsoft.Phone.Controls.LongListSelector.
public Style JumpListStyle { get; set; }
//
// Summary:
// Gets or sets a value that specifies if the Microsoft.Phone.Controls.LongListSelector
// is in a list mode or grid mode from the Microsoft.Phone.Controls.LongListSelectorLayoutMode
// enum.
//
// Returns:
// A Microsoft.Phone.Controls.LongListSelectorLayoutMode value that specifies
// if the Microsoft.Phone.Controls.LongListSelector is in a list mode or grid
// mode.
public LongListSelectorLayoutMode LayoutMode { get; set; }
//
// Summary:
// Gets or sets the object that is displayed at the foot of the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Object that is displayed at the foot of the Microsoft.Phone.Controls.LongListSelector.
public object ListFooter { get; set; }
//
// Summary:
// Gets or sets the System.Windows.DataTemplatefor an item to display at the
// foot of the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Windows.DataTemplate for an item to display at the foot of the
// Microsoft.Phone.Controls.LongListSelector.
public DataTemplate ListFooterTemplate { get; set; }
//
// Summary:
// Gets or sets the object to display at the head of the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Object that is displayed at the head of the Microsoft.Phone.Controls.LongListSelector.
public object ListHeader { get; set; }
//
// Summary:
// Gets or sets the System.Windows.DataTemplatefor an item to display at the
// head of the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Windows.DataTemplate for an item to display at the head of the
// Microsoft.Phone.Controls.LongListSelector.
public DataTemplate ListHeaderTemplate { get; set; }
//
// Summary:
// Gets the state of manipulation handling on the Microsoft.Phone.Controls.LongListSelector
// control.
//
// Returns:
// The state of manipulation handling on the Microsoft.Phone.Controls.LongListSelector
// control.
public ManipulationState ManipulationState { get; }
//
// Summary:
// Gets the currently selected item in the Microsoft.Phone.Controls.LongListSelector.
//
// Returns:
// The System.Object that represents the currently selected item in the Microsoft.Phone.Controls.LongListSelector.
public object SelectedItem { get; set; } // Summary:
// Occurs when a new item is realized.
public event EventHandler<ItemRealizationEventArgs> ItemRealized;
//
// Summary:
// Occurs when an item in the Microsoft.Phone.Controls.LongListSelector is unrealized.
public event EventHandler<ItemRealizationEventArgs> ItemUnrealized;
//
// Summary:
// Occurs when the jump list is closed.
public event EventHandler JumpListClosed;
//
// Summary:
// Occurs when a jump list is opened.
public event EventHandler JumpListOpening;
//
// Summary:
// Occurs when Microsoft.Phone.Controls.ManipulationState changes.
public event EventHandler ManipulationStateChanged;
//
// Summary:
// Occurs when a property value changes.
public event PropertyChangedEventHandler PropertyChanged;
//
// Summary:
// Occurs when the currently selected item changes.
public event SelectionChangedEventHandler SelectionChanged; // Summary:
// Provides the behavior for the Measure pass of layout.
//
// Parameters:
// availableSize:
// The available size that this object can give to child objects. Infinity (System.Double.PositiveInfinity)
// can be specified as a value to indicate that the object will size to whatever
// content is available.
//
// Returns:
// The size that this object determines it needs during layout, based on its
// calculations of the allocated sizes for child objects; or based on other
// considerations, such as a fixed container size.
protected override Size MeasureOverride(Size availableSize);
//
// Summary:
// Builds the visual tree for the Microsoft.Phone.Controls.LongListSelector
// control when a new template is applied.
public override void OnApplyTemplate();
//
// Summary:
// Scrolls to a specified item in the Microsoft.Phone.Controls.LongListSelector.
//
// Parameters:
// item:
// The list item to scroll to.
public void ScrollTo(object item);
}

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

 <Grid x:Name="ContentPanel" Grid.Row="" Margin="12,0,12,0">
<phone:LongListSelector x:Name="list" SelectionChanged="list_SelectionChanged" />
</Grid> using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using Microsoft.Phone.Controls; namespace ListCheck
{
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<string> clist; // Constructor
public MainPage()
{
InitializeComponent(); clist = new ObservableCollection<string>(); clist.Add("");
clist.Add("");
clist.Add("");
clist.Add("");
clist.Add("");
clist.Add("");
clist.Add("");
clist.Add(""); list.ItemsSource = clist;
} private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
clist.Remove((string)e.AddedItems[]);
}
}
}

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

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

 public class LongListSelectorEx : LongListSelector
{ protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
try
{
return base.MeasureOverride(availableSize);
}
catch (ArgumentException)
{
return this.DesiredSize;
}
}
}

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

 list.ItemsSource = null;
clist.Remove((string)e.AddedItems[]);
list.ItemsSource = clist;
  • 总结

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

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

Windows Phone中的几种集合控件的更多相关文章

  1. Windows 8.1 应用再出发 - 几种布局控件

    本篇为大家介绍Windows 商店应用中几种布局控件的用法.分别是Canvas.Grid.StackPanel 和 VariableSizedWrapGrid. 1. Canvas Canvas使用绝 ...

  2. Delphi一共封装(超类化)了8种Windows基础控件和17种复杂控件

    超类化源码: procedure TWinControl.CreateSubClass(var Params: TCreateParams; ControlClassName: PChar); con ...

  3. 重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresenter

    原文:重新想象 Windows 8 Store Apps (5) - 控件之集合控件: ComboBox, ListBox, FlipView, ItemsControl, ItemsPresente ...

  4. [Xcode 实际操作]三、视图控制器-(12)在Storyboard中使用集合控件

    目录:[Swift]Xcode实际操作 本文将演示集合控件在故事板中的使用. 在控制器根视图上点击鼠标,以选择该根视图. 现在往根视图中添加一个集合视图. 点击[库面板]图标,打开控件库面板 在控件库 ...

  5. Windows 8.1 应用再出发 (WinJS) - 几种新增控件(2)

    上篇我们介绍了Windows 8.1 和 WinJS 中新增控件中的 AppBarCommand.BackButton.Hub.ItemContainer,本篇我们接着来介绍 NavBar.Repea ...

  6. 在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件(转)

    引言 这两天沉迷了Google SketchUp,刚刚玩够,一时兴起,研究了一下WebBrowser. 我在<WebBrowser控件使用技巧分享>一文中曾谈到过“我现在可以通过WebBr ...

  7. 重新想象 Windows 8.1 Store Apps (76) - 新增控件: SearchBox

    [源码下载] 重新想象 Windows 8.1 Store Apps (76) - 新增控件: SearchBox 作者:webabcd 介绍重新想象 Windows 8.1 Store Apps 之 ...

  8. WPF集合控件实现分隔符(ItemsControl Splitter)

    在WPF的集合控件中常常需要在每一个集合项之间插入一个分隔符样式,但是WPF的ItemsControl没有相关功能的直接实现,所以只能考虑曲线救国,经过研究,大概想到了以下两种实现方式. 先写出Ite ...

  9. WPF集合控件实现分隔符(ItemsControl Separator)

    在WPF的集合控件中常常需要在每一个集合项之间插入一个分隔符样式,但是WPF的ItemsControl没有相关功能的直接实现,所以只能考虑曲线救国,经过研究,大概想到了以下两种实现方式. 先写出Ite ...

随机推荐

  1. jQuery中each的break和continue

    each实质上是一个for循环,那么能不能像普通的for循环那样break和continue呢? 参考http://bevisoft.iteye.com/blog/641195做了个实验,可以的, 代 ...

  2. jQuery选择器之基本选择器Demo

    测试代码: 01-基本选择器.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &quo ...

  3. hdu 1530 最大团模板

    说明摘自:pushing my way 的博文 最大团 通过该博主的代码,总算理解了最大团问题,但是他实现时的代码效率却不算太高.因此在最后献上我的模板.加了IO优化目前的排名是: 6 yejinru ...

  4. 分页控件(PageControl)的相关属性说明

    UIPageControl *pageControl = [[UIPageControl alloc] init]; pageControl.center = CGPointMake(w * ); p ...

  5. JS辨别浏览器系统IOS或安卓

    详细内容请点击 /* * 智能机浏览器版本信息: * */ (function($,window,document){     $.extend({         browser:{         ...

  6. Asp.Net复习篇之Asp.Net生命周期与Asp.Net页的生命周期

    Asp.Net生命周期与Asp.Net页的生命周期是一个比较重要的话题,有时可能似乎知道一些,但又说不出个所以然,而且时常把这两个概念混淆.现在也是该好好理清思路,把这两个概念搞懂. Asp.Net生 ...

  7. WScript.SendKeys()的sendkeys发送组合键以及特殊字符

    SendKeys.Send("^+{TAB}"); 使用SendKeys将键击和组合键击发送到活动应用程序.此类无法实例化.若要发送一个键击给某个类并立即继续程序流,请使用Send ...

  8. 异常getaddrinfo enotfound

    在看NodeJS开发指南这本书时,书中的一个例子,讲解http.request的.代码如下: var http = require('http'); var querystring = require ...

  9. 构建前端Mock Server

    写在前面 最开始只是在做活动页面时苦于效率太低制定了这样一个自动化的工作环境, 所以Github上项目名是Rapid-Dev-Activity-Page(快速开发活动页...). 活动页这类比较简单的 ...

  10. CSS伪对象选择符整理

    1.E::selection 2.E::placeholder 1. E::selection 设置对象被选择时的样式. 需要注意的是,::selection只能定义被选择时的background-c ...