[翻译]我的手势都去哪里了?

原文http://blogs.msdn.com/b/wsdevsol/archive/2013/02/16/where-did-all-my-gestures-go.aspx

第一次翻译东西,别见笑,如翻译的有问题请联系我。

欢迎来我的个人博客:http://blog.s2003zy.com

经常在我们的用户哪里被问道:“ 怎么在collection控件中控制pointer输入和处理事件(manipulations),比如在ListViews, GridViews或者是 FlipViews中,应用程序可以在View中的items获取到PointerPressed事件,但是在那之后点击事件(pointer events)就消失了,应用程序无法将其转换成手势或操作。”

这里到底发生了什么?

这些控件的共同点是,他们都拥有一个ScrollViewer,并且ScrollViewer获取了pointer的控制来进行ScrollViewer的滚动。 一旦ScrollViewer获得了所有的pointer和处理事件(manipulation events)权的话,应用程序将没有机会再去控制它了。

这意味着如果应用程序需要在滚动控件中处理Pointer事件的话,需要disable掉ScrollViewer(下面会说说更多)。

为甚么ScrollViewer要获取pointerd?

效率和响应能力,Xaml应用在不同的线程中组合和渲染他们的图形。渲染线程负责保持屏幕流畅的更新。组合线程是应用程序的UI执行并且可以更新控件的布局的地方。每当布局不更新,渲染线程可以保持视觉显示清晰、快捷,即使其他工作正在被完成。非布局的更新,例如如独立的动画和渲染变换,可以在渲染线程上完成,而不会减慢同步两个线程。

因为触碰事件需要很快的响应。Xaml应用程序利用一个叫Direct Manipulation 的Windows特性,在底层的渲染线程上来处理。Direct Manipulation 可以探测到的触控事件比如滚动,平移和缩放。ScrollViewer 利用它迅速利落的进行滚动。应用程序可以在渲染线程中通过Xaml Manipulation 事件监听到Direct Manipulation,来实现缩放、平移、旋转变换等渲染变换并快速响应。

Pointer事件就是去那儿了,当ScrollViewer处理PointerPressed event时Direct Manipulation引擎接手了pointer 的控制,而应用程序不会再接受到任何Pointer事件直到当前的操控结束。

那如果应用程序需要Pointer的信息怎么办?

一个简单的方法是,限制,让一个子元素(比如一个Image)隔断滚动并设置让其受自己控制。 例如设置一个在FlipView的ItemTemplate中的Image的ManipulationMode设置为"All"(ManipulationMode=”All”),这样触控图片的事件就不会触发ScrollViewer。这个图像可以接收到Xaml manipulation事件和pointer 事件,这样就可以将其传递给一个手势识别的处理程序。

如果应用不适用于讲事件传递给一个子元素,那么就有点麻烦了。最好的方式就是限制信息到一个子区域中去或者是利用设置模式。在这种情况下PointerPressed处理者可以判断是否在子区域中或者是非滚动模式,这样的话它就能通过可视化树来找到ScrollViewer结束Direct Manipulation的魔咒了。

为了实现上面的,需要改变ScrollViewer的HorizontalScrollMode 以及 VerticalScrollMode为Disabled。如果你有GridView或FlipView的一个方向的滚动(水平或者是垂直)的话,只需要改变一个味Disabled。

ScrollViewer FindParentScrollViewer(DependencyObject start) 
{ 
    DependencyObject item = start; 
    DependencyObject parent = null; 
    do 
    { 
        parent = (DependencyObject)VisualTreeHelper.GetParent(item); 
        if (parent is ScrollViewer) 
        { 
            return parent as ScrollViewer; 
        } 
        item = parent; 
    } while (item != null);

    return null; 
}

 

bool InDisableScrollViewerRegion(PointerPoint pt) 
{ 
    // For demonstration purposes, scroll at the top and block the rest 
    return pt.Position.Y > 400; 
}

private void OnPointerPressed(object sender, PointerRoutedEventArgs e) 
{ 
    DependencyObject depObj = (DependencyObject)e.OriginalSource; 
   
    if (InDisableScrollViewerRegion(e.GetCurrentPoint(this))) 
    { 
        DisableScrolling((DependencyObject)e.OriginalSource); 
    } 
    
    UIElement target = sender as UIElement;

    PointerPoint point = e.GetCurrentPoint(itemFlipView); 
    gestureRecognizer.ProcessDownEvent(point); 
    target.CapturePointer(e.Pointer); 
    e.Handled = true; 
}

ScrollMode _originalScrollMode; 
bool _scrollingDisabled = false; 
private void DisableScrolling(DependencyObject depObj) 
{ 
       ScrollViewer parentElem; 
       parentElem = FindParentScrollViewer(depObj); 
       if (parentElem != null) 
      { 
            _originalScrollMode = parentElem.HorizontalScrollMode; 
            _scrollingDisabled = true; 
            parentElem.HorizontalScrollMode = ScrollMode.Disabled; 
     } 
}

private void RestoreScrolling(DependencyObject depObj) 
{ 
     if (_scrollingDisabled) 
     { 
            _scrollingDisabled = false; 
            ScrollViewer parentElem = FindParentScrollViewer(depObj); 
            if (parentElem != null) 
            { 
                    parentElem.HorizontalScrollMode = _originalScrollMode; 
            } 
     } 
}

 


很不幸的是,这不是一个很好的方法,如果应用程序同时需要滚动和手势。这样的情况下,唯一的选择就是在获取Pointer 消息的地方关闭Direct Manipulation,但是这样同样会使滚动失效,为了让应用程序能够进行滚动的手势,需要使用ScrollViewer的ScrollToHorizontalOffset 或者 ScrollToVerticalOffset(8.1里变成ChangeView了)来更新ScrollViewer的位置。这很麻烦而且会比让ScrollViewer自己做事情要慢,应当尽量避免。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值