WP7 - 用手勢(Flick)做一個簡單的水平ListBox

Windows Phone 7 – 用手勢(Flick)做一個簡單的水平ListBox

此篇文章,其實是當麻下午忽然問我的一個問題,「如何做一個ListBox可以限制一次只滑動一個,而且可以循環」。

剛好昨天發了一篇<Windows Phone 7 - 淺談手勢(Gestures)運作>,所以我就想說直接透過Flick的事件,來進行這項功能。

 

不囉嗦往下直接帶程式碼進行說明:

1. 建立一個新的專案,並在專案建立一個資料夾「Images」,裡面放了5張圖片:a、b、c、d、e,均是jpg

    =>如果要放*.png也是可以喔,只是範例使用*.jpg。

 

2. 在MainPage.xaml中的Grid控件:ContentPanel中定義要放入的StackPanel(放五個圈)與Image(放圖片)

   1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="0,0,0,0">
   2:     <Grid.RowDefinitions>
   3:         <RowDefinition Height="5*" />
   4:         <RowDefinition Height="95*" />
   5:     </Grid.RowDefinitions>
   6:     <!-- 定義五個圈 -->
   7:     <StackPanel x:Name="splEllipses"
   8:         Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center">
   9:         <Ellipse x:Name="elpA" Fill="#FFF4F4F5"
  10:                  Stroke="Black" Width="23" Height="23" Margin="0,0,5,0" />
  11:         <Ellipse x:Name="elpB" Fill="#FFF4F4F5"
  12:                  Stroke="Black" Width="23" Height="23" Margin="0,0,5,0"/>
  13:         <Ellipse x:Name="elpC" Fill="#FFF4F4F5"
  14:                  Stroke="Black" Width="23" Height="23" Margin="0,0,5,0"/>
  15:         <Ellipse x:Name="elpD" Fill="#FFF4F4F5"
  16:                  Stroke="Black" Width="23" Height="23" Margin="0,0,5,0"/>
  17:         <Ellipse x:Name="elpE" Fill="#FFF4F4F5"
  18:                  Stroke="Black" Width="23" Height="23" Margin="0,0,5,0"/>
  19:     </StackPanel>
  20:     <!-- 定義要用到的Image物件與GestureListener -->
  21:     <StackPanel Grid.Row="1">
  22:         <Canvas>
  23:             <Image x:Name="imgSource" Width="480" Height="603"
  24:                    Source="/Images/a.jpg" Stretch="Fill">
  25:                 <wp7toolkit:GestureService.GestureListener>
  26:                     <!-- 定義Flick與DoubleTap事件 -->
  27:                     <wp7toolkit:GestureListener
  28:                     Flick="GestureListener_Flick"
  29:                     DoubleTap="GestureListener_DoubleTap">
  30:                     </wp7toolkit:GestureListener>
  31:                 </wp7toolkit:GestureService.GestureListener>
  32:                 <Image.RenderTransform>
  33:                     <TranslateTransform x:Name="translation" />
  34:                 </Image.RenderTransform>
  35:             </Image>
  36:         </Canvas>
  37:     </StackPanel>
  38: </Grid>

 

3. 實作Flick事件,隨著用戶的手勢進行圖片的切換

   1: #region Flick
   2: /// <summary>
   3: /// Flick事件,隨著手勢由左往右或由右往左更換圖示。
   4: /// </summary>
   5: /// <param name="sender"></param>
   6: /// <param name="e"></param>
   7: private void GestureListener_Flick(object sender, FlickGestureEventArgs e)
   8: {
   9:     if (e.Direction == System.Windows.Controls.Orientation.Horizontal)
  10:     {
  11:         //利用FlickGestureEventArgs的Angle(甩尾的角度)來識別
  12:         if (e.Angle > 270 || e.Angle < 90)
  13:             //往上一張
  14:             TurnToPreviousImage();
  15:         else
  16:             //往下一張
  17:             TurnToNextImage();
  18:     }
  19: }
  20: #endregion

 

4. 實作DoubleTap事件與設定在建構子時,自動塞入第一張圖示

   1: public ManiPage()
   2: {
   3:     InitializeComponent();
   4:     
   5:     //呼叫初始化的功能
   6:     GestureListener_DoubleTap(null, null);
   7: }
   8:  
   9: #region DoubleTap
  10: /// <summary>
  11: /// 負責DoubleTap後回到第一張圖片,用於呈現
  12: /// </summary>
  13: /// <param name="sender"></param>
  14: /// <param name="e"></param>
  15: private void GestureListener_DoubleTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
  16: {
  17:     gCurrentIdx = 0;
  18:     UpdateImage();
  19: }
  20: #endregion

 

5. 實作更換上、下一張圖示,隨著更換五個圈圈的主要邏輯部分

   1: #region 處理圖像
   2: private int gCurrentIdx = 0;
   3: private List<string> gImageList = new List<string>() { "a.jpg", "b.jpg", "c.jpg", "d.jpg", "e.jpg" };
   4:  
   5: /// <summary>
   6: /// 往上一張圖。
   7: /// </summary>
   8: private void TurnToPreviousImage()
   9: {
  10:     gCurrentIdx--;
  11:     if (gCurrentIdx < 0)
  12:         gCurrentIdx = gImageList.Count - 1; //循環的處理
  13:     
  14:     UpdateImage();
  15: }
  16:  
  17: /// <summary>
  18: /// 往下一張圖。
  19: /// </summary>
  20: private void TurnToNextImage()
  21: {
  22:     gCurrentIdx++;
  23:     if (gCurrentIdx >= gImageList.Count)
  24:         gCurrentIdx = 0;   //循環的處理
  25:  
  26:     UpdateImage();
  27: }
  28:  
  29: /// <summary>
  30: /// 實際更換圖示與更新五個圈圈的內容。
  31: /// </summary>
  32: private void UpdateImage()
  33: {
  34:     Dispatcher.BeginInvoke(() =>
  35:     {
  36:         imgSource.Source = new BitmapImage(
  37:                 new Uri(string.Format("/Images/{0}", gImageList[gCurrentIdx]),
  38:                                                     UriKind.RelativeOrAbsolute));
  39:         //先將五個圈圈全部設定成灰色
  40:         foreach (Ellipse tEllipse in splEllipses.Children)
  41:         {
  42:             tEllipse.Fill = new SolidColorBrush(Colors.Gray);
  43:         }
  44:         //再依照現在選擇的圖示,設定成白色
  45:         ((Ellipse)splEllipses.Children[gCurrentIdx]).Fill = new SolidColorBrush(Colors.White);
  46:     });
  47: }
  48: #endregion

 

[執行範例圖]

000001002003004

[註] 圖片所有權不屬於本文章,因此,只暫用圖示,範例中的使用到的圖示,請大家自行選擇適合的圖示,謝謝

 

[補充]

1. 如果想要感覺手在拉動時,圖像跟著跑的話,再加上三個事件:「DragStarted」、「DragDelta」、「DragCompleted」;

   1: #region Drag系列
   2: private Point gSourcePoint;
   3: private void GestureListener_DragStarted(object sender, DragStartedGestureEventArgs e)
   4: {
   5:     //記錄一開始的位置
   6:     gSourcePoint = e.GetPosition(imgSource);
   7: }
   8:  
   9: /// <summary>
  10: /// 按住拉動。
  11: /// </summary>
  12: /// <param name="sender"></param>
  13: /// <param name="e"></param>
  14: private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
  15: {
  16:     //由於是水平拉動,所以把Y軸的功能隱藏
  17:     this.translation.X += e.HorizontalChange;
  18:     //this.translation.Y += e.VerticalChange;
  19: }
  20:  
  21: /// <summary>
  22: /// 放開後復原回來的位置
  23: /// </summary>
  24: /// <param name="sender"></param>
  25: /// <param name="e"></param>
  26: private void GestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
  27: {
  28:     //手指放開後,要記得把圖還原到一開始的位置
  29:     this.translation.X = 0;
  30:     this.translation.Y = 0;
  31: }
  32: #endregion

     如果要加上這三個事件,別忘到xaml檔加上事件的監聽喔。

 

[範例程式]


======

以上是簡單直接介紹怎麼使用Flick的手勢事件,實作一個類似水平ListBox感覺的效果。

不過這不是一個好的作法,我相信還有其他更漂亮的做法,例如透過Blend做出滑動的效果…等,

至於水平的ListBox很容易的,可以參考下方的連結,希望對大家有所幫助。   
 

References:

Creating a Horizontal ListBox in Silverlight and Windows Phone

How do I get a Horizontal ListBox to scroll horizontally in WP7?

Windows Phone 7中实现ListBox的分页加载 - Windows Phone 开发笔记 - 青藤园 (重要)

horizontal listbox that could be infinite circle scrolling

Windows Phone ListBox 水平滚动的代码 & horizontal listbox?

How to write a control similar to ListBox, but sliding left to right instead of up and down

 

Dotblogs Tags: