【问题标题】:Stop WPF ScrollViewer automatically scrolling to perceived content停止 WPF ScrollViewer 自动滚动到感知内容
【发布时间】:2012-01-13 03:06:57
【问题描述】:

应用程序

我正在构建一个包含范围选择器的应用程序。这包括两个自定义绘制的Slider 控件,这些控件包含在一个UserControl 派生类中。然后,范围选择器控件包含在 ScrollViewer 中,其中 Horizo​​nalScrollBar 大多数时间都是可见的。

示例应用程序代码:(适用于文字墙)

Window.xaml(Window 文件):

<Grid>
    <ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Visible"  VerticalScrollBarVisibility="Disabled">
            <local:SliderTest x:Name="slider"                                                                         
                           LowerValue="0"
                           UpperValue="10"
                           Minimum="0"
                           Maximum="100" Width="900" Height="165" Padding="15,0,15,0" HorizontalAlignment="Left">
            </local:SliderTest>
    </ScrollViewer>
</Grid>

SliderTest.xaml:

<UserControl x:Class="scrollviewerDemoProblem.SliderTest"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             x:Name="root"
             xmlns:local="clr-namespace:scrollviewerDemoProblem"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <ControlTemplate x:Key="simpleSlider" TargetType="{x:Type Slider}">
            <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Track x:Name="PART_Track" Grid.Row="1">
                        <Track.Thumb>
                            <Thumb x:Name="Thumb" FlowDirection="LeftToRight" Width="15">
                                <Thumb.Template>
                                    <ControlTemplate TargetType="Thumb">
                                        <Canvas>
                                            <Path x:Name="test1" StrokeThickness="0" Fill="DarkGreen">
                                                <Path.Data>
                                                    <GeometryGroup FillRule="NonZero">
                                                        <PathGeometry>
                                                            <PathGeometry.Figures>
                                                                <PathFigure IsClosed="True" StartPoint="0,150" IsFilled="True">
                                                                    <PathFigure.Segments>
                                                                        <PathSegmentCollection>
                                                                            <LineSegment Point="-15,150" />
                                                                            <LineSegment Point="-15,0" />
                                                                            <LineSegment Point="0,0" />
                                                                        </PathSegmentCollection>
                                                                    </PathFigure.Segments>
                                                                </PathFigure>
                                                            </PathGeometry.Figures>
                                                        </PathGeometry>
                                                    </GeometryGroup>
                                                </Path.Data>
                                            </Path>
                                        </Canvas>
                                    </ControlTemplate>
                                </Thumb.Template>
                            </Thumb>
                        </Track.Thumb>
                    </Track>
                </Grid>
            </Border>
        </ControlTemplate>

        <ControlTemplate x:Key="simpleSliderRight" TargetType="{x:Type Slider}">
            <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Track x:Name="PART_Track" Grid.Row="1">
                        <Track.Thumb>
                            <Thumb x:Name="Thumb" HorizontalAlignment="Center" HorizontalContentAlignment="Center" Width="15">
                                <Thumb.Template>
                                    <ControlTemplate TargetType="Thumb">
                                        <Canvas>
                                            <Path Stroke="Black" StrokeThickness="0" Fill="DarkCyan">
                                                <Path.Data>
                                                    <GeometryGroup FillRule="NonZero">
                                                        <PathGeometry>
                                                            <PathGeometry.Figures>
                                                                <PathFigure IsClosed="True" StartPoint="0,150">
                                                                    <PathFigure.Segments>
                                                                        <PathSegmentCollection>
                                                                            <LineSegment Point="15,150" />
                                                                            <LineSegment Point="15,0" />
                                                                            <LineSegment Point="0,0" />
                                                                        </PathSegmentCollection>
                                                                    </PathFigure.Segments>
                                                                </PathFigure>
                                                            </PathGeometry.Figures>
                                                        </PathGeometry>
                                                    </GeometryGroup>
                                                </Path.Data>
                                            </Path>
                                        </Canvas>
                                    </ControlTemplate>
                                </Thumb.Template>
                            </Thumb>
                        </Track.Thumb>
                    </Track>
                </Grid>
            </Border>
        </ControlTemplate>
    </UserControl.Resources>

    <Grid x:Name="Gridd" VerticalAlignment="Top" Height="165" >
        <Border x:Name="timeScaleBorder" Width="auto" Height="15" VerticalAlignment="Top" Background="Black">
            <Canvas x:Name="timeCanvas" Width="auto" Height="15">
            </Canvas>
        </Border>
        <Border x:Name="background" BorderThickness="1,1,1,1" BorderBrush="Black" VerticalAlignment="Center" Height="150"
                Margin="0,15,0,0" Background="White" />
        <Slider  x:Name="LowerSlider"
                Minimum="{Binding ElementName=root, Path=Minimum}"
                Maximum="{Binding ElementName=root, Path=Maximum}"
                Value="{Binding ElementName=root, Path=LowerValue, Mode=TwoWay}"
                Template="{StaticResource simpleSlider}"
                Margin="0,15,0,0" />
        <Slider  x:Name="UpperSlider"
                Minimum="{Binding ElementName=root, Path=Minimum}"
                Maximum="{Binding ElementName=root, Path=Maximum}"
                Value="{Binding ElementName=root, Path=UpperValue, Mode=TwoWay}"
                Template="{StaticResource simpleSliderRight}"
                Margin="0,15,0,0" />
    </Grid>
</UserControl>

SliderText.xaml.cs:

public partial class SliderTest : UserControl
{
    public SliderTest()
    {
        InitializeComponent();
    }

    #region Dependency properties, values etc.

    public static readonly DependencyProperty MinimumProperty =
        DependencyProperty.Register("Minimum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double LowerValue
    {
        get { return (double)GetValue(LowerValueProperty); }
        set { SetValue(LowerValueProperty, value); }
    }

    public static readonly DependencyProperty LowerValueProperty =
        DependencyProperty.Register("LowerValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double UpperValue
    {
        get { return (double)GetValue(UpperValueProperty); }
        set { SetValue(UpperValueProperty, value); }
    }

    public static readonly DependencyProperty UpperValueProperty =
        DependencyProperty.Register("UpperValue", typeof(double), typeof(SliderTest), new UIPropertyMetadata(0d));

    public double Maximum
    {
        get { return (double)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }

    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(SliderTest), new UIPropertyMetadata(1d));

    public double Minimum
    {
        get { return (double)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    #endregion        
}

问题 提供的大多数示例代码都很无聊,而且它的机制工作得很好。我遇到的问题是我在主窗口中拥有的ScrollViewer 控件的视觉问题。当Slider 的任何一个获得焦点时(例如通过鼠标单击),ScrollViewer 似乎会自动调整ScrollViewer 的水平偏移。

重现行为

  1. 运行应用,你会看到ScrollViewer的水平滚动条是可见的。
  2. 单击绿色(最左侧)Slider,您会注意到 ScrollViewer 会自动调整以将水平偏移移动到感知“内容”开始的位置。

这些症状出现在滚动窗格的任一端。

应用程序运行时的屏幕截图(应用程序放大 200% 以确保细节清晰):

单击左滑块时的行为截图:

我想要发生的事情:

当我单击任一滑块项(在任一端)时,当滑块看起来超出滑块的末端(滑块范围由顶部的黑条表示)时,我希望 ScrollViewer 自动调整它的水平偏移量。

疑似问题:

我怀疑问题在于 ScrollViewer 感知到其子项的实际“内容”从实际绘制内容开始的 15 个像素(我的两个滑块的绘制宽度)开始。 Canvas 仅绘制是因为我在主窗口的 SliderTest 控件内包含了 15 像素的填充,如果删除此填充,则 ScrollViewer 不会显示任何 Slider 的 Canvas。

编辑:看来填充不是问题,请阅读 cmets 了解原因。

我尝试过的事情

我尝试过覆盖主窗口的 OnPreviewMouseDown 事件。这里的问题是我仍然希望两个 Slider 的行为正常,将事件设置为 Handled 会导致 Slider 完全停止工作。

注意事项:

范围选择器控件(本例中称为 SliderTest)内的 Slider 的宽度必须均为 1 像素。滑块必须能够超出时间选择范围的末端 15 个像素(请参阅顶部的黑条以获取参考)。

感谢您阅读这个长篇小说。

【问题讨论】:

  • +1 写得很好的问题 :)
  • 这似乎发生在所有Sliders 上,而不仅仅是你的UserControl。如果您注释掉您的测试滑块并将其替换为水平的StackPanel,其中包含诸如TextBlock 之类的对象,并且将Slider 替换为具有Width 之类的900,您将获得相同的行为。

标签: wpf wpf-controls scrollviewer


【解决方案1】:

默认情况下,当控件接收到逻辑焦点时,FrameworkElement 会调用其自己的BringIntoView 方法(如果它具有键盘焦点,则从其 OnGotFocus 方法中调用)。这会导致生成一个RequestBringIntoView 事件,该事件使元素树冒泡,以允许祖先元素将元素的该部分带入视图。 ScrollViewer 侦听此事件,并最终将在关联的 IScrollInfo/ScrollContentPresenter 上调用 MakeVisible,这将其留给面板以将该部分显示在视图中(因为面板将知道它如何排列其子项)。然后,它接收它收到的返回的矩形,并要求将其自身的那部分显示在视图中(如果您有嵌套元素,需要采取一些措施来确保将原始元素显示在视图中)。因此,抑制这种行为的一种方法是处理滑块上的 RequestBringIntoView 事件并将该事件标记为已处理。

【讨论】:

  • 你拯救了我的一天 :)
【解决方案2】:

这在这种特定情况下可能不起作用,但防止ScrollViewer 将焦点元素滚动到视图中的简单、干净的解决方案是通过Focusable=False 使元素无法聚焦。如果一个元素不能被聚焦,那么它也不会自动滚动到视图中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-14
    • 2012-12-07
    • 1970-01-01
    相关资源
    最近更新 更多