停止WPF ScrollViewer自动滚动到感知内容

38

应用程序

我正在构建一个应用程序,其中包括一个范围选择器。这由两个自定义绘制的 Slider 控件组成,它们被包含在一个派生自UserControl类的控件中。范围选择器控件然后包含在一个ScrollViewer中,该容器大部分时间都可见水平滚动条。

示例应用程序代码: ( 抱歉文字较多 )

Window.xaml ( 窗口文件 ):

<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 控件时,ScrollViewer 控件似乎会在任一 Slider(例如来自鼠标点击)获得焦点时自动调整水平偏移量。

重现行为

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

这些症状发生在滚动窗格的任一端。

运行应用程序时的屏幕截图(应用程序缩放了200%以获得清晰度):

Screenshot1

左侧滑块被单击时的行为的屏幕截图:

enter image description here

我希望发生的事情:

当我单击任一滑块项(在滑块范围超出滑块的末端时)时,我不希望 ScrollViewer 自动调整其水平偏移量。

疑似问题:

我怀疑问题在于 ScrollViewer 认为它的子元素的实际“内容”从其实际绘制内容的开始位置向内15像素(我的两个滑块的绘制宽度)开始。仅因为我在主窗口的 SliderTest 控件中包含了 15 像素的填充,所以画布才会被绘制。如果删除此填充,ScrollViewer 就不会显示任何 Slider 的画布。

编辑:看起来填充并不是问题,请阅读评论以获取详细信息。

我尝试过的事情

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

注:

范围选择器控件(本示例中称为 SliderTest)内的滑块必须都有 1 像素的宽度。滑块必须能够延伸到时间选择范围的末端之外 15 像素(请参见顶部的黑色条形图)。

感谢您阅读这个小说般的问题。


3
谢谢你提问得这么好!+1 - Rachel
这似乎发生在所有的“滑块”上,而不仅仅是你的“用户控件”。如果你注释掉你的测试滑块,并用一个包含对象(如“TextBlock”)和一个带有宽度(如900)的“Slider”的水平“StackPanel”来替换它,你会得到相同的行为。 - Rachel
2个回答

48

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


3

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接