• 《ListBox》———何如实现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.

    View Code
      1 <Style TargetType="rlb:CustListBox">
      2 
      3 <Setter Property="Background" Value="Transparent" />
      4 
      5 <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}" />
      6 
      7 <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
      8 
      9 <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
     10 
     11 <Setter Property="BorderThickness" Value="0" />
     12 
     13 <Setter Property="BorderBrush" Value="Transparent" />
     14 
     15 <Setter Property="Padding" Value="0" />
     16 
     17 <Setter Property="Template">
     18 
     19 <Setter.Value>
     20 
     21 <ControlTemplate TargetType="rlb:CustListBox">
     22 
     23 <Grid>
     24 
     25 <Grid.ColumnDefinitions>
     26 
     27 <ColumnDefinition Width="*" />
     28 
     29 <ColumnDefinition Width="Auto" />
     30 
     31 </Grid.ColumnDefinitions>
     32 
     33 <!-- ScrollViewer的ManipulationMode属性必须设为Conrtrol才能和PullDownToRefreshPanel兼容 -->
     34 
     35 <!-- ScrollViewer的ManipulationMode属性默认是System它能提供更好的滚动效果 -->
     36 
     37 <ScrollViewer x:Name="ScrollViewer" Grid.ColumnSpan="2" ManipulationMode="Control" Foreground="{TemplateBinding Foreground}" Background="{TemplateBinding Background}"
     38 
     39 BorderBrush="Transparent" BorderThickness="0" Padding="{TemplateBinding Padding}">
     40 
     41 <ItemsPresenter />
     42 
     43 </ScrollViewer>
     44 
     45 <!-- The DragInterceptor sits on top of the item DragHandles and intercepts drag events
     46 
     47 so that the capture is not lost when the item container is removed from the panel.
     48 
     49 Its width must be equal to the width of the item DragHandles. -->
     50 
     51 <Canvas x:Name="DragInterceptor" Grid.Column="1" Margin="{TemplateBinding Padding}" Background="Transparent" VerticalAlignment="Stretch" Width="52">
     52 
     53 <Image x:Name="DragIndicator" Visibility="Collapsed">
     54 
     55 <Image.RenderTransform>
     56 
     57 <TranslateTransform />
     58 
     59 </Image.RenderTransform>
     60 
     61 </Image>
     62 
     63 </Canvas>
     64 
     65 <Canvas x:Name="RearrangeCanvas" Grid.ColumnSpan="2" Margin="{TemplateBinding Padding}" Background="Transparent" Visibility="Collapsed" />
     66 
     67 </Grid>
     68 
     69 </ControlTemplate>
     70 
     71 </Setter.Value>
     72 
     73 </Setter>
     74 
     75 </Style>
     76 
     77 
     78 
     79 <Style TargetType="rlb:PullDownToRefreshPanel">
     80 
     81 <Setter Property="Background" Value="Transparent" />
     82 
     83 <Setter Property="PullingDownTemplate">
     84 
     85 <Setter.Value>
     86 
     87 <DataTemplate>
     88 
     89 <TextBlock Margin="0,16,0,0" Style="{StaticResource PhoneTextGroupHeaderStyle}" TextAlignment="Center" FontStyle="Italic" Text="Pull down to refresh." />
     90 
     91 </DataTemplate>
     92 
     93 </Setter.Value>
     94 
     95 </Setter>
     96 
     97 <Setter Property="ReadyToReleaseTemplate">
     98 
     99 <Setter.Value>
    100 
    101 <DataTemplate>
    102 
    103 <TextBlock Margin="0,16,0,0" Style="{StaticResource PhoneTextGroupHeaderStyle}" TextAlignment="Center" FontWeight="Bold" Text="Release to refresh!" />
    104 
    105 </DataTemplate>
    106 
    107 </Setter.Value>
    108 
    109 </Setter>
    110 
    111 <Setter Property="RefreshingTemplate">
    112 
    113 <Setter.Value>
    114 
    115 <DataTemplate>
    116 
    117 <ProgressBar Margin="0,4,0,4" IsIndeterminate="True" />
    118 
    119 </DataTemplate>
    120 
    121 </Setter.Value>
    122 
    123 </Setter>
    124 
    125 <Setter Property="Template">
    126 
    127 <Setter.Value>
    128 
    129 <ControlTemplate TargetType="rlb:PullDownToRefreshPanel">
    130 
    131 <StackPanel x:Name="PullDownContainer" HorizontalAlignment="Stretch">
    132 
    133 <StackPanel.Resources>
    134 
    135 <rlb:NegativeValueConverter x:Key="NegativeValueConverter" />
    136 
    137 </StackPanel.Resources>
    138 
    139 <StackPanel x:Name="PullingDownPanel" Background="{TemplateBinding Background}" Height="{TemplateBinding PullDistance}" Opacity="{TemplateBinding PullFraction}"
    140 
    141 Margin="{Binding PullDistance, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource NegativeValueConverter}, ConverterParameter=Bottom}" Visibility="Collapsed">
    142 
    143 <ContentPresenter ContentTemplate="{TemplateBinding PullingDownTemplate}" />
    144 
    145 </StackPanel>
    146 
    147 <StackPanel x:Name="ReadyToReleasePanel" Background="{TemplateBinding Background}" Height="{TemplateBinding PullDistance}"
    148 
    149 Margin="{Binding PullDistance, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource NegativeValueConverter}, ConverterParameter=Bottom}" Visibility="Collapsed">
    150 
    151 <ContentPresenter ContentTemplate="{TemplateBinding ReadyToReleaseTemplate}" />
    152 
    153 </StackPanel>
    154 
    155 <StackPanel x:Name="RefreshingPanel" Background="{TemplateBinding Background}" Visibility="Collapsed">
    156 
    157 <ContentPresenter ContentTemplate="{TemplateBinding RefreshingTemplate}" />
    158 
    159 </StackPanel>
    160 
    161 <VisualStateManager.VisualStateGroups>
    162 
    163 <VisualStateGroup x:Name="ActivityStates">
    164 
    165 <VisualState x:Name="Inactive" />
    166 
    167 <VisualState x:Name="PullingDown">
    168 
    169 <Storyboard>
    170 
    171 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PullingDownPanel" Storyboard.TargetProperty="Visibility">
    172 
    173 <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
    174 
    175 </ObjectAnimationUsingKeyFrames>
    176 
    177 </Storyboard>
    178 
    179 </VisualState>
    180 
    181 <VisualState x:Name="ReadyToRelease">
    182 
    183 <Storyboard>
    184 
    185 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ReadyToReleasePanel" Storyboard.TargetProperty="Visibility">
    186 
    187 <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
    188 
    189 </ObjectAnimationUsingKeyFrames>
    190 
    191 </Storyboard>
    192 
    193 </VisualState>
    194 
    195 <VisualState x:Name="Refreshing">
    196 
    197 <Storyboard>
    198 
    199 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RefreshingPanel" Storyboard.TargetProperty="Visibility">
    200 
    201 <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
    202 
    203 </ObjectAnimationUsingKeyFrames>
    204 
    205 </Storyboard>
    206 
    207 </VisualState>
    208 
    209 </VisualStateGroup>
    210 
    211 </VisualStateManager.VisualStateGroups>
    212 
    213 </StackPanel>
    214 
    215 </ControlTemplate>
    216 
    217 </Setter.Value>
    218 
    219 </Setter>
    220 
    221 </Style>

    3、编写PullDownToRefreshPanel控件,主要是把PullDownToRefreshPanel和ScrollViewer联系起来,通过PullDwonReflesh距离滚动条的位置
         来实现下拉刷新功能. 具体的实现,代码里有很好的注释。相信难不到你

    View Code
      1 /// <summary>
      2 
      3 /// 给滚得条添加下拉刷新机制
      4 
      5 /// </summary>
      6 
      7 /// <remarks>
      8 
      9 /// 使用PullDwonReflesh距离滚动条的位置来实现下拉刷新功能. 
     10 
     11 /// 包含PullDwonReflesh的容器,必须直接或间接的包含滚动条。
     12 
     13 /// 例如:一个StackPanel包含一个PullDownToRefreshPanel 和一个ListBox,
     14 
     15 /// 而ListBox内部包含一个ScrollViewer来实现子项的显示。
     16 
     17 /// </remarks>
     18 
     19 [TemplateVisualState(Name = PullDownToRefreshPanel.InactiveVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
     20 
     21 [TemplateVisualState(Name = PullDownToRefreshPanel.PullingDownVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
     22 
     23 [TemplateVisualState(Name = PullDownToRefreshPanel.ReadyToReleaseVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
     24 
     25 [TemplateVisualState(Name = PullDownToRefreshPanel.RefreshingVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
     26 
     27 public class PullDownToRefreshPanel : Control
     28 
     29 {
     30 
     31 #region Visual state name constants
     32 
     33 //滚动中
     34 
     35 private const string ActivityVisualStateGroup = "ActivityStates";
     36 
     37 //无操作
     38 
     39 private const string InactiveVisualState = "Inactive";
     40 
     41 //下拉
     42 
     43 private const string PullingDownVisualState = "PullingDown";
     44 
     45 //松手刷新
     46 
     47 private const string ReadyToReleaseVisualState = "ReadyToRelease";
     48 
     49 //刷新中
     50 
     51 private const string RefreshingVisualState = "Refreshing";
     52 
     53 
     54 
     55 #endregion
     56 
     57 
     58 
     59 /// <summary>
     60 
     61 /// 用于绑定下拉文字显示,松手刷新的滚动条
     62 
     63 /// </summary>
     64 
     65 private ScrollViewer targetScrollViewer;
     66 
     67 
     68 
     69 /// <summary>
     70 
     71 /// 构造函数
     72 
     73 /// </summary>
     74 
     75 public PullDownToRefreshPanel()
     76 
     77 {
     78 
     79 this.DefaultStyleKey = typeof(PullDownToRefreshPanel);
     80 
     81 this.LayoutUpdated += this.PullDownToRefreshPanel_LayoutUpdated;
     82 
     83 }
     84 
     85 
     86 
     87 /// <summary>
     88 
     89 /// 当滚动条下拉到指定程度(PullThreshold)时引发这个事件,并显示松手刷新
     90 
     91 /// 如果开始刷新的时候事件要把IsRefreshing设置为true
     92 
     93 /// 当刷新完成时把IsRefreshing设回false.
     94 
     95 /// </summary>
     96 
     97 public event EventHandler RefreshRequested;
     98 
     99 
    100 
    101 #region IsRefreshing DependencyProperty 是否正在刷新
    102 
    103 
    104 
    105 public static readonly DependencyProperty IsRefreshingProperty = DependencyProperty.Register
    106 
    107 (
    108 
    109 "IsRefreshing", typeof(bool), typeof(PullDownToRefreshPanel),
    110 
    111 new PropertyMetadata(false, (d, e) => 
    112 
    113 {
    114 
    115 ((PullDownToRefreshPanel)d).OnIsRefreshingChanged(e); 
    116 
    117 })
    118 
    119 );
    120 
    121 
    122 
    123 public bool IsRefreshing
    124 
    125 {
    126 
    127 get
    128 
    129 {
    130 
    131 return (bool)this.GetValue(PullDownToRefreshPanel.IsRefreshingProperty);
    132 
    133 }
    134 
    135 set
    136 
    137 {
    138 
    139 this.SetValue(PullDownToRefreshPanel.IsRefreshingProperty, value);
    140 
    141 }
    142 
    143 }
    144 
    145 
    146 
    147 protected void OnIsRefreshingChanged(DependencyPropertyChangedEventArgs e)
    148 
    149 {
    150 
    151 string activityState = (bool)e.NewValue ? PullDownToRefreshPanel.RefreshingVisualState : PullDownToRefreshPanel.InactiveVisualState;
    152 
    153 VisualStateManager.GoToState(this, activityState, false);
    154 
    155 }
    156 
    157 
    158 
    159 #endregion
    160 
    161 
    162 
    163 #region PullThreshold DependencyProperty 设置下拉距离
    164 
    165 
    166 
    167 public static readonly DependencyProperty PullThresholdProperty = DependencyProperty.Register(
    168 
    169 "PullThreshold", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(100.0));
    170 
    171 
    172 
    173 /// <summary>
    174 
    175 /// 得到或设置一个滚动条从顶部下拉的最小距离,当达到这个距离松手时会触发刷新事件
    176 
    177 /// 默认距离是100. 推荐最大值不要超过125.
    178 
    179 /// </summary>
    180 
    181 public double PullThreshold
    182 
    183 {
    184 
    185 get
    186 
    187 {
    188 
    189 return (double)this.GetValue(PullDownToRefreshPanel.PullThresholdProperty);
    190 
    191 }
    192 
    193 set
    194 
    195 {
    196 
    197 this.SetValue(PullDownToRefreshPanel.PullThresholdProperty, value);
    198 
    199 }
    200 
    201 }
    202 
    203 
    204 
    205 #endregion
    206 
    207 
    208 
    209 #region PullDistance DependencyProperty 滚动条拉下的距离
    210 
    211 
    212 
    213 public static readonly DependencyProperty PullDistanceProperty = DependencyProperty.Register(
    214 
    215 "PullDistance", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(0.0));
    216 
    217 
    218 
    219 /// <summary>
    220 
    221 /// 得到滚动条已经拉下的距离。
    222 
    223 /// 用它绑定图形距离
    224 
    225 /// </summary>
    226 
    227 public double PullDistance
    228 
    229 {
    230 
    231 get
    232 
    233 {
    234 
    235 return (double)this.GetValue(PullDownToRefreshPanel.PullDistanceProperty);
    236 
    237 }
    238 
    239 private set
    240 
    241 {
    242 
    243 this.SetValue(PullDownToRefreshPanel.PullDistanceProperty, value);
    244 
    245 }
    246 
    247 }
    248 
    249 
    250 
    251 #endregion 
    252 
    253 
    254 
    255 #region PullFraction DependencyProperty 相对PullDistance拉下距离分值
    256 
    257 
    258 
    259 public static readonly DependencyProperty PullFractionProperty = DependencyProperty.Register(
    260 
    261 "PullFraction", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(0.0));
    262 
    263 
    264 
    265 /// <summary>
    266 
    267 /// 得到滚动条从顶部拉下的距离分数。即拉下PullThreshold的几分之几,取值范围(0.0 - 1.0)
    268 
    269 /// </summary>
    270 
    271 public double PullFraction
    272 
    273 {
    274 
    275 get
    276 
    277 {
    278 
    279 return (double)this.GetValue(PullDownToRefreshPanel.PullFractionProperty);
    280 
    281 }
    282 
    283 private set
    284 
    285 {
    286 
    287 this.SetValue(PullDownToRefreshPanel.PullFractionProperty, value);
    288 
    289 }
    290 
    291 }
    292 
    293 
    294 
    295 #endregion
    296 
    297 
    298 
    299 #region PullingDownTemplate DependencyProperty 提示下拉刷新的模板
    300 
    301 
    302 
    303 public static readonly DependencyProperty PullingDownTemplateProperty = DependencyProperty.Register(
    304 
    305 "PullingDownTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);
    306 
    307 
    308 
    309 /// <summary>
    310 
    311 /// Gets or sets a template that is progressively revealed has the ScrollViewer is pulled down.
    312 
    313 /// </summary>
    314 
    315 public DataTemplate PullingDownTemplate
    316 
    317 {
    318 
    319 get
    320 
    321 {
    322 
    323 return (DataTemplate)this.GetValue(PullDownToRefreshPanel.PullingDownTemplateProperty);
    324 
    325 }
    326 
    327 set
    328 
    329 {
    330 
    331 this.SetValue(PullDownToRefreshPanel.PullingDownTemplateProperty, value);
    332 
    333 }
    334 
    335 }
    336 
    337 
    338 
    339 #endregion
    340 
    341 
    342 
    343 #region ReadyToReleaseTemplate DependencyProperty 提示松开刷新的模板
    344 
    345 
    346 
    347 public static readonly DependencyProperty ReadyToReleaseTemplateProperty = DependencyProperty.Register(
    348 
    349 "ReadyToReleaseTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);
    350 
    351 
    352 
    353 /// <summary>
    354 
    355 /// Gets or sets the template that is displayed after the ScrollViewer is pulled down past
    356 
    357 /// the PullThreshold.
    358 
    359 /// </summary>
    360 
    361 public DataTemplate ReadyToReleaseTemplate
    362 
    363 {
    364 
    365 get
    366 
    367 {
    368 
    369 return (DataTemplate)this.GetValue(PullDownToRefreshPanel.ReadyToReleaseTemplateProperty);
    370 
    371 }
    372 
    373 set
    374 
    375 {
    376 
    377 this.SetValue(PullDownToRefreshPanel.ReadyToReleaseTemplateProperty, value);
    378 
    379 }
    380 
    381 }
    382 
    383 
    384 
    385 #endregion
    386 
    387 
    388 
    389 #region RefreshingTemplate DependencyProperty 提示正在刷新的模板
    390 
    391 
    392 
    393 public static readonly DependencyProperty RefreshingTemplateProperty = DependencyProperty.Register(
    394 
    395 "RefreshingTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);
    396 
    397 
    398 
    399 /// <summary>
    400 
    401 /// Gets or sets the template that is displayed while the ScrollViewer is refreshing.
    402 
    403 /// </summary>
    404 
    405 public DataTemplate RefreshingTemplate
    406 
    407 {
    408 
    409 get
    410 
    411 {
    412 
    413 return (DataTemplate)this.GetValue(PullDownToRefreshPanel.RefreshingTemplateProperty);
    414 
    415 }
    416 
    417 set
    418 
    419 {
    420 
    421 this.SetValue(PullDownToRefreshPanel.RefreshingTemplateProperty, value);
    422 
    423 }
    424 
    425 }
    426 
    427 
    428 
    429 #endregion
    430 
    431 
    432 
    433 #region Initial setup 初始化设置
    434 
    435 
    436 
    437 private void PullDownToRefreshPanel_LayoutUpdated(object sender, EventArgs e)
    438 
    439 {
    440 
    441 if (this.targetScrollViewer == null)
    442 
    443 {
    444 
    445 //找到要绑定的目标滚动条
    446 
    447 this.targetScrollViewer = FindVisualElement<ScrollViewer>(VisualTreeHelper.GetParent(this));
    448 
    449 App._ScrollViewer = targetScrollViewer;
    450 
    451 if (this.targetScrollViewer != null)
    452 
    453 {
    454 
    455 this.targetScrollViewer.MouseMove += this.targetScrollViewer_MouseMove;
    456 
    457 this.targetScrollViewer.MouseLeftButtonUp += this.targetScrollViewer_MouseLeftButtonUp;
    458 
    459 
    460 
    461 //滚得条的类型必须是控件类型
    462 
    463 if (this.targetScrollViewer.ManipulationMode != ManipulationMode.Control)
    464 
    465 {
    466 
    467 throw new InvalidOperationException("PullDownToRefreshPanel requires the ScrollViewer " +
    468 
    469 "to have ManipulationMode=Control. (ListBoxes may require re-templating.");
    470 
    471 }
    472 
    473 }
    474 
    475 }
    476 
    477 }
    478 
    479 
    480 
    481 #endregion
    482 
    483 
    484 
    485 #region Pull-down detection 下拉检测
    486 
    487 
    488 
    489 /// <summary>
    490 
    491 /// 当滚动条被拖拽时,显示动画和对应的控件模型
    492 
    493 /// </summary>
    494 
    495 private void targetScrollViewer_MouseMove(object sender, MouseEventArgs e)
    496 
    497 {
    498 
    499 UIElement scrollContent = (UIElement)this.targetScrollViewer.Content;
    500 
    501 CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;
    502 
    503 if (ct != null && !this.IsRefreshing)
    504 
    505 {
    506 
    507 string activityState;
    508 
    509 if (ct.TranslateY > this.PullThreshold)
    510 
    511 {
    512 
    513 this.PullDistance = ct.TranslateY;
    514 
    515 this.PullFraction = 1.0;
    516 
    517 activityState = PullDownToRefreshPanel.ReadyToReleaseVisualState;
    518 
    519 }
    520 
    521 else if (ct.TranslateY > 0)
    522 
    523 {
    524 
    525 this.PullDistance = ct.TranslateY;
    526 
    527 double threshold = this.PullThreshold;
    528 
    529 this.PullFraction = threshold == 0.0 ? 1.0 : Math.Min(1.0, ct.TranslateY / threshold);
    530 
    531 activityState = PullDownToRefreshPanel.PullingDownVisualState;
    532 
    533 }
    534 
    535 else
    536 
    537 {
    538 
    539 this.PullDistance = 0;
    540 
    541 this.PullFraction = 0;
    542 
    543 activityState = PullDownToRefreshPanel.InactiveVisualState;
    544 
    545 }
    546 
    547 VisualStateManager.GoToState(this, activityState, false);
    548 
    549 }
    550 
    551 }
    552 
    553 
    554 
    555 /// <summary>
    556 
    557 /// 当松手时检查是否要刷新
    558 
    559 /// </summary>
    560 
    561 private void targetScrollViewer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    562 
    563 {
    564 
    565 UIElement scrollContent = (UIElement)this.targetScrollViewer.Content;
    566 
    567 CompositeTransform ct = scrollContent.RenderTransform as CompositeTransform;
    568 
    569 if (ct != null && !this.IsRefreshing)
    570 
    571 {
    572 
    573 VisualStateManager.GoToState(this, PullDownToRefreshPanel.InactiveVisualState, false);
    574 
    575 this.PullDistance = 0;
    576 
    577 this.PullFraction = 0;
    578 
    579 
    580 
    581 if (ct.TranslateY >= this.PullThreshold)
    582 
    583 {
    584 
    585 EventHandler handler = this.RefreshRequested;
    586 
    587 if (handler != null)
    588 
    589 {
    590 
    591 handler(this, EventArgs.Empty);
    592 
    593 }
    594 
    595 }
    596 
    597 }
    598 
    599 }
    600 
    601 
    602 
    603 #endregion
    604 
    605 
    606 
    607 #region Utility methods 查找第一个符合要求的元素方法
    608 
    609 
    610 
    611 /// <summary>
    612 
    613 /// 查找容器内所有元素,并返回第一个是T类型的元素
    614 
    615 /// </summary>
    616 
    617 /// <typeparam name="T">要搜索的元素类型</typeparam>
    618 
    619 /// <param name="initial">要搜索的容器</param>
    620 
    621 /// <returns>返回要找的元素,没找到返回null</returns>
    622 
    623 private static T FindVisualElement<T>(DependencyObject container) where T : DependencyObject
    624 
    625 {
    626 
    627 Queue<DependencyObject> childQueue = new Queue<DependencyObject>();
    628 
    629 childQueue.Enqueue(container);
    630 
    631 
    632 
    633 while (childQueue.Count > 0)
    634 
    635 {
    636 
    637 DependencyObject current = childQueue.Dequeue();
    638 
    639 
    640 
    641 System.Diagnostics.Debug.WriteLine(current.GetType().ToString());
    642 
    643 
    644 
    645 T result = current as T;
    646 
    647 if (result != null && result != container)
    648 
    649 {
    650 
    651 return result;
    652 
    653 }
    654 
    655 
    656 
    657 int childCount = VisualTreeHelper.GetChildrenCount(current);
    658 
    659 for (int childIndex = 0; childIndex < childCount; childIndex++)
    660 
    661 {
    662 
    663 childQueue.Enqueue(VisualTreeHelper.GetChild(current, childIndex));
    664 
    665 }
    666 
    667 }
    668 
    669 
    670 
    671 return null;
    672 
    673 }
    674 
    675 
    676 
    677 #endregion
    678 
    679 }

    4、实现自定义ListBox->CustListBox,代码很简单直接上。

    View Code
     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

    View Code
     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>
    View Code
      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保存起来,并在这里作为目标滚动条。
    然后编写获得状态函数,如下

    View Code
     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的博客

    源码:猛击下载

    本文版权归作者和卤面网所有,
    转载请标明原始出处

  • 相关阅读:
    几个很实用的BOM属性对象方法
    JavaScript hash
    WPF中ControlTemplate和DataTemplate的区别
    C#性能测试方法
    Lc.exe已退出,代码为-1
    C#遍历enum类型
    WPF画线问题,几千条以后就有明显的延迟了。
    WPF学习笔记(3):Path绘制命令zz
    WPF: 旋转Thumb后,DragDelta移动距离出错的解决
    在Revit中如何显示附件模块(Add Ins) 这个命令页?zz
  • 原文地址:https://www.cnblogs.com/qq278360339/p/2530532.html
Copyright © 2020-2023  润新知