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

时间:2011-12-05 10:56:58

标签: wpf wpf-controls scrollviewer

应用

我正在构建一个包含范围选择器的应用程序。这包含一个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控件。当ScrollViewer中的任何一个获得焦点时(例如,通过鼠标点击),ScrollViewer似乎会自动调整Slider的水平偏移。

重现行为

  1. 运行应用程序,您将看到ScrollViewer的水平滚动条可见。
  2. 点击绿色(最左侧)Slider,您会注意到ScrollViewer会自动调整以将水平偏移移动到感知“内容”开始的位置。
  3. 这些症状出现在滚动窗格的两端。

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

    Screenshot1

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

    enter image description here

    我想要发生什么:

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

    疑似问题:

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

    编辑:看来填充不是问题,请阅读评论原因。

    我尝试过的事情

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

    备注:

    范围选择器控件(本例中称为SliderTest)中的Slider必须都具有1像素的宽度。滑块必须能够在时间选择范围的末尾延伸15个像素(参见顶部的黑条以供参考)。

    感谢您阅读这篇新颖的问题。

2 个答案:

答案 0 :(得分:41)

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

答案 1 :(得分:1)

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