首页 > 代码库 > 【笔记】WPF实现类似安卓ViewPager引导界面效果及问题汇总
【笔记】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 #endregion124 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大于1179 else if (x_offset < -100 && ActiveItemIndex > 0)180 {181 --ActiveItemIndex;182 }183 else184 {185 MoveAnimation(ActiveItemIndex);186 }187 e.Handled = true;188 }189 #endregion190 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 #endregion207 }208 }
3、数据绑定:有了依赖属性,在客户端的任何一个窗口中调用该控件,都可以进行数据绑定了,以下是调用该控件的窗口XAML:
1 <Grid>2 <control:IndicatorControl ItemsListSource="{Binding ImageList}" TotalItemsCount="{Binding ImageList.Count}"></control:IndicatorControl>3 </Grid>
如果控件的当前激活条目需要绑定到其他地方,那么这里也可以进行设置,但这里暂时不需要设置了。
【笔记】WPF实现类似安卓ViewPager引导界面效果及问题汇总
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。