Остановите автоматическую прокрутку WPF ScrollViewer для просмотра содержимого - PullRequest
30 голосов
/ 05 декабря 2011

Приложение

Я создаю приложение, которое включает в себя селектор диапазона.Он состоит из двух пользовательских Slider элементов управления, содержащихся в одном UserControl производном классе.В этом случае элемент управления диапазоном находится внутри ScrollViewer, в котором большую часть времени отображается HorizonalScrollBar.

Пример кода приложения: (извинения за стену текста)

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, кажется, автоматически корректирует горизонтальное смещение ScrollViewer, когда любой из Slider получает фокус (например, щелчком мыши).

Воспроизведение поведения

  1. Запустите приложение, вы увидите, что горизонтальная полоса прокрутки ScrollViewer видна.
  2. Нажмите на зеленый (крайний левый) Slider, вы увидитечто ScrollViewer автоматически настраивается для смещения горизонтального смещения туда, где начинается воспринимаемый «контент».

Эти симптомы появляются на любом конце панели прокрутки.

Снимок экрана приложения, когда оновыполняется (приложение увеличено на 200% для ясности деталей):

Screenshot1

Снимок экрана поведения при нажатии левого ползунка:

enter image description here

То, что я хочу, чтобы произошло:

Когда я нажимаю на любой элемент ползунка (на любом конце), когда ползунок выглядит за пределами конца ползунка (диапазон ползункаобозначенныйk бар вверху) Я не хочу, чтобы ScrollViewer автоматически корректировал свое горизонтальное смещение.

Предполагаемая проблема:

Я подозреваю, чтопроблема в том, что ScrollViewer воспринимает фактический «контент» своего ребенка, начиная с 15 пикселей (ширина прорисовки обоих моих ползунков), откуда начинается фактическое прорисованное содержимое.Canvas рисует только потому, что я включил отступ 15 пикселей внутри элемента управления SliderTest в главном окне, если этот отступ удален, ScrollViewer не отображает холст Slider.

EDIT: кажется, что заполнение не является проблемой, прочитайте комментарии о том, почему.

Вещи, которые я пробовал

Я попытался переопределить OnPreviewMouseDownСобытие главного окна.Проблема в том, что я все еще хочу, чтобы оба Slider работали нормально, установка события на Handled приводит к тому, что Slider перестает работать полностью.

Примечания:

Внутри Sliderэлемент управления диапазоном выбора (в данном примере называется SliderTest) должен иметь ширину 1 пиксель.Ползунки должны иметь возможность увеличиваться на 15 пикселей за пределы диапазона выбора времени (см. Справочную информацию по черной полосе вверху).

Спасибо за чтение этой новой проблемы.

1 Ответ

41 голосов
/ 05 декабря 2011

По умолчанию, когда элемент управления получает логический фокус, FrameworkElement вызывает свой собственный метод BringIntoView (изнутри его метода OnGotFocus, если он имеет фокус клавиатуры).Это приводит к генерации события RequestBringIntoView , которое пузырится на дереве элементов, чтобы позволить элементам-предкам представить эту часть элемента в поле зрения.ScrollViewer прослушивает это событие и в конечном итоге вызовет MakeVisible для связанного IScrollInfo / ScrollContentPresenter, который оставляет его на панели для отображения этой части (поскольку панель будет знать, как она размещает своих дочерних элементов).Затем он возвращает возвращенный прямоугольник, который получает обратно, и запрашивает, чтобы эта его часть была отображена (в случае, если у вас были вложенные элементы, которые потребовали бы некоторых действий, чтобы обеспечить просмотр исходного элемента).Таким образом, один из способов подавить это поведение - обработать событие RequestBringIntoView на ползунках и отметить обработанное событие.

...