2

I have this code:

<TabItem Header="Card Details">
    <ScrollViewer Name="cardDetailsScroll" MaxHeight="600" Width="Auto" Padding="10">
        <StackPanel>
            <Canvas Height="50">
            </Canvas>
            ...
        </StackPanel>
    </ScrollViewer>
</TabItem>

Each time I change Tab then come back, the ScrollViewer reset its offset (to the top). Am I wrong when using StackPanel? Or did I miss something?

Thanks.

Onfealive
  • 70
  • 8

3 Answers3

1

This is the expected behavior of WPF as you can read from THIS thread. If you use MVVM you could bind the offset postion of the scroll viewer using an attached behavior suggested HERE, or if you prefer customizing the tab control, the former link contains a good starting point.

Community
  • 1
  • 1
Daniel Leiszen
  • 1,827
  • 20
  • 39
1

From the Daniel Leiszen's first link:

The default behavior of WPF is to unload items which are not visible, which includes unloading TabItems which are not visible. This means when you go back to the tab, the TabItem gets re-loaded, and anything not bound (such as a scroll position) will get reset.

I had thought this solution before I asked, I just want to know a few other solutions to help me improve knowledge:

private void cardDetailsScroll_Loaded(object sender, RoutedEventArgs e)
{
    double offset;
    if (cardDetailsScroll.Tag != null 
         && double.TryParse(cardDetailsScroll.Tag.ToString(), out offset))
    {
        cardDetailsScroll.ScrollToVerticalOffset(offset);                
    }
}

private void cardDetailsScroll_Unloaded(object sender, RoutedEventArgs e)
{
    cardDetailsScroll.Tag = cardDetailsScroll.VerticalOffset;
}
Onfealive
  • 70
  • 8
  • I think, I gave you two different solutions, the binding (which I personally prefer) is suitable when you use MVVM (and I would suggest you to do so), and the custom control. These are both valid and reusable solutions. You can certainly store the scroll position somewhere else eg. the Tag property. I would not suggest that, since this is not a generic solution and harder to reuse the code when you need somewhere else. Anyways, from your question it was not clear that you already had an answer for it. My bad, sorry for answering. – Daniel Leiszen Nov 15 '16 at 11:50
  • @DanielLeiszen I will learn more to understand about 2 that solution. Maybe I can use them in the future. Thanks. – Onfealive Nov 19 '16 at 09:33
  • This one works (on Loaded and Unloaded). Had a hard time to even google the issue. Was looking and looking for what in the code was setting the position to 0 all the time when switching tabs. – Wolf5 Nov 30 '17 at 12:15
1

I had a similar problem with a TabControl. Each tab shows graphics from a UserControl and when I switched between tabs, the scroll viewer scrolled back to the origin after activating the tab.

I used a similar approach as Onfealive proposed. But I used the HorizontalOffset and VerticalOffset instead of the Tag.

    private void m_ScrollViewer_Loaded(object sender, RoutedEventArgs e)
    {
        m_ScrollViewer.ScrollToHorizontalOffset(m_HorizontalOffset);
        m_ScrollViewer.ScrollToVerticalOffset(m_VerticalOffset);
    }

    private void m_ScrollViewer_Unloaded(object sender, RoutedEventArgs e)
    {
        m_HorizontalOffset = m_ScrollViewer.HorizontalOffset;
        m_VerticalOffset = m_ScrollViewer.VerticalOffset;
    }

This works for me and I think this approach is safer!

  • In my case, I need to invoke the actions inside the Loaded handler in Application.Current.Dispatcher.InvokeAsync with DispatcherPriority.Send. – YantingChen Feb 02 '21 at 02:46