【笔记】WPF实现类似安卓ViewPager引导界面效果及问题汇总

最近在开发项目的首次使用引导界面时,遇到了问题,引导界面类似于安卓手机ViewPager那样的效果,希望通过左右滑动手指来实现切换不同页面,其间伴随动画。

实现思路:

1、界面布局:新建一个UserControl,最外层为Grid,两行一列,内嵌一个Canvas和StackPanel。Canvas中放一个StackPanel用于存放大图列表,外层的StackPanel用于存放RadioButton组,Xaml代码如下:

 1     <Grid x:Name="grid">
 2         <Grid.RowDefinitions>
 3             <RowDefinition Height="7*"></RowDefinition>
 4             <RowDefinition></RowDefinition>
 5         </Grid.RowDefinitions>
 6         <Canvas x:Name="canvas" Grid.Row="0" Grid.RowSpan="2" Background="#eaede6">
 7             <StackPanel x:Name="imageStack"  Orientation="Horizontal"></StackPanel>
 8         </Canvas>
 9         <StackPanel x:Name="buttonStack" Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Center" >
10             <RadioButton></RadioButton>
11         </StackPanel>
12     </Grid>

2、后台代码:定义三个依赖属性,分别为ActiveItemIndex,TotalItemsCount,ItemsListSource,分别表示当前处于活动状态的条目ID,总的条目数量,及条目源,这里的后台代码我的ItemsListSource的数据类型是IEnumerable<BitmapImage>,为了方便看效果我直接把每个页面作为一张图片,到项目集成的时候应该用Page或其他控件,当ActiveItemIndex改变时,执行相应的动画,C#代码如下:

  1 namespace UserInterface.UserControls
  2 {
  3     /// <summary>
  4     /// IndicatorControl.xaml 的交互逻辑
  5     /// </summary>
  6     public partial class IndicatorControl : UserControl
  7     {
  8         #region 字段及属性
  9         /// <summary>
 10         /// 单张图片宽度
 11         /// </summary>
 12         private Double _width = 1300;
 13         /// <summary>
 14         /// 触摸起始点
 15         /// </summary>
 16         private TouchPoint _startTouchPoint;
 17         /// <summary>
 18         /// 触摸结束点
 19         /// </summary>
 20         private TouchPoint _endTouchPoint;
 21
 22         // Using a DependencyProperty as the backing store for ActiveButtonIndex.  This enables animation, styling, binding, etc...
 23         public static readonly DependencyProperty ActiveItemIndexProperty =
 24             DependencyProperty.Register("ActiveItemIndex", typeof(Int32), typeof(IndicatorControl), new UIPropertyMetadata(-1, new PropertyChangedCallback((sender, e) =>
 25             {
 26                 IndicatorControl control = sender as IndicatorControl;
 27                 control.SetActiveItem();
 28             })));
 29
 30         // Using a DependencyProperty as the backing store for TotalButtonCount.  This enables animation, styling, binding, etc...
 31         public static readonly DependencyProperty TotalItemsCountProperty =
 32             DependencyProperty.Register("TotalItemsCount", typeof(Int32), typeof(IndicatorControl), new UIPropertyMetadata(-1, new PropertyChangedCallback((sender, e) =>
 33             {
 34                 IndicatorControl control = sender as IndicatorControl;
 35                 control.SetItemsByTotalCount();
 36             })));
 37
 38         // Using a DependencyProperty as the backing store for ImageListProperty.  This enables animation, styling, binding, etc...
 39         public static readonly DependencyProperty ItemsListSourceProperty =
 40             DependencyProperty.Register("ItemsListSource", typeof(IEnumerable<BitmapImage>), typeof(IndicatorControl), new UIPropertyMetadata(null, new PropertyChangedCallback((sender, e) =>
 41             {
 42                 IndicatorControl control = sender as IndicatorControl;
 43                 control.SetItemsList();
 44             })));
 45         /// <summary>
 46         /// 当前处于激活状态的条目索引
 47         /// </summary>
 48         public Int32 ActiveItemIndex
 49         {
 50             get { return (Int32)GetValue(ActiveItemIndexProperty); }
 51             set { SetValue(ActiveItemIndexProperty, value); }
 52         }
 53         /// <summary>
 54         /// 总条目数量
 55         /// </summary>
 56         public Int32 TotalItemsCount
 57         {
 58             get { return (Int32)GetValue(TotalItemsCountProperty); }
 59             set { SetValue(TotalItemsCountProperty, value); }
 60         }
 61         /// <summary>
 62         /// 条目数据源
 63         /// </summary>
 64         public IEnumerable<BitmapImage> ItemsListSource
 65         {
 66             get { return (IEnumerable<BitmapImage>)GetValue(ItemsListSourceProperty); }
 67             set { SetValue(ItemsListSourceProperty, value); }
 68         }
 69         #endregion
 70
 71         #region 构造函数
 72         public IndicatorControl()
 73         {
 74             InitializeComponent();
 75         }
 76         #endregion
 77
 78         #region 方法
 79         /// <summary>
 80         /// 设置当前活动的Item项
 81         /// </summary>
 82         public void SetActiveItem()
 83         {
 84             for (int i = 0; i < this.TotalItemsCount; i++)
 85             {
 86                 if (i.Equals(this.ActiveItemIndex))
 87                 {
 88                     (this.buttonStack.Children[i] as RadioButton).IsChecked = true;
 89                 }
 90             }
 91             MoveAnimation(ActiveItemIndex);
 92         }
 93         /// <summary>
 94         /// 设置Item的总数
 95         /// </summary>
 96         public void SetItemsByTotalCount()
 97         {
 98             this.buttonStack.Children.Clear();
 99             for (Int32 i = 0; i < this.TotalItemsCount; i++)
100             {
101                 RadioButton r = new RadioButton();
102                 r.IsEnabled = false;
103                 r.GroupName = "Index";
104                 r.Margin = new Thickness(10);
105                 this.buttonStack.Children.Add(r);
106             }
107         }
108         /// <summary>
109         /// 设置Items数据源
110         /// </summary>
111         public void SetItemsList()
112         {
113             this.imageStack.Children.Clear();
114             for (Int32 i = 0; i < ItemsListSource.Count(); i++)
115             {
116                 Image image = new Image();
117                 image.Source = ItemsListSource.ElementAt(i);
118                 image.Width = _width;
119                 image.Stretch = Stretch.Fill;
120                 this.imageStack.Children.Add(image);
121             }
122         }
123         #endregion
124
125         #region 事件
126         /// <summary>
127         /// 控件加载时执行一些操作
128         /// </summary>
129         /// <param name="sender"></param>
130         /// <param name="e"></param>
131         private void UserControl_Loaded(object sender, RoutedEventArgs e)
132         {
133             this.ActiveItemIndex = 0;
134             this.imageStack.Width = _width * TotalItemsCount;
135         }
136         /// <summary>
137         /// 触摸按下
138         /// </summary>
139         /// <param name="sender"></param>
140         /// <param name="e"></param>
141         private void imageStack_TouchDown(object sender, TouchEventArgs e)
142         {
143             _startTouchPoint = e.GetTouchPoint(App.Current.MainWindow);
144             e.Handled = true;
145         }
146         /// <summary>
147         /// 长按并移动
148         /// </summary>
149         /// <param name="sender"></param>
150         /// <param name="e"></param>
151         private void imageStack_TouchMove(object sender, TouchEventArgs e)
152         {
153             TouchPoint tempPoint = e.GetTouchPoint(App.Current.MainWindow);
154             //得到前后两点X的平移距离
155             double distance = _startTouchPoint.Position.X - tempPoint.Position.X;
156             //计算相偏移量
157             Double offset = this._width * ActiveItemIndex + distance;
158             //释放属性,使其可以被设置
159             this.imageStack.BeginAnimation(Canvas.LeftProperty, null);
160
161             Canvas.SetLeft(this.imageStack, -offset);
162             e.Handled = true;
163         }
164         /// <summary>
165         /// 触摸释放
166         /// </summary>
167         /// <param name="sender"></param>
168         /// <param name="e"></param>
169         private void imageStack_TouchUp(object sender, TouchEventArgs e)
170         {
171             _endTouchPoint = e.GetTouchPoint(App.Current.MainWindow);
172             double x_offset = _startTouchPoint.Position.X - _endTouchPoint.Position.X;
173             //当X轴偏移量向右大于100且当前Index小于页总数
174             if (x_offset > 100 && ActiveItemIndex < TotalItemsCount - 1)
175             {
176                 ++ActiveItemIndex;
177             }
178             //当X轴偏移量向左偏移量大于100且当前Index大于1
179             else if (x_offset < -100 && ActiveItemIndex > 0)
180             {
181                 --ActiveItemIndex;
182             }
183             else
184             {
185                 MoveAnimation(ActiveItemIndex);
186             }
187             e.Handled = true;
188         }
189         #endregion
190
191         #region 动画
192         /// <summary>
193         /// 动画
194         /// </summary>
195         /// <param name="index"></param>
196         private void MoveAnimation(Int32 index)
197         {
198             DoubleAnimation da = new DoubleAnimation();
199             da.Duration = new Duration(TimeSpan.FromMilliseconds(300));
200             da.DecelerationRatio = 0.2;
201             da.AccelerationRatio = 0.2;
202             da.From = Canvas.GetLeft(this.imageStack);
203             da.To = -(index * _width);
204             this.imageStack.BeginAnimation(Canvas.LeftProperty, da);
205         }
206         #endregion
207     }
208 }

C# Code

3、数据绑定:有了依赖属性,在客户端的任何一个窗口中调用该控件,都可以进行数据绑定了,以下是调用该控件的窗口XAML:

1     <Grid>
2         <control:IndicatorControl ItemsListSource="{Binding ImageList}" TotalItemsCount="{Binding ImageList.Count}"></control:IndicatorControl>
3     </Grid>

Xaml Code

如果控件的当前激活条目需要绑定到其他地方,那么这里也可以进行设置,但这里暂时不需要设置了。

时间: 2024-08-28 12:39:47

【笔记】WPF实现类似安卓ViewPager引导界面效果及问题汇总的相关文章

安卓开发复习笔记——ViewPager组件(仿微信引导界面&gt;)

这2天事情比较多,都没时间更新博客,趁周末,继续继续~ 今天来讲个比较新潮的组件——ViewPager 什么是ViewPager? ViewPager是安卓3.0之后提供的新特性,继承自ViewGroup,专门用以实现左右滑动切换View的效果. 如果想向下兼容就必须要android-support-v4.jar这个包的支持,这是一个来自google提供的一个附加包. 通俗点来讲,就是现在市面上大多数app,安装完第一次打开软件会出现的一个左右滑动的引导界面. 先来看下效果图:     这是一个

用ViewPager实现一个程序引导界面

下面使用ViewPager来实现一个程序引导的demo: 一般来说,引导界面是出现第一次运行时出现的,之后不会再出现.所以需要记录是否是第一次使用程序,办法有很多,最容易想到的就是使用SharedPreferences来保存.步骤如下: 1.程序进入欢迎界面,SplashActivity,在这里读取SharedPreferences里面的变量,先设置为true.进入引导界面,然后设置为false. 2.之后每次进入欢迎界面读取SharedPreferences里面的变量,因为是false,所以不

GuideActivity.java引导界面:

这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包囊了只有在安卓3.0以上可以使用的api. 而viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等.那如何使用它呢,与LisstView类似,我们也需要一个适配器,他就是PagerAdapter.看一下api的图片, ViewPager的功能就是可以使视图滑动,就像Lanucher左右滑动那样.分三个步骤来使用它: 1.在住布局文件里加入 <android.support.v4.view.ViewP

【Android UI设计与开发】第05期:引导界面(五)实现应用程序只启动一次引导界面

[Android UI设计与开发]第05期:引导界面(五)实现应用程序只启动一次引导界面 jingqing 发表于 2013-7-11 14:42:02 浏览(229501) 这篇文章算是对整个引导界面开发专题的一个终结了吧,个人觉得大部分的引导界面基本上都是千篇一律的,只要熟练掌握了一个,基本上也就没什么好说的了,要是在今后的开发中遇到了更好玩,更有趣的引导界面,博主也会在这里及时的跟大家分享,今天的内容主要是教大家的应用程序只有在第一次启动的时候显示引导界面,以后在启动程序的时候就不再显示了

Android应用引导界面-ViewPages使用

ViewPager的功能就是可以使视图滑动.可以用来做导航.页面菜单.使用时需要适配器PagerAdapter. 因此需要继承PagerAdapter,实现引导界面至少需要复写4个方法: instantiateItem(ViewGroup, int):用来创建在指定位置上的一个页面: destroyItem(ViewGroup, int, Object):销毁指定位置上的一个页面: getCount():页面数量: isViewFromObject(View, Object):用来判断pager

十八、Android引导界面

一.所需素材 很有必要整理一下,里面附带友盟的社会化分享组件,我就不去掉了. 二.代码 import com.umeng.update.UmengUpdateAgent; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import an

Android UI开发第四十三篇——使用Property Animation实现墨迹天气3.0引导界面及动画实现

前面写过<墨迹天气3.0引导界面及动画实现>,里面完美实现了动画效果,那一篇文章使用的View Animation,这一篇文章使用的Property Animation实现.Property Animation是Android3.0以后新增的动画库. 这篇文章的源码以及效果在github. 实现墨迹天气向上滑动的viewpager使用的开源库ViewPager-Android.ViewPager-Android开源库设置app:orientation定义滑动方向. 墨迹天气引导界面共有4个视图

【转】引导界面(五)实现应用程序只启动一次引导界面

这篇文章算是对整个引导界面开发专题的一个终结了吧,个人觉得大部分的引导界面基本上都是千篇一律的,只要熟练掌握了一个,基本上也就没什么好说的了,要是在今后的开发中遇到了更好玩,更有趣的引导界面,博主也会在这里及时的跟大家分享,今天的内容主要是教大家的应用程序只有在第一次启动的时候显示引导界面,以后在启动程序的时候就不再显示了. 其实要想实现这样的效果,只要使用SharedPreferences类,就会让程序变的非常简单,下面来详细介绍一下这个类的使用方法 一.SharedPreferences的详

App 引导界面

App 引导界面 1.前言 最近在学习实现App的引导界面,本篇文章对设计流程及需要注意的地方做一个浅显的总结. 附上项目链接,供和我水平类似的初学者参考——http://files.cnblogs.com/files/tgyf/Tutorial.rar. 对于有引导界面的App,刚安装或使用后将其数据清除(Setting-Apps-...),启动后就会出现引导界面,目的是向用户介绍本款应用的使用方法或主要功能. App引导过程的页面数一般为为3到6个,特殊的如刷机后的SetupWizard设置