仿新浪微博实现ListBox下拉刷新和到底部自动加载
一、下拉刷新
下拉刷新实现思路:
1、定义一个PullDownToRefreshPanel容器控件。为它添加3种状态模板,分别是PullingDownTemplate,ReadyToReleaseTemplate
和RefreshingTemplate,顾名思义分别是显示下拉状态模板,显示松开刷新状态模板和正在刷新中的状态模板。
2、定义自己的ListBox让它继承系统的ListBox,并重写它的Style,把ScrollViewer的ManipulationMode属性设为Conrtrol(必需),
只有这样才能和我们的定义的PullDownToRefreshPanel兼容。ManipulationMode属性系统默认是System;区别就是,System的
滑动效果更好。这里的ListBox的Style定义可以参考安装的SDK目录里面的系统定义,路径大致是:c->Program Files(x86)->
Microsoft SDKs->Windows Phone->v7.1->Design->System.Windows.xaml.
3、编写PullDownToRefreshPanel控件,主要是把PullDownToRefreshPanel和ScrollViewer联系起来,通过PullDwonReflesh距离滚动条的位置
来实现下拉刷新功能. 具体的实现,代码里有很好的注释。相信难不到你
4、实现自定义ListBox->CustListBox,代码很简单直接上。
1 [TemplatePart(Name = CustListBox.ScrollViewerPart, Type = typeof(ScrollViewer))] 2 3 public class CustListBox : ListBox 4 5 { 6 7 public const string ScrollViewerPart = "ScrollViewer"; 8 9 10 11 public CustListBox() 12 13 { 14 15 this.DefaultStyleKey = typeof(CustListBox); 16 17 } 18 19 20 21 #region AutoScrollMargin DependencyProperty 22 23 24 25 public static readonly DependencyProperty AutoScrollMarginProperty = DependencyProperty.Register( 26 27 "AutoScrollMargin", typeof(int), typeof(CustListBox), new PropertyMetadata(32)); 28 29 30 31 public double AutoScrollMargin 32 33 { 34 35 get 36 37 { 38 39 return (int)this.GetValue(CustListBox.AutoScrollMarginProperty); 40 41 } 42 43 set 44 45 { 46 47 this.SetValue(CustListBox.AutoScrollMarginProperty, value); 48 49 } 50 51 } 52 53 54 55 #endregion 56 57 }
5、使用要求
包含PullDwonReflesh的容器,必须直接或间接的包含滚动条。
例如:一个StackPanel包含一个PullDownToRefreshPanel 和一个ListBox,而ListBox内部包含一个ScrollViewer来实现子项的显示。
6、为了使用起来更方便,我把CustListBox和PullDownToRefreshPanel 又封装了一层
xaml
1 <UserControl x:Class="PullDwonReflesh.Themes.CustListbox" 2 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 5 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 6 7 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 8 9 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 10 11 mc:Ignorable="d" 12 13 FontFamily="{StaticResource PhoneFontFamilyNormal}" 14 15 FontSize="{StaticResource PhoneFontSizeNormal}" 16 17 Foreground="{StaticResource PhoneForegroundBrush}" 18 19 d:DesignHeight="480" d:DesignWidth="480" xmlns:my="clr-namespace:PullDwonReflesh" 20 21 22 23 xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" 24 25 x:Name="this"> 26 27 28 29 <Grid x:Name="LayoutRoot"> 30 31 <Grid.RowDefinitions> 32 33 <RowDefinition Height="Auto" /> 34 35 <RowDefinition Height="*" /> 36 37 </Grid.RowDefinitions> 38 39 <my:PullDownToRefreshPanel x:Name="refreshPanel" RefreshRequested="refreshPanel_RefreshRequested" Grid.Row="0" /> 40 41 <my:CustListBox x:Name="custListBox" Grid.Row="1" Margin="12,0,12,12" toolkit:TiltEffect.IsTiltEnabled="True" 42 43 ItemsSource="{Binding ElementName=this, Path=ItemsSource}" 44 45 ItemTemplate="{Binding ElementName=this,Path=ItemTemplate}" 46 47 SelectionChanged="CustListBox_SelectionChanged" > 48 49 <!--<ListBox.ItemsPanel> 50 51 <ItemsPanelTemplate> 52 53 <toolkit:WrapPanel/> 54 55 </ItemsPanelTemplate> 56 57 </ListBox.ItemsPanel> 58 59 <ListBox.ItemContainerStyle> 60 61 <Style TargetType="ListBoxItem"> 62 63 <Setter Property="HorizontalContentAlignment" Value="Stretch"/> 64 65 </Style> 66 67 </ListBox.ItemContainerStyle>--> 68 69 </my:CustListBox> 70 71 </Grid> 72 73 </UserControl>
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.Linq; 6 7 using System.Net; 8 9 using System.Windows; 10 11 using System.Windows.Controls; 12 13 using System.Windows.Documents; 14 15 using System.Windows.Input; 16 17 using System.Windows.Media; 18 19 using System.Windows.Media.Animation; 20 21 using System.Windows.Shapes; 22 23 using System.Collections; 24 25 26 27 namespace PullDwonReflesh.Themes 28 29 { 30 31 public partial class CustListbox : UserControl 32 33 { 34 35 public event SelectionChangedEventHandler SelectionChanged; 36 37 public event EventHandler RefreshRequested; 38 39 40 41 public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CustListbox), new PropertyMetadata("")); 42 43 public IEnumerable ItemsSource 44 45 { 46 47 get 48 49 { 50 51 return (IEnumerable)base.GetValue(ItemsSourceProperty); 52 53 } 54 55 set 56 57 { 58 59 base.SetValue(ItemsSourceProperty, value); 60 61 } 62 63 } 64 65 66 67 private DataTemplate _ItemTemplate = null; 68 69 public DataTemplate ItemTemplate 70 71 { 72 73 get 74 75 { 76 77 return _ItemTemplate; 78 79 } 80 81 set 82 83 { 84 85 _ItemTemplate = value; 86 87 custListBox.ItemTemplate = value; 88 89 } 90 91 } 92 93 94 95 public static readonly DependencyProperty IsRefreshingProperty = DependencyProperty.Register("IsRefreshing", typeof(bool), typeof(CustListbox), new PropertyMetadata(false, (d, e) => ((CustListbox)d).OnIsRefreshingChanged(e))); 96 97 public bool IsRefreshing 98 99 { 100 101 get 102 103 { 104 105 return (bool)this.GetValue(CustListbox.IsRefreshingProperty); 106 107 } 108 109 set 110 111 { 112 113 this.SetValue(CustListbox.IsRefreshingProperty, value); 114 115 } 116 117 } 118 119 120 121 protected void OnIsRefreshingChanged(DependencyPropertyChangedEventArgs e) 122 123 { 124 125 this.refreshPanel.IsRefreshing = (bool)e.NewValue; 126 127 } 128 129 130 131 public CustListbox() 132 133 { 134 135 InitializeComponent(); 136 137 } 138 139 140 141 private void CustListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 142 143 { 144 145 if (SelectionChanged != null) 146 147 { 148 149 SelectionChanged(sender, e); 150 151 } 152 153 } 154 155 156 157 private void refreshPanel_RefreshRequested(object sender, EventArgs e) 158 159 { 160 161 if (RefreshRequested != null) 162 163 { 164 165 RefreshRequested(sender, e); 166 167 } 168 169 } 170 171 } 172 173 }
到此,下拉刷新已经完成,可以拿出来单独使用。
二、ListBox滚动到底部自动加载
这个实现起来就更简单,思路
1、检测ListBox中的ScrollViewer控件状态
2、若状态不为滚动中:根据ScrollViewer的ExtentHeight与VerticalOffset,判断是否到底,并执行请求加载数据。
首先获取ScrollViewer,这个上面实现下拉的中已经得到了,这里在App定义一个变量把下拉时获得的ScrollViewer保存起来,并在这里作为目标滚动条。
然后编写获得状态函数,如下
1 private VisualStateGroup FindVisualState(FrameworkElement element, string name) 2 3 { 4 5 if (element == null) 6 7 return null; 8 9 10 11 IList groups = VisualStateManager.GetVisualStateGroups(element); 12 13 foreach (VisualStateGroup group in groups) 14 15 { 16 17 if (group.Name == name) 18 19 return group; 20 21 } 22 23 24 25 return null; 26 27 }
接下来根据状态的改变做相应的加载功能,代码如下
1 void visualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e) 2 3 { 4 5 var visualState = e.NewState.Name; 6 7 if (visualState == "NotScrolling") 8 9 { 10 11 var v1 = _ScrollViewer.ExtentHeight - _ScrollViewer.VerticalOffset; 12 13 var v2 = _ScrollViewer.ViewportHeight * 1.5; 14 15 16 17 if (v1 <= v2 && !custListBox.IsRefreshing) 18 19 { 20 21 AddString(index, 20); 22 23 visualState += "_End"; 24 25 } 26 27 } 28 29 }
最后,在页面的loaded里把这些代码串联起来就OK了。如下:
1 private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) 2 3 { 4 5 if (_IsHookedScrollEvent) 6 7 return; 8 9 _ScrollViewer = App._ScrollViewer; 10 11 if (_ScrollViewer != null) 12 13 { 14 15 _IsHookedScrollEvent = true; 16 17 FrameworkElement element = VisualTreeHelper.GetChild(_ScrollViewer, 0) as FrameworkElement; 18 19 if (element != null) 20 21 { 22 23 VisualStateGroup visualStateGroup = FindVisualState(element, "ScrollStates"); 24 25 visualStateGroup.CurrentStateChanged += new EventHandler<VisualStateChangedEventArgs>(visualStateGroup_CurrentStateChanged); 26 27 } 28 29 } 30 31 }
本文参考:http://www.hugwp.com/thread-2058-1.html
和Jason Ginchereau的博客
本文版权归作者和卤面网所有,
转载请标明原始出处