Подтвердить что ты не робот

Остановить WPF ScrollViewer, автоматически прокручивая до предполагаемого содержимого

Приложение

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

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

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

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

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

Screenshot1

Снимок экрана, когда щелкнут левый слайдер:

enter image description here

Что я хочу:

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

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

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

РЕДАКТИРОВАТЬ: кажется, что заполнение не является проблемой, прочитайте комментарии о том, почему.

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

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

Примечания:

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

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

4b9b3361

Ответ 1

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

Ответ 2

Это может не сработать в этом конкретном сценарии, но простое, чистое решение, позволяющее ScrollViewer не прокручивать сфокусированный элемент в поле зрения, состоит в том, чтобы сделать элемент не сфокусированным с помощью Focusable=False. Если элемент не может быть сфокусирован, то он также не будет автоматически прокручиваться в поле зрения.