FluentAvalonia
FluentAvalonia copied to clipboard
Flicker when switching between nested TabViews
Describe the bug When using a nest of TabViews, switching between the top level tabs causes the tab content being switched to to flicker, as though there is a blank frame before the child content is shown.
Desktop/Platform
- OS: MacOS, but I believe this also happens on Windows.
- FluentAvalonia Version: 2.0.1
- Avalonia Version: 11.0.2
Additional context This only seems to happen if the tab content being shown at the top level is a view model rather than a direct view, and the sub view that represents this view model contains another TabView i.e.:
MainView:
<ui:TabView>
<ui:TabViewItem Header="Header">
<vm:SubViewModel />
</ui:TabViewItem>
<ui:TabViewItem Header="Header">
<vm:SubViewModel />
</ui:TabViewItem>
</ui:TabView>
SubView:
<ui:TabView>
<ui:TabViewItem Header="Header">
<Grid Background="DarkRed" MinHeight="800" />
</ui:TabViewItem>
</ui:TabView>
If the MainView
TabViewItem content is another view, or the SubView
TabView is placed directly in MainView
, the issue doesn't seem to occur. I've created a very simple reproducer project here: https://github.com/brads55/AvTest.
I was able to get rid of the flicker by removing the MinHeight="800"
, which also made the inner TabView show correctly. 800 is kind of an non-sensible value for height, considering 1080 is probably the max most people have, and that's before display scaling considerations.
Also note, TabView has a default VerticalAlignment
of Top
, it's probably better to set VerticalAlignment="Stretch"
than a MinWidth.
I'm also testing on a much simpler case of just straight nested TabViews which should be the same thing, in principle:
<ui:TabView VerticalAlignment="Stretch">
<ui:TabViewItem Header="Header1">
<ui:TabView VerticalAlignment="Stretch">
<ui:TabViewItem Header="SubHeader">
<Grid Background="DarkRed" />
</ui:TabViewItem>
</ui:TabView>
</ui:TabViewItem>
<ui:TabViewItem Header="Header2">
<ui:TabView>
<ui:TabViewItem Header="SubHeader2">
<Grid Background="DarkRed" />
</ui:TabViewItem>
</ui:TabView>
</ui:TabViewItem>
</ui:TabView>
The MinHeight
was just there to ensure the control was visible to see the flicker, but yes, using VerticalAlignment=Stretch
is more sensible. Replacing that, I still see the flicker however.
When I was testing this, I noted that using a nested viewmodel like I have, rather than all in one xaml file like you have, made the flicker much more reproducible and apparent. I think the flicker may still be there with it all in one xaml file, however it only seems to happen on the first tab switch, after that it seems to go away. Which I guess suggests this may be something to do with view construction?
I'd highly recommend not using the ViewLocator approach to templates. I thought that was supposed to be removed from the default Avalonia template.
Fixing your MainWindow.axaml file like below, I no longer see the flickering:
<Window.DataTemplates>
<DataTemplate DataType="vm:SubViewModel">
<views:SubView />
</DataTemplate>
</Window.DataTemplates>
<ui:TabView x:Name="MainTab" VerticalAlignment="Stretch">
<ui:TabViewItem Header="Header">
<vm:SubViewModel />
</ui:TabViewItem>
<ui:TabViewItem Header="Header">
<vm:SubViewModel />
</ui:TabViewItem>
</ui:TabView>
Hm ok, using that approach does appear to fix the flicker, however I'm still getting odd behaviour when nesting TabViews.
I've updated the linked repository to extend the testcase to show two more issues I'm hitting:
- When switching top level tabs, the lower level tabs seem to collapse and expand, i.e. you can visually see
SubView
changing between 1 and 3 tabs. - The
SelectedItem
binding of the childTabView
doesn't seem to persist when changing top level tab, i.e. you end up with no selected tab inSubView
when changing top level tab. (I thinkSelectedIndex
also has similar problems).
I've had more of a dig into this and I don't believe this has anything to do with nesting TabViews, it's more about the initial state of the TabView. When a TabView is created it is rendered before the DataContext applied to it has properly taken effect. This manifests itself it two ways:
Upon creating a brand new TabView with a DataContext applied at time of construction, the tab content presenter is displayed empty before the real contents get applied, this is what is causing the flicker I reported. (The ViewLocator was not recycling views).
When applying a different DataContext to an existing TabView, the TabListView seems to update late which results in visually being able to see TabListView contents changing, as well as the selected item logic breaking as it tries to update the selected item prior to the TabListView items changing. This is what I observe with the changes you suggested. (The DataTemplate does recycle views).
I've updated the testcase at https://github.com/brads55/AvTest to better demonstrate what is going on.