++++++++++++++++++++++++++++++++++++++++++
本文系本站原创,欢迎转载! 转载请注明出处:
http://blog.csdn.net/mr_raptor/article/details/7358226
++++++++++++++++++++++++++++++++++++++++++
前言
最近一些时间有一些杂事,没有及时更新Blog,Sorry,现在将下拉刷新更新完。
上一节讲了,下拉刷新的基本框架结构,现在我们在上节的基础上添加内容。
WindowsPhone下拉刷新控件 - PullRefreshListBox(一)
在第一节中,解决了:Control选择问题和样式模板,本节主要内容包含:
思路:
获得点击Y坐标startY,在手指移动时将新的移动坐标currentY与startY对比,如果currentY - startY的值大于了一定的坐标值就设置刷新显示区,同时设置控件的依赖项属性,同时还开始动画效果。
1. 用户操作及距离计算问题
用户操作主要是重写了基类里的三个方法:OnManipulationCompleted,OnManipulationStarted,OnMouseMove。
通过OnManipulationStarted(ManipulationStartedEventArgs e)参数ManipulationStartedEventArgs 可以获得Y坐标点
protected override void OnManipulationStarted(ManipulationStartedEventArgs e) { base.OnManipulationStarted(e); startY = e.ManipulationOrigin.Y; ...
通过OnMouseMove(MouseEventArgs e)参数MouseEventArgs 可以获得当前相对参照控件的坐标点Y
protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); double currentY = e.GetPosition(this.ScrollViewer).Y; ...
在OnManipulationCompleted方法里将下拉显示关闭掉,同时关闭动画效果,修改依赖项属性 PullDownPanel.Visibility = Visibility.Collapsed;
具体代码如下:
private double startY, endY; private double scrolledOffset; private bool isShowing = false; protected override void OnManipulationStarted(ManipulationStartedEventArgs e) { base.OnManipulationStarted(e); startY = e.ManipulationOrigin.Y; scrolledOffset = this.ScrollViewer.VerticalOffset; //Debug.WriteLine("OnManipulationStarted startY = " + startY + " scrolledY = " + scrolledOffset); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); double currentY = e.GetPosition(this.ScrollViewer).Y; double offset = currentY - startY; if (scrolledOffset - offset < -30.0) { setVisible(); } else { setInvisible(); } } private void setInvisible() { if (!isShowing) return; isShowing = false; //Debug.WriteLine("stop show"); ShowAnimation.Stop(); TransformAnimation.Stop(); PullDownPanel.Visibility = Visibility.Collapsed; } private void setVisible() { if (isShowing) return; isShowing = true; Debug.WriteLine("show"); PullDownPanel.Visibility = Visibility.Visible; ShowAnimation.Begin(); TransformAnimation.Begin(); DataRefreshedEventHandler handler = DataRefreshed; if (handler != null) { handler(this, null); } }
protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e) { base.OnManipulationCompleted(e); endY = e.ManipulationOrigin.Y; //Debug.WriteLine("OnManipulationCompleted endY" + endY);
setInvisible(); }
这个时候编译一个工程会发现,我们的下拉能够被正常识别到,当Y方向拉动超过30像素,就会有显示下拉显示区内容。
但是,这时还没有动画效果,我们为它添加上动画效果,其中下拉显示区有淡进淡出的效果,然后,有一个转动的就箭头,表示正在刷新。
2. 添加动画效果
动画效果是发生在下拉时,所以我们要回去Generic.xaml样式模板里,在Grid.Resources中添加如下代码:
<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:PullRefreshListBox"> <Grid> <Grid.Resources> <Storyboard x:Name="showAnimation"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PullDownPanel"> <EasingDoubleKeyFrame KeyTime="0" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Name="transformAnimation" RepeatBehavior="10x"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="RefreshImage" RepeatBehavior="Forever"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="90"/> <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="180"/> <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="270"/> <EasingDoubleKeyFrame KeyTime="0:0:1.6" Value="360"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </Grid.Resources>
其中,showAnimation动画为在0.8秒内其透明度由0到1从全透明到不透明,从而实现淡进淡出的效果,transformAnimation动画为小转轮在0.4秒里顺时针重复转动90度,实现转动效果。
由于我们要在下拉条件成立时,开启动画,因此要在OnApplyTemplate方法中得到两个动画的引用:
public override void OnApplyTemplate() { // the Animation of the Image and RefreshText TransformAnimation = this.GetTemplateChild(TransformAnimationName) as Storyboard; ShowAnimation = this.GetTemplateChild(ShowAnimationName) as Storyboard;
这样,在setInvisible和setVisible方法里分别开启和关闭动画效果就可以了。
编译当前代码,再执行,会发现,下拉时,动画效果已经实现,但是下拉列表基本的点击事件不能使用了。
这是因为我们的下拉控件继承自ItemsControl控件,对于ItemsControl控件里的列表内容,要自己添加其列表项的点击行为。
3. 添加Item点击事件
当用户在列表中选择一个item时,我们希望将所有的Item都放到一个栈里,然后从Tap事件里找到被Tap的对象,然后从栈里遍历该对象,如果找到,就将SelectedItem与SelectedIndex设置为其对应的值,在其属性改变事件里响应点击行为。
在OnApplyTemplate方法里注册Tap 事件:
public override void OnApplyTemplate() { // add the Event handler for the Tap action on ScrollViewer this.ScrollViewer.Tap += new EventHandler<GestureEventArgs>(ScrollViewer_Tap);
Tap事件处理:
void ScrollViewer_Tap(object sender, GestureEventArgs e) { DependencyObject dpendObj = e.OriginalSource as DependencyObject; object obj; // get StackPanel of the listbox StackPanel itemsStack = VisualTreeHelper.GetChild(_ItemsContainer, 0) as StackPanel; if ((obj = RetriveElementOfTree<ItemsPresenter>(dpendObj)) != null) { //ItemsPresenter itemsGroup = obj as ItemsPresenter; if (childObjStack != null) { // pop StackPanel childObjStack.Pop(); // the event Element obj = childObjStack.Pop(); } for(int i = 0; i < itemsStack.Children.Count; i++) { if (object.Equals(obj, itemsStack.Children[i])) { // found the Taped obj saveSelectedItem = itemsStack.Children[i]; SelectedIndex = i; SelectedItem = saveSelectedItem; OnSelectedItem(SelectedItem); break; } } Debug.WriteLine("ScrollViewer_Tap::"+VisualTreeHelper.GetChildrenCount(itemsStack)); } }
VisualTreeHelper.GetChild是个很牛B的东西,它可以从指定的控件里获得对应的子控件引用。
VisualTreeHelper.GetChildVisualTreeHelper.GetChild这里我们从装有所有Items的控件父控件_ItemsContainer里得到依次得到每一个子控件放到childObjStack栈里,如下图所示。
具体的入栈如下代码所示:
private Stack<DependencyObject> childObjStack; private object RetriveElementOfTree<T>(DependencyObject tree) { if (childObjStack == null) childObjStack = new Stack<DependencyObject>(); else childObjStack.Clear(); while (tree != null) { if (tree is T) { return tree; } childObjStack.Push(tree); tree = VisualTreeHelper.GetParent(tree); } return null; }在获得了被点击的Item之后,主动回调SelectionChangedEvent事件,响应用户点击Item操作。
private void OnSelectedItem(object obj) { SelectionChangedEventHandler handler = SelectionChanged; if (handler != null) { _selectionList[0] = obj; // Call back user define Event handler(this, new SelectionChangedEventArgs(EmptyList, _selectionList)); } }
现在来看我们的控件看似没有问题了,但是不要忘记了本控件的最初目的,即,用户下拉列表时,要去刷新其数据源,所以在用户下拉条件成立时,调用用户的回调方法。
3. 添加下拉刷新处理事件
首先添加一个代理和事件
// user pull down the list delegate event public delegate void DataRefreshedEventHandler(object sender, RoutedEventArgs e); // pull down refresh the ItemSource Event public event DataRefreshedEventHandler DataRefreshed;
然后在setVisible()里回调用户注册的事件方法:
private void setVisible() { ... DataRefreshedEventHandler handler = DataRefreshed; if (handler != null) { handler(this, null); } }
至此,自定义下拉控件完篇,有问题请跟评论,作者第一时间看到回复。
++++++++++++++++++++++++++++++++++++++++++
本文系本站原创,欢迎转载! 转载请注明出处:
http://blog.csdn.net/mr_raptor/article/details/7358226
++++++++++++++++++++++++++++++++++++++++++