相比Ios 和Android. Windows Phone 控件庫中多了兩個比較特殊的空間Pivot 樞軸和Panamera 全景視圖控件.在基於樞軸控件Pivot中咱們常常會碰到一些比較特殊應用場景.相似Pivot中存在相似Silder 左右滑動時 存在一些手勢操做控制. 在某些特殊邏輯下禁止Pivot 左右滑動等需求.本篇幅將詳細說明Pivot在這特殊場景中關於Pivot手勢控制.以及WP7和WP8 兩個版本之間存在的一些差別.css
首先要說的是在Pivot樞軸控件在某些特定業務需求下須要禁止左滑或右滑應用場景. 相似咱們在WP上基於Pivot控件作新手教程或應用開始時的用戶引導.如何來處理禁止PivotItem左右滑動操做?html
基於Windows Phone 7.1 的SDK 咱們一般採用的方式是在對應的PivotItem下添加GestureService 中GestureListener事件分別監聽ManipulationStarted和ManipulationCompleted.或是在後臺代碼中Loaded方法中手動訂閱PivotItem該事件:git
1: this.FirstPivot_PV.AddHandler(PivotItem.ManipulationStartedEvent, new EventHandler<ManipulationStartedEventArgs>(pivot_ManipulationStarted), false);
2: this.FirstPivot_PV.AddHandler(PivotItem.ManipulationDeltaEvent, new EventHandler<ManipulationDeltaEventArgs>(pivot_ManipulationCompleted), false);
先在Started事件中表示手勢滑動起始點Point的座標位置.:github
1: Point startPoint;
2: private void pivot_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
3: {
4: startPoint = e.ManipulationOrigin;
5: }
再在Complated方法中獲取手勢操做結束時的位置Point座標位置 經過判斷X軸移動距離來 判斷當前手勢向左仍是向右滑動 採用Complate()方法完成操做來達到禁止PivotItem向左或向右滑動的控制:web
1: private void pivot_ManipulationCompleted(object sender, ManipulationDeltaEventArgs e)
2: {
3: Point endPoint = e.ManipulationOrigin;
4: if (endPoint.X - startPoint.X >= 0)
5: {
6: #region Control The Right Side
7: e.Complete();
8: e.Handled = true;
9: #endregion
10: }
11:
12: if (endPoint.X - startPoint.X < 0)
13: {
14: #region Control The Left Side
15: e.Complete();
16: e.Handled = true;
17: #endregion
18: }
19: }
這樣很容易WP SDK7.1控制某個PivotItem在手勢操做禁止左右滑動的操做.雖然可以禁止PivotItem左右滑動的操做.可是操做過程當中發現若是PivotItem放入其餘控件在滑動時很容易引發誤操做或左右誤滑的狀況.而對應系統採用Pivot卻不存在這個狀況.參考了阿幹 @ MoHoo Studio 在博文[有效解決Pivot左右誤滑問題]中提出的方案很好解決這個問題.其實原理在使用PivotItem中ManipulationCompleted事件中加入一些判斷條件.來處理誤操做或誤滑動存在的一些狀況. windows
經過擴展方法首先獲取Pivot控件的ItemsPresenter可視化樹結構.獲取其中TransformGroup視圖.來控制Pivot左右滑動,首先咱們須要繼承擴展Pivot控件擴展方法.若是不明白ExtensionMethods如何使用請參考這裏[MSDN ExtensionMethods],在擴展方法中分別實現GetVisualDescendents、FindVisualChild、GetVisualChildren三個方法.實現以下:app
1: public static class ExtensionMethods
2: {
3: public static FrameworkElement FindVisualChild(this FrameworkElement root, string name)
4: {
5: FrameworkElement temp = root.FindName(name) as FrameworkElement;
6: if (temp != null)
7: return temp;
8:
9: foreach (FrameworkElement element in root.GetVisualDescendents())
10: {
11: temp = element.FindName(name) as FrameworkElement;
12: if (temp != null)
13: return temp;
14: }
15:
16: return null;
17: }
18:
19: public static IEnumerable<FrameworkElement> GetVisualDescendents(this FrameworkElement root)
20: {
21: Queue<IEnumerable<FrameworkElement>> toDo = new Queue<IEnumerable<FrameworkElement>>();
22:
23: toDo.Enqueue(root.GetVisualChildren());
24: while (toDo.Count > 0)
25: {
26: IEnumerable<FrameworkElement> children = toDo.Dequeue();
27: foreach (FrameworkElement child in children)
28: {
29: yield return child;
30: toDo.Enqueue(child.GetVisualChildren());
31: }
32: }
33: }
34:
35: public static IEnumerable<FrameworkElement> GetVisualChildren(this FrameworkElement root)
36: {
37: for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
38: yield return VisualTreeHelper.GetChild(root, i) as FrameworkElement;
39: }
40: }
那能夠Complated方法中經過以下方式來獲取Pivot出現誤滑動的狀況的,對Pivot控件滑動進行必定控制:ide
1: private void pivot_ManipulationCompleted(object sender, ManipulationDeltaEventArgs e)
2: {
3: var itemsPresenter = this.pivot.GetVisualDescendents().FirstOrDefault(x => x.GetType() == typeof(ItemsPresenter));
4: var group = itemsPresenter.RenderTransform as TransformGroup;
5: var trans = group.Children.FirstOrDefault(o => o is TranslateTransform) as TranslateTransform;
6:
7: double xvalue = Math.Abs(e.CumulativeManipulation.Translation.X);
8: double yvalue = Math.Abs(e.CumulativeManipulation.Translation.Y);
9: if (xvalue / yvalue < 2 && yvalue > 80 && trans.X == 0.0)
10: {
11: e.Handled = true;
12: }
13: }
xValue和YValue當前Pivot水平滑動距離的值. 其中判斷是否執行e.Handled 條件根據本身實際的項目須要來進行微調.這樣就很好的實現PivotItem禁止左右滑動和Pivot在滑動的誤操做的問題.注意這裏基於Windows Phone SDK 7.1 版本.能夠在Github上下載到這部分源碼[https://github.com/chenkai/ControlLeftPivotDemo]ui
now 基於WPSDK 7.1 咱們如今要基於對PivotItem移植到WP 8.0上.this
原來基於SDK 7.1代碼並不作任何更改.直接移植到WP8上來卻發現原來關於禁止PivotItem左右滑動的控制失去了效果.調試代碼發現.當觸發手勢操做後後臺代碼只執行了ManipulationStarted事件.並無執行ManipulationDelta和ManipulationCompleted事件.很詭異.但查看官方MSDN Windows Phone Pivot 控件 文檔中能夠看出一些端倪.在MSDN文檔中提到Pivot應用:
其中有一點提到:
內置的輕拂和手勢支持
Pivot 應用已提供對常見導航的手勢支持。您沒必要在您的應用中實現諸如拖動、輕拂或點按之類的手勢。
也就是說Pivot在SDK 8.0 已經內置對滑動手勢事件處理.咱們不能採用WP SDK7.1 時經過GestureService 服務或是Code Behind中強制訂閱事件的方式來進行自定義處理.在觸發手勢時其實執行了Manipulation相關事件,可是被Pivot或Panorama內部封裝的手勢路由事件給攔截掉了.這樣咱們後臺關於自定義的ManipulationCompleted事件就得不到執行.雖然Pivot控件內置手勢處理事件. 咱們依然能夠採用FrameworkElement.UseOptimizedManipulationRouting =false屬性來設置Pivot執行自定義事件.關於該屬性說明以下:
FrameworkElement.UseOptimizedManipulationRouting 屬性:
獲取或設置指示系統是否應處理輸入事件或是否 FrameworkElement 應處理輸入事件的值。
若是採用系統默認處理輸入事件,則爲 true;若是 FrameworkElement 應處理輸入自定義事件,則爲 false。 默認值爲true。
適用於如下控件:
可見在WP8.0除了Pivot 在全景視圖Panorama 等其餘控件也內置手勢操做事件處理.這樣一來咱們Xaml文件設置PivotItem UseOptimizedManipulationRouting =false.從新編譯 運行程序發現爆出異常信息:
Can't set UseOptimizedManipulationRouting to false on the control
但當咱們把Xaml訂閱事件方式放在後臺代碼來訂閱時發現異常消失 代碼以下:
1: public void Events()
2: {
3: this.FirstPivot_PV.UseOptimizedManipulationRouting = false;
4: this.FirstPivot_PV.AddHandler(PivotItem.ManipulationStartedEvent, new EventHandler<ManipulationStartedEventArgs>(pivot_ManipulationStarted), true);
5: this.FirstPivot_PV.AddHandler(PivotItem.ManipulationDeltaEvent, new EventHandler<ManipulationDeltaEventArgs>(pivot_ManipulationCompleted), true);
6: }
其實問題在於若是咱們Xaml文件中設置PivotItem UseOptimizedManipulationRouting =false 屬性後.若是咱們同時也在Xaml 文件訂閱該PivotItem中直接訂閱該事件就會觸發這個異常. 第一次運行正常.但第二次就會爆出異常.咱們須要採用後臺代碼添加訂閱事件方式來處理來避免這個異常.
1: FirstPivot_PV.AddHandler(PivotItem.ManipulationStartedEvent, new EventHandler<ManipulationStartedEventArgs>(pivot_ManipulationStarted), true);
注意AddHandler方法須要在設置處理方法設置True.爲已標記爲由其餘元素在事件路由過程當中處理的路由事件調用所提供的處理程序指向該方法. 具體關於AddHandler請參考UIElement.AddHandler[MSDN]說明.
這樣一來.在來調試後臺代碼發現手勢在觸發ManipulationStarted事件後 同時也成功的觸發了ManipulationCompleted事件.這樣一來咱們移植原來的代碼邏輯來控制PivotItem左右滑動操做 代碼以下:
1: Point startPoint;
2: private void pivot_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
3: {
4: startPoint = e.ManipulationOrigin;
5: }
6:
7: private void pivot_ManipulationCompleted(object sender, ManipulationDeltaEventArgs e)
8: {
9: Point endPoint = e.ManipulationOrigin;
10: if (endPoint.X - startPoint.X >= 0)
11: {
12: #region Control Right Side
13: e.Complete();
14: e.Handled = true;
15: #endregion
16: }
17:
18: if (endPoint.X - startPoint.X < 0)
19: {
20: #region Control Left Side
21: e.Complete();
22: e.Handled = true;
23: #endregion
24: }
25: }
咱們發現實際效果關於禁止PivotItem 左右滑動的效果成功移植到WP 8.0上來.源碼請到Github上下載[https://github.com/chenkai/PivotDisableLeftDemo]
源碼下載:
WP7.1 [https://github.com/chenkai/ControlLeftPivotDemo]
WP8.0 [https://github.com/chenkai/PivotDisableLeftDemo]
Contact ME: [@chenkaihome]
參考資料:
FrameworkElement.UseOptimizedManipulationRouting 屬性
Handle Swipe Up, Swipe Down, Swipe Left & Swipe Right Gestures in a WinRT app