一、下拉刷新
下拉刷新实现思路:
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的博客

源码:http://files.cnblogs.com/qq278360339/PullDwonReflesh(%E4%B8%8B%E6%8B%89%E5%88%B7%E6%96%B0%EF%BC%8C%E5%88%B0%E5%BA%95%E8%87%AA%E5%8A%A8%E5%8A%A0%E8%BD%BD).zip

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

转载于:https://www.cnblogs.com/wuzhsh/archive/2012/09/04/2670307.html

仿新浪微博实现ListBox下拉刷新和到底部自动加载相关推荐

  1. Android新浪微博分页加载,Android仿新浪微博自定义ListView下拉刷新(4)

    自定义PullToRefreshListView继承ListView,在ListView头部添加一个下拉的头部布局.跟ListView用法完全一致. 此处详细介绍Adapter的详细代码. 1.首先给 ...

  2. JRoll 2 使用文档(史上最强大的下拉刷新,滚动,无限加载插件)

    概述 说明 JRoll,一款能滚起上万条数据,具有滑动加速.回弹.缩放.滚动条.滑动事件等功能,兼容CommonJS/AMD/CMD模块规范,开源,免费的轻量级html5滚动插件. JRoll第二版是 ...

  3. 微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据 下篇

    事件(wxml)>bindscrolltolower="onscrollLower" slice() 方法可从已有的数组中返回选定的元素. 请注意,该方法并不会修改数组,而是 ...

  4. 仿新浪微博的ListView下拉更新功能

    由于最近做的项目中也用到了这个功能,今天刚好实现了下,就趁现在有时间写篇博客分享下.在做的时候也参考了下别人的代码,毕竟站在巨人的肩膀上才会是自己更加强大.哈哈!先看看新浪的下拉更新是什么样的吧! O ...

  5. Android开发笔记(一百六十四)仿京东首页的下拉刷新

    上一篇文章介绍了高仿京东的沉浸式状态栏,可是跟京东首页的头部轮播图相比,依然有三处缺憾: 1.京东的头部Banner上方,除了有悬浮着的状态栏,状态栏下面还有一行悬浮工具栏,内嵌扫一扫图标.搜索框,以 ...

  6. 微信小程序仿系统自带下拉刷新效果

    微信小程序仿系统自带下拉刷新效果 前言 思路分析 实现 尾巴 前言 看到标题也许有人会说:系统不是已经自带了下拉刷新,你去仿照系统的下拉刷新是不是吃多了没事干?其实真相并不是这样的.在微信小程序手把手 ...

  7. 探索SwipeRefreshLayout配合自定义ListView完成下拉刷新、滑到底部自动加载更多

    在Android开发过程中经常需要实现上下拉刷新功能,Google推出的下拉刷新控件SwipeRefreshLayout(彩虹条),由于官方版本只有下拉刷新而没有上拉加载更多的功能,很多人也尝试在这个 ...

  8. html底部自动加载下一页,js页面滚动到底部时自动加载下一页数据

    一段滚动到页面底部自动加载更多数据的js代码,需要jquery.js . jquey.tmpl.js 支持 var Data = { PageCount: 1, PageSize: 30, IsCom ...

  9. jQuery插件实现网页底部自动加载-类似新浪微博

    要实现滚动条滚到底部自动加载后续内容到内容到底部的功能,用jQuery非常方便的,只要知道什么时候滚动到了底部就好办了. $(document).scrollTop() //获取垂直滚动条到顶部的距离 ...

最新文章

  1. 值得收藏!基于激光雷达数据的深度学习目标检测方法大合集(下)
  2. jquery瀑布流布局和鼠标滚动加载
  3. hdc和hwnd的区别
  4. Redis学习笔记之入门基础知识——简介
  5. Atomic Integer 原理分析-get方法
  6. LeetCode 1267. 统计参与通信的服务器(计数)
  7. java 连接mongodb
  8. 这月到手的工资多了没?
  9. 6.企业安全建设指南(金融行业安全架构与技术实践) --- 安全培训
  10. 锂电池欧姆内阻和极化内阻
  11. Android Studio连接安卓模拟器教程
  12. 互联网诞生记: 浪成于微澜之间
  13. 手机定位导航不准确?做到这几点就好了!
  14. 拦截导弹问题(递归)
  15. 服务条目与采购订单、采购申请、工单、项目及WBS的关系
  16. 同花顺的故事(7)业务逻辑相关
  17. rosetta stone fatal application error: #1141错误 (罗塞塔石碑1141) 解决方法
  18. SQL 涉及3个表的连接
  19. 豆瓣,流行的秘密(解密流行真相)
  20. 抓包精灵NetCapture APP抓包教程《齐全》

热门文章

  1. leetcode - 84. 柱状图中最大的矩形
  2. RNN调试错误:lstm_cell = tf.contrib.rnn.core_rnn_cell.BasicLSTMCell(lstm_size) 方法已失效
  3. 概率论和数理统计 - 04 -
  4. mysql5.6semi plugin_mysql5.6 semi replication 半同步复制配置(示例代码)
  5. mysql floor报错_【学习笔记】MYSQL的floor报错原理分析总结
  6. 密码学原理与实践_到底什么是防火墙入侵检测密码学身份认证?如何高效建立网络安全知识体系?...
  7. flyme7 android彩蛋,魅族 15 系列开启预约,Flyme7 或是发布会彩蛋
  8. 保利管道微服务1_.netcore 3.1高性能微服务架构:webapi规范
  9. python单元测试mock_Python单元测试的Mock是怎么回事
  10. math库是python语言的数学模块_Python 数学模块(Math)