Home > Software design >  WPF ScrollViewer starts scrolling to early
WPF ScrollViewer starts scrolling to early

Time:06-29

I need a way to make a button overlap a textblock when resizing horizontally to a smaller size. This works fine for the first example, but when i put the same code in a scrollviewer, the horizontal scrollbar kicks in from the moment the textblock and button meet. How can i avoid this so that the button overlaps the same way as without the scrollviewer? Ideally scrolling should start from the moment that the textblock reaches its minimum width.

    <!--works-->
<StackPanel>
    <Grid Height="30" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"  HorizontalAlignment="Left"></TextBlock>
        <Button Grid.Column="1" MinWidth="20">te</Button>
    </Grid>
</StackPanel>

    <!--doesn't work-->
<StackPanel>
    <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
        <Grid Height="30" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"  HorizontalAlignment="Left"></TextBlock>
            <Button Grid.Column="1" MinWidth="20">te</Button>
        </Grid>
    </ScrollViewer>
</StackPanel>

Update

In fact i can reduce the problem to a textblock in a scrollviewer. I would like that it would only become possible for the horizontal scrollbar to scroll when the min width of the textblock is reached.

<ScrollViewer HorizontalScrollBarVisibility="Visible" 
                  VerticalScrollBarVisibility="Disabled">
    <TextBlock Background="Yellow" 
               MinWidth="50">reallylooooooooooooooooooooong 
               text</TextBlock>
</ScrollViewer>

CodePudding user response:

If you want to show multiple elements in a panel, governed by a scrollviewer you may use an ItemsControl inside the ScrollViewer

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" >
      <ItemsControl >
        <Grid Height="30">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
          </Grid.ColumnDefinitions>
          <TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"
                     HorizontalAlignment="Left"></TextBlock>
          <Button Grid.Column="1"
                  MinWidth="20">te</Button>
        </Grid>
        <Grid Height="30">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
          </Grid.ColumnDefinitions>
          <TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"
                     HorizontalAlignment="Left"></TextBlock>
          <Button Grid.Column="1"
                  MinWidth="20">te</Button>
        </Grid>
      </ItemsControl>
    </ScrollViewer>

CodePudding user response:

You'll need to re-template the ScrollViewer control. Start by extracting the template by moving the cursor over the ScrollViewer control and from the Properties pane select Miscellaneous -> Template -> Convert To New Resource (from the drop-down). The ScrollContentPresenter in the new template that appears is what draws everything inside the "client area":

<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>

So move your 2-column grid from outside the template to that point there, and move the ScrollContentPresenter to it's left column and your button to it's right. You should also set the button's collumn width to "Auto" so that it gets all the space it needs. Final ControlTemplate should look like this:

<ControlTemplate x:Key="ScrollViewerTemplate1" TargetType="{x:Type ScrollViewer}">
    <Grid x:Name="Grid" Background="{TemplateBinding Background}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
                <Button Grid.Column="1" MinWidth="20">te</Button>
            </Grid>
        </Grid>
        <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
        <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
    </Grid>
</ControlTemplate>

And your XAML should look like this:

<StackPanel>
    <ScrollViewer Template="{DynamicResource ScrollViewerTemplate1}" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
        <Grid Height="30" >
            <TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"  HorizontalAlignment="Left"></TextBlock>
        </Grid>
    </ScrollViewer>
</StackPanel>

Result:

enter image description here

  • Related