wpf wpf-controls scrollviewer

Detenga WPF ScrollViewer desplazándose automáticamente al contenido percibido



scrollviewer xaml (1)

La aplicación

Estoy construyendo una aplicación que incluye un selector de rango. Consiste en dos controles Slider dibujados personalizados contenidos en una clase derivada de UserControl . El control del selector de rango se encuentra dentro de un ScrollViewer que tiene la barra de barras horizontal (HorizonalScrollBar) visible la mayor parte del tiempo.

Código de aplicación de muestra: (disculpas por el muro de texto)

Window.xaml (el archivo de la ventana):

<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 }

El problema La mayor parte del código de muestra proporcionado es aburrido y la mecánica funciona bastante bien. El problema que tengo es un problema visual específicamente con el control ScrollViewer que tengo en la ventana principal. El ScrollViewer parece estar ajustando automáticamente el desplazamiento horizontal del ScrollViewer cuando alguno de los Slider del Slider (por ejemplo, con un clic del mouse).

Reproduciendo el comportamiento.

  1. Ejecute la aplicación, verá que la barra de desplazamiento horizontal del ScrollViewer es visible.
  2. Haga clic en el Slider verde (extremo izquierdo), observará que el ScrollViewer se ajusta automáticamente para desplazar el desplazamiento horizontal a donde comienza el "contenido" percibido.

Estos síntomas se producen en cualquier extremo del panel de desplazamiento.

Captura de pantalla de la aplicación cuando se ejecuta (la aplicación se amplía en 200% para mayor claridad de detalle):

Captura de pantalla del comportamiento cuando se hace clic en el control deslizante izquierdo:

Lo que quiero que suceda:

Cuando hago clic en cualquiera de los elementos del control deslizante (en cualquier extremo) cuando un control deslizante parece estar más allá del final del control deslizante (el rango del control deslizante se indica mediante la barra negra en la parte superior) No quiero que el ScrollViewer ajuste automáticamente el desplazamiento horizontal.

Sospechoso problema:

Sospecho que el problema es que el ScrollViewer percibe el "contenido" real de sus hijos, comienza 15 píxeles (el ancho dibujado de mis dos controles deslizantes) desde donde comienza el contenido real dibujado. El Lienzo solo se dibuja porque SliderTest un relleno de 15 píxeles dentro del control SliderTest en la ventana principal. Si se elimina este relleno, ScrollViewer no muestra ninguno de los SliderTest control deslizante.

EDITAR : parece que el relleno no es el problema, lea los comentarios de por qué.

Cosas que he probado

He intentado buscar anular el evento OnPreviewMouseDown de la ventana principal. El problema aquí es que todavía quiero que ambos Slider se comporten normalmente, al configurar el evento en Handled hace que el Slider deje de funcionar por completo.

Notas:

El control deslizante del control deslizante dentro del control de rango (llamado Prueba de control deslizante en este ejemplo) debe tener un ancho de 1 píxel. El control deslizante debe poder extender 15 píxeles más allá del final del rango de selección de tiempo (consulte la barra negra en la parte superior para obtener una referencia).

Gracias por leer este novedoso problema.


De forma predeterminada, cuando un control recibe el enfoque lógico, FrameworkElement llama a su propio método BringIntoView (desde dentro de su método OnGotFocus si tiene el enfoque del teclado). Eso da lugar a que se RequestBringIntoView un evento RequestBringIntoView que burbujea el árbol de elementos para permitir que los elementos del antepasado RequestBringIntoView esa parte del elemento. El ScrollViewer escucha este evento y, finalmente, llamará a MakeVisible en el IScrollInfo / ScrollContentPresenter asociado, lo que lo deja en el panel para mostrar esa parte (ya que el panel sabría cómo organiza a sus hijos). A continuación, toma el mensaje de retorno que recibe y solicita que se muestre esa parte de sí mismo (en caso de que haya elementos anidados que requieran alguna acción para garantizar que el elemento original se haya visto). Entonces, una forma de suprimir este comportamiento sería manejar el evento RequestBringIntoView en los controles deslizantes y marcar el evento manejado.