bas
bas

Reputation: 14992

WPF How to affect the scroll amount of a scrollview

I have a TabControl, which has a TabPanel, which is put inside a ScrollViewer.

<Style x:Key="TabCtrl" TargetType="{x:Type TabControl}">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    <Setter Property="Padding" Value="2,0,0,0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <RepeatButton
                        x:Name="ScrolltoLeft_Btn"
                        Grid.Row="0"
                        Grid.Column="0"
                        VerticalAlignment="Top"
                        Command="{x:Static ScrollBar.LineLeftCommand}"
                        CommandTarget="{Binding ElementName=sv}"
                        Style="{StaticResource ScrolltoLeft}" />

                    <ScrollViewer
                        x:Name="sv"
                        Grid.Row="0"
                        Grid.Column="1"

                        HorizontalScrollBarVisibility="Hidden"
                        VerticalScrollBarVisibility="Disabled">

                        <TabPanel
                            x:Name="HeaderPanel"
                            Panel.ZIndex="1"
                            IsItemsHost="true"
                            KeyboardNavigation.TabIndex="1" />
                    </ScrollViewer>

                    <ContentPresenter
                        x:Name="PART_SelectedContentHost"
                        Grid.Row="1"
                        Grid.ColumnSpan="2"
                        Grid.Column="0"
                        Margin="{TemplateBinding Padding}"
                        ContentSource="SelectedContent"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

                    <RepeatButton
                        x:Name="ScrolltoRight_Btn"
                        Grid.Row="0"
                        Grid.Column="2"

                        VerticalAlignment="Top"
                        Command="{x:Static ScrollBar.LineRightCommand}"
                        CommandTarget="{Binding ElementName=sv}"
                        Style="{StaticResource ScrolltoRight}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

When I press the repeat buttons, the amount which is scrolled is minimal, a small portion of the width of one TabItem.

I want this to be bigger. I want (at a minimal) to scroll the width of TabItem.

I already played with the CanContentScroll property of a ScrollViewer, but that doesn't change anything.

I also tried to change the SmallChange property on the horizontal scrollbar, but that also has no impact.

myTab.ApplyTemplate();
var scrollviewer = myTab.Template.FindName("sv", myTab) as ScrollViewer;
if (scrollviewer != null)
{
    scrollviewer.ApplyTemplate();
    _scrollBar = scrollviewer.Template.FindName("PART_HorizontalScrollBar", scrollviewer) as ScrollBar;
    if (_scrollBar != null)
    {
        _scrollBar.SmallChange = 0.5;
    }
}

Upvotes: 0

Views: 1594

Answers (1)

dymanoid
dymanoid

Reputation: 15227

The SmallChange property of a WPF ScrollBar is only considered for scrolling when the scroll bar is stand-alone (that means, not in a ScrollViewer). Since you are using a ScrollViewer in your control template, this will never work. A ScrollViewer will always apply a default small change of 16 units.

You have two options now:

  • Implement IScrollInfo in a wrapper class (e.g. derived from a ContentControl), attach it to the scroll viewer as content, put the TabPanel inside of this customized wrapper class, set the CanContentScroll property of the ScrollViewer to true. Now you have full control of the scrolling.
  • Use a little hack: create an external ScrollBar and let it scroll the ScrollViewer.

I show you the second approach:

Below your ScrollViewer, add a new scroll bar:

<ScrollBar 
    x:Name="myScrollBar"
    Grid.Row="0" Grid.Column="1" Orientation="Horizontal"
    Visibility="Collapsed"
    Tag="{Binding ElementName=sv}"
    Minimum="0"
    Maximum="{Binding ScrollableWidth, ElementName=sv}"
    ViewportSize="{Binding ViewportWidth, ElementName=sv}"
    Value="{Binding HorizontalOffset, ElementName=sv, Mode=OneWay}"
    SmallChange="100"
    Scroll="MyScrollBar_OnScroll"/>

You can change the SmallChange property as you wish.

Update both scroll commands to target this scroll bar, not the ScrollViewer:

<RepeatButton
    <!-- ... -->
    CommandTarget="{Binding ElementName=myScrollBar}"/>

Finally, you need to connect the external scroll bar to the ScrollViewer:

void MyScrollBar_OnScroll(object sender, ScrollEventArgs e)
{
  ScrollBar sb = (ScrollBar)sender;
  (sb.Tag as ScrollViewer)?.ScrollToHorizontalOffset(e.NewValue);
}

(You might want to make a Behavior out of this to keep the code-behind empty, to avoid using the Tag property, and to allow reusability).

Upvotes: 2

Related Questions