none
请教ScrollViewer的PanningMode和IsManipulationEnabled冲突问题 RRS feed

  • 问题

  • <ScrollViewer Background="Gray" x:Name="scrolls" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
                        <Grid Name="docrect" Background="White" HorizontalAlignment="Center"  Height="1080" VerticalAlignment="Center" Width="1920"
                            TouchDown="docrect_TouchDown" MouseDown="docrect_MouseDown" IsManipulationEnabled="True">
     
                        </Grid>
                    </ScrollViewer>

    然后在代码里写了

                docrect.ManipulationDelta += docrect_ManipulationDelta;
                docrect.ManipulationStarting += docrect_ManipulationStarting;
                docrect.ManipulationCompleted += docrect_ManipulationCompleted;
                docrect.IsManipulationEnabled = true;

    之后,在ManipulationStarting 内 e.Handled = true;

    结果就不能通过触摸来使ScrollViewer的滚动条滚动了

    如果e.Handled = true;去掉,就可以通过触摸来滚动,但是ManipulationDelta触发不了缩放操作,因为收到的数据永远都是1.0

    请问这是bug还是设置问题?需要怎么解决?

    2015年4月17日 12:53

答案

  • Hi czysofthz,

    >>"请问这是bug还是设置问题?需要怎么解决?"

    在我个人看来这不是一个bug,我测试了一下,发现在ManipulationStarting事件里不设置e.habdle,但将ScrollView 的PanningMode 属性设置为None,我发现这样可以实现缩放,滚动条通过触摸拉动也是可以的,你可以试一下。

    你也可以这样考虑,PanniMode 属性是指如何响应触摸操作,而你的代码只是Grid需要实现缩放,你并没有给ScrollView指定触摸操作的具体执行方式,所以默认值就可以。

    另外我找到一些信息,关于ScrollView触摸实现滚动的内容,你可以自定义一个ScrollView控件,来实现你需要的效果。

    链接:http://stackoverflow.com/questions/19855920/wpf-scrollviewer-consuming-touch-before-longpress

    希望我的回复会对你有所帮助。

    Best Regards,

    Xavier Eoro


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • 已标记为答案 czysofthz 2015年4月26日 14:45
    2015年4月24日 3:27
    版主

全部回复

  • Hi czysofthz,

    你的问题是在在ScrollView中实现Grid的缩放效果,我觉得你说的冲突,可能是因为你没有在ManipulationStarting中加上这句的原因“e.ManipulationContainer = this;”,我写了个例子,Grid放大到超过一定范围后,出现滚动条,我在模拟器中通过触摸操作拉动滚动条和实现缩放操作,你可以参照一下:

    <ScrollViewer Background="Gray" x:Name="scrolls" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
            <Grid Name="docrect" Background="White" HorizontalAlignment="Center"  Height="100" VerticalAlignment="Center" Width="100"
                              ManipulationStarting="docrect_ManipulationStarting" ManipulationDelta="docrect_ManipulationDelta" ManipulationCompleted="docrect_ManipulationCompleted" IsManipulationEnabled="True">
                <TextBlock Text="abc"></TextBlock>
            </Grid>
        </ScrollViewer>
    private void docrect_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
            {
                e.ManipulationContainer = this;
                e.Handled = true;
            }
    
    private void docrect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
            {
                
                Grid gridtoResize = e.OriginalSource as Grid;
    
                gridtoResize.Width = gridtoResize.Width * e.DeltaManipulation.Scale.X;
                gridtoResize.Height = gridtoResize.Height * e.DeltaManipulation.Scale.Y;
    
                if (e.IsInertial)
                {
                    e.Complete();
                }
    
    
                e.Handled = true;
            }

    Best Regards,

    Xavier Eoro

    2015年4月21日 3:25
    版主
  • 多谢大神回复,我刚新建了一个wpf应用程序,试了一下

    还是不行,可以缩放,但是放大到有滚动条之后,还是不能触摸拖动的,滚动条丝毫不动

    以下是完整的代码,麻烦大神再帮我看一下

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ScrollViewer Background="Gray" x:Name="scrolls" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
                <Grid Name="docrect" Background="White" HorizontalAlignment="Center"  Height="100" VerticalAlignment="Center" Width="100"
                              ManipulationStarting="docrect_ManipulationStarting"  ManipulationDelta="docrect_ManipulationDelta"  IsManipulationEnabled="True">
                    <TextBlock Text="abc"></TextBlock>
                </Grid>
            </ScrollViewer>
        </Grid>
    </Window>
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApplication2
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
            private void docrect_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
            {
                e.ManipulationContainer = this;
                e.Handled = true;
            }
    
            private void docrect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
            {
    
                Grid gridtoResize = e.OriginalSource as Grid;
    
                gridtoResize.Width = gridtoResize.Width * e.DeltaManipulation.Scale.X;
                gridtoResize.Height = gridtoResize.Height * e.DeltaManipulation.Scale.Y;
    
                if (e.IsInertial)
                {
                    e.Complete();
                }
    
    
                e.Handled = true;
            }
        }
    }
    

    2015年4月21日 8:52
  • Hi czysofthz,

    >>"请问这是bug还是设置问题?需要怎么解决?"

    在我个人看来这不是一个bug,我测试了一下,发现在ManipulationStarting事件里不设置e.habdle,但将ScrollView 的PanningMode 属性设置为None,我发现这样可以实现缩放,滚动条通过触摸拉动也是可以的,你可以试一下。

    你也可以这样考虑,PanniMode 属性是指如何响应触摸操作,而你的代码只是Grid需要实现缩放,你并没有给ScrollView指定触摸操作的具体执行方式,所以默认值就可以。

    另外我找到一些信息,关于ScrollView触摸实现滚动的内容,你可以自定义一个ScrollView控件,来实现你需要的效果。

    链接:http://stackoverflow.com/questions/19855920/wpf-scrollviewer-consuming-touch-before-longpress

    希望我的回复会对你有所帮助。

    Best Regards,

    Xavier Eoro


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • 已标记为答案 czysofthz 2015年4月26日 14:45
    2015年4月24日 3:27
    版主
  • 你好,使用触摸来拉动滚动条,用户体验是很差的

    我试一下自定义ScrollView,先谢了,试好之后我再来说结果

    2015年4月24日 10:14
  • 刚才试了一下,那个自定义的滚动控件不好用,全部都是灰的

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:my="clr-namespace:WpfApplication2"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <my:ScrollViewerWithTouch Background="Gray" x:Name="scrolls" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
                <Grid Name="docrect" Background="White" HorizontalAlignment="Center"  Height="100" VerticalAlignment="Center" Width="100"
                              ManipulationStarting="docrect_ManipulationStarting"  ManipulationDelta="docrect_ManipulationDelta"  IsManipulationEnabled="True">
                    <TextBlock Text="abc"></TextBlock>
                </Grid>
            </my:ScrollViewerWithTouch>
        </Grid>
    </Window>
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApplication2
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
            private void docrect_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
            {
                e.ManipulationContainer = this;
                //e.Handled = true;
            }
    
            private void docrect_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
            {
    
                Grid gridtoResize = e.OriginalSource as Grid;
    
                gridtoResize.Width = gridtoResize.Width * e.DeltaManipulation.Scale.X;
                gridtoResize.Height = gridtoResize.Height * e.DeltaManipulation.Scale.Y;
    
                if (e.IsInertial)
                {
                    e.Complete();
                }
    
    
                e.Handled = true;
            }
        }
    }
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Windows.Controls.Primitives;
    
    namespace WpfApplication2
    {
        /// <summary>
        /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
        ///
        /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
        /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
        /// 元素中: 
        ///
        ///     xmlns:MyNamespace="clr-namespace:WpfApplication2"
        ///
        ///
        /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
        /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
        /// 元素中: 
        ///
        ///     xmlns:MyNamespace="clr-namespace:WpfApplication2;assembly=WpfApplication2"
        ///
        /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
        /// 并重新生成以避免编译错误: 
        ///
        ///     在解决方案资源管理器中右击目标项目,然后依次单击
        ///     “添加引用”->“项目”->[浏览查找并选择此项目]
        ///
        ///
        /// 步骤 2)
        /// 继续操作并在 XAML 文件中使用控件。
        ///
        ///     <MyNamespace:ScrollViewerWithTouch/>
        ///
        /// </summary>
        public class ScrollViewerWithTouch : ScrollViewer   
        {
            /// <summary>
            /// Original panning mode.
            /// </summary>
            private PanningMode panningMode;
    
            /// <summary>
            /// Set panning mode only once.
            /// </summary>
            private bool panningModeSet;
    
            /// <summary>
            /// Initializes static members of the <see cref="ScrollViewerWithTouch"/> class.
            /// </summary>
            static ScrollViewerWithTouch()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerWithTouch), new FrameworkPropertyMetadata(typeof(ScrollViewerWithTouch)));
            }
    
            protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)
            {
                base.OnManipulationCompleted(e);
    
                // set it back
                this.PanningMode = this.panningMode;
            }
    
            protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
            {
                // figure out what has the user touched
                var result = VisualTreeHelper.HitTest(this, e.ManipulationOrigin);
                if (result != null && result.VisualHit != null)
                {
                    var hasButtonParent = this.HasButtonParent(result.VisualHit);
    
                    // if user touched a button then turn off panning mode, let style bubble down, in other case let it scroll
                    this.PanningMode = hasButtonParent ? PanningMode.None : this.panningMode;
                }
    
                base.OnManipulationStarted(e);
            }
    
            protected override void OnTouchDown(TouchEventArgs e)
            {
                // store panning mode or set it back to it's original state. OnManipulationCompleted does not do it every time, so we need to set it once more.
                if (this.panningModeSet == false)
                {
                    this.panningMode = this.PanningMode;
                    this.panningModeSet = true;
                }
                else
                {
                    this.PanningMode = this.panningMode;
                }
    
                base.OnTouchDown(e);
            }
    
            private bool HasButtonParent(DependencyObject obj)
            {
                var parent = VisualTreeHelper.GetParent(obj);
    
                if ((parent != null) && (parent is ButtonBase) == false)
                {
                    return HasButtonParent(parent);
                }
    
                return parent != null;
            }
    
        }
    }
    

    2015年4月24日 10:36