TTGroup TTGroup - 1 month ago 13
C# Question

ScrollToVerticalOffset() doesn't work?

I'm using ScrollView in WPF, and my app allow user click on a button and then it will auto scroll the scrollview, I use ScrollToVerticalOffset() in button click event, but the scrollview not changed anything.

I searched about this issue on internet, but so far I can't not solve it yet.

And one more question: ScrollToVerticalOffset() take a double as parameter, it may will scroll to the special pixels, there any way to scroll to n items (not pixel)?

Here is my code

<ScrollViewer x:Name="scrollViewerChannelBtns" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Center"
Background="Transparent" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"
CanContentScroll="True" ScrollChanged="ScrollViewerChannelBtns_ScrollChanged">
<StackPanel x:Name="channelBtns" Orientation="Vertical">
<ItemsControl x:Name="channelBtnItems" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton x:Name="tgbChannelName" Width="{Binding Path=ChannelNameBtnWidth}" Height="{Binding Path=ChannelNameBtnHeight}" HorizontalAlignment="Left" VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="{Binding Path=ChannelName}" Tag="{Binding Path=Index}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>


C# Code

//The button click event handled
private void BtnScrollDownClicked(object sender, RoutedEventArgs e)
{ scrollViewerChannelBtns.ScrollToVerticalOffset(scrollViewerChannelBtns.VerticalOffset + 50);
}
private void BtnScrollUpClicked(object sender, RoutedEventArgs e)
{ scrollViewerChannelBtns.ScrollToVerticalOffset(scrollViewerChannelBtns.VerticalOffset - 50);
}


Many thanks,
T&T

Answer

For me, this example works:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

<Grid>
    <ScrollViewer x:Name="scrollViewerChannelBtns" HorizontalAlignment="Center" Height="100" CanContentScroll="False" VerticalAlignment="Center" 
          Background="Transparent" VerticalScrollBarVisibility="Auto">

        <StackPanel x:Name="channelBtns" Orientation="Vertical">
            <ItemsControl x:Name="channelBtnItems">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ToggleButton x:Name="tgbChannelName" Width="40" Height="20" HorizontalAlignment="Left" VerticalAlignment="Center" IsChecked="{x:Null}" Content="Test" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>

                <sys:String>Item 1</sys:String>
                <sys:String>Item 2</sys:String>
                <sys:String>Item 3</sys:String>
                <sys:String>Item 4</sys:String>
                <sys:String>Item 5</sys:String>
                <sys:String>Item 6</sys:String>
                <sys:String>Item 7</sys:String>
                <sys:String>Item 8</sys:String>
                <sys:String>Item 9</sys:String>
                <sys:String>Item 10</sys:String>
            </ItemsControl>
        </StackPanel>
    </ScrollViewer>

    <Button Name="Up" Width="50" Height="30" VerticalAlignment="Top" Margin="110,0,0,0" Content="Up" Click="Up_Click" />

    <Button Name="Down" Width="50" Height="30" VerticalAlignment="Top" Margin="210,0,0,0" Content="Down" Click="Down_Click" />
</Grid>

In the example I set the height for the ScrollViewer and CanContentScroll set false. Quote from answer why setting ScrollViewer.CanContentScroll to false disable virtualization:

ScrollViewer currently allows two scrolling modes: smooth pixel-by-pixel scrolling (CanContentScroll = false) or discrete item-by-item scrolling (CanContentScroll = true). Currently WPF supports UI virtualization only when scrolling by item. Pixel-based scrolling is also called “physical scrolling” and item-based scrolling is also called “logical scrolling”.

Virtualization requires item-based scrolling so it can keep track of logical units (items) currently in view... Setting the ScrollViewer to pixel-based scrolling their is no more concept of logic units but only pixels!

Code behind

private void Up_Click(object sender, RoutedEventArgs e)
{
    scrollViewerChannelBtns.ScrollToVerticalOffset(scrollViewerChannelBtns.VerticalOffset - 50);
}

private void Down_Click(object sender, RoutedEventArgs e)
{
    scrollViewerChannelBtns.ScrollToVerticalOffset(scrollViewerChannelBtns.VerticalOffset + 50);
}

Scrolling elements not supported by default, so you have to look at these links:

Consolidated Scrolling - "Pixel by Pixel" + "Item by Item"

ScrollViewer's Viewport Height VS Actual Height