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
[執行範例圖]
[註] 圖片所有權不屬於本文章,因此,只暫用圖示,範例中的使用到的圖示,請大家自行選擇適合的圖示,謝謝。
[補充]
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