Search code examples
c#wpftabcontroltabitembusyindicator

BusyIndicator for TabControl


I'm working with a TabControl which has a few TabItems. Inside these TabItems, there are several charts being bound to a large data source. So, every time the user tries to navigate between the tabs, the system would freeze to take a few seconds to load the contents of the tab. Also, these charts were configured and bound to the data as soon as the system starts. Not when the user clicks the tabs.

I would like to show the Loading.... logo with a transparent background while the user waits. But because of all of these charts were configured at the start of the system (before the UI shows up), I cannot show the BusyIndicator at this stage. It seems like the UI is taking some time to prepare these charts because there are no events which tries to call the backend side except the two-way binding.

My TabControl looks like this:

<TabControl x:Name="TabMain"
 Grid.RowSpan="2"
 ....
 ....
 Style="{DynamicResource TabControl}">
 <TabItem x:Name=".." DataContext="{Binding Source={StaticResource Locator}}" ....>
 <TabItem x:Name=".." ....>
 ....
</TabControl>

I've been finding a way to show the BusyIndicator at the appropriate time without being have to hard-code the delay time. Any idea how to achieve this?


Solution

  • Use SelectionChanged event and add a loading logo, Rectangle in this example.

    XAML

    <TabControl x:Name="TabMain" SelectionChanged="tabMain_SelectionChanged"
     Grid.RowSpan="2"
     ....
     ....
     Style="{DynamicResource TabControl}">
     <TabItem x:Name=".." DataContext="{Binding Source= {StaticResource Locator}}" ....>
     <TabItem x:Name=".." ....>
     ....
    </TabControl>
    <Rectangle x:Name="LoadingLogo" Fill = "White" Visibility="Collapsed" Opacity=0.5/>
    

    C#

    private void tabMain_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl) //if this event fired from TabControl then enter
        {
                //show loading logo
                LoadingLogo.Visibility = Visibility.Visible;
    
                //hide the loading logo with lower priority, so that it closes after UI finished loading
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(()=> { LoadingLogo.Visibility = Visibility.Collapsed; }));
        }
    }
    

    More info about Dispatcher.Invoke.

    In the case of Rectangle not showing due to UI loading, you might want to use a borderless translucent Window as your loading logo.