Why do WPF tabs become "unresponsive" when `Dispatcher.Invoke()` is called? - c#

As I understand it, WPF "messages" (e.g. a button click handler) are added to an internal prioritized queue. A single UI thread is then responsible for processing the queued messages.
Unfortunately my knowledge of WPF is not deep enough to understand the internal working of the framework. So my question is, given that there is only 1 thread processing messages...
What is the internal sequence of events (high level) that is leading to the tabs becoming unresponsive?
Observed Behavior
If you click slowly, the TabControl behaves as expected.
To reproduce: click 1 tab every 4 seconds.
It appears that if you give the TabControl.SelectedIndex data binding an opportunity to complete, the control will behave as designed.
If you click tabs quickly, then some of the tabs will become unresponsive.
To reproduce: click as many tabs as you can within 3 seconds.
Additional Reading
Two selected tabs in tabcontroller
While the behavior is similar, this article is different because the symptom is the result of using a Tab + MessageBox.
Sample Code
The following code can be used to reproduce the behavior, whereby, WPF tabs become permanently selected.
Paste into MainWindow.xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="30" />
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<WrapPanel Grid.Row="0">
<TextBlock>
1. Click on as many tabs as possible within 3 seconds.<LineBreak/>
2. Wait until multiple tabs are selected.<LineBreak/>
3. Uncheck the `Simulate Bug` checkbox.<LineBreak/>
</TextBlock>
</WrapPanel>
<CheckBox Grid.Row="1" IsChecked="{Binding CanSimulateBug}" Content="Simulate Bug"/>
<TabControl x:Name="ColorWorkspaces" Grid.Row="2" SelectedIndex="{Binding SelectedTab, Mode=TwoWay}">
<TabItem x:Name="RedTab" Header="Red"/>
<TabItem x:Name="OrangeTab" Header="Orange"/>
<TabItem x:Name="YellowTab" Header="Yellow"/>
<TabItem x:Name="GreenTab" Header="Green"/>
<TabItem x:Name="BlueTab" Header="Blue"/>
<TabItem x:Name="VioletTab" Header="Violet"/>
</TabControl>
<TextBlock Grid.Row="3" Text="{Binding Status}"/>
</Grid>
Paste into MainWindow.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private int _selectedTab;
private string _status;
private bool _canSimulateBug;
public MainWindow()
{
this.CanSimulateBug = true;
this.Status = String.Empty;
this.DataContext = this;
InitializeComponent();
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
public bool CanSimulateBug
{
get
{
return _canSimulateBug;
}
set
{
_canSimulateBug = value;
RaisePropertyChanged();
}
}
public string Status
{
get
{
return _status;
}
set
{
_status = value ?? string.Empty;
RaisePropertyChanged();
}
}
public int SelectedTab
{
get
{
return _selectedTab;
}
set
{
UpdateStatus($"SelectedTab changing... Value={value}");
if (this.CanSimulateBug)
{
SimulateBug(value);
}
_selectedTab = value;
UpdateStatus($"SelectedTab changed. Value={value}");
// This missing line was added as per
Felix's comment
RaisePropertyChanged();
}
}
private void UpdateStatus(string message)
{
var formattedMessage = $"[{Thread.CurrentThread.ManagedThreadId}] {DateTime.Now.ToLongTimeString()}: {message}";
this.Status = formattedMessage;
Debug.WriteLine(formattedMessage);
}
private void SimulateBug(int id)
{
var delay = TimeSpan.FromSeconds(3);
UpdateStatus($"Bug simulation started... ID={id}, Delay={delay}");
// IMPORTANT: If you comment out this following line
// ... the application will behave as expected.
Application.Current.Dispatcher.Invoke( // blocking call
DispatcherPriority.Background, // tells UI thread to execute this as lowest priority job
new Action(delegate { /* do nothing */ }));
Thread.Sleep(delay);
UpdateStatus($"Bug simulation complete. ID={id}");
}
}

Your complete code is executing on a single thread. You can't execute concurrent operations using a single thread. What you are currently doing is to block the main thread twice (too long) by invoking two potentially long-running operations synchronously:
Synchronous Dispatcher invocation using Dispatcher.Invoke:
Application.Current.Dispatcher.Invoke(() => {}, DispatcherPriority.Background);
Synchronous thread sleep:
Thread.Sleep(TimeSpan.FromSeconds(3));
While executing these synchronous operations, the main thread is not free to execute the logic (which is in this case part of the Selector, which is a superclass of TabControl) that manages the selection state of the hosted items.
The main thread is blocked by waiting for the Dispatcher to return and then by sending it to sleep i.e. suspending it.
As a result the Selector is not able to unselect the previously selected TabItem.
The Selector is able to cancel the selection procedure, which involves handling the selected item and unselecting every other item (in case multi-select is not supported). Obviously, the Selector cancels the unselection/processing of the pending items.
You should be able to test this by listening to the Selector.Unselected event which you attach to the TabItem. It should not be raised. Apparently the blocking of the main thread creates a race condition for the internal item validation of the Selector.
To fix this race condition it should be sufficient to increase the DispatcherPriority of the queued dispatcher messages to at least DispatcherPriority.DataBind (above DispatcherPriority.Input):
Application.Current.Dispatcher.Invoke(() => {}, DispatcherPriority.DataBind);
This is not the recommended fix, although it fixes the race condition and therefore the issue of multiple selected tabs as the critical code can now execute in time. The real underlying problem is the blocked main thread (which actually is a blocked Dispatcher).
You never want to block the main thread. Now you understand why.
For this reason .NET introduced the TPL. Additionally, the compiler/runtime environment allows true asynchronous execution: by delegating execution to the OS, .NET can use kernel level features like interrupts. This way .NET can allow the main thread to continue e.g., to process essential UI related events like device input.
Part of the interface between OS level and framework level is the Dispatcher and the InputManager. The Dispatcher basically manages the associated thread. In an STA application this is the main thread. Now, when you block the main thread using Thread.Sleep, the Dispatcher can't continue to work on the message queue that contains handlers that are executed on the associated dispatcher thread (main thread).
The Dispatcher is now unable to execute the input events posted by the InputManager. Since the dependency property system (on which routed events and the data binding engine is based on) and usually the code of UI controls are also executed on the Dispatcher, they are also suspended.
The combination of the very low dispatcher priority DispatcherPriority.Background in conjunction with the long Thread.Sleep makes the problem even worse.
The solution is to not block the main thread:
Post work to the Dispatcher asynchronously and allow the application to continue while the job is enqueued and pending by calling Dispatcher.InvokeAsync:
Application.Current.Dispatcher.InvokeAsync(() => {}, DispatcherPriority.Background);
Execute blocking I/O bound operations asynchronously using async/await:
await Task.Delay(TimeSpan.FromSeconds(3));
Execute blocking CPU bound operations concurrently:
Task.Run(() => {});
Your fixed code would look as followed:
private async Task SimulateNoBugAsync(int id)
{
var delay = TimeSpan.FromSeconds(3);
UpdateStatus($"Bug simulation started... ID={id}, Delay={delay}");
// If you need to wait for a result or for completion in general,
// await the Dispatcher.InvokeAsync call.
Application.Current.Dispatcher.InvokeAsync(() => {}, DispatcherPriority.Background);
await Task.Delay(delay);
UpdateStatus($"Bug simulation complete. ID={id}");
}

Related

Why is my WPF aplication loading animation steel Freezing?

I am developing a desktop application for managing a forest catalog. When starting the application, certain data must be displayed from a MySql database, which slows down the start of the application, so I want to show a dialog that shows an animated gif until the data is finished loading.
The problem is that the application freezes and the dialog is not shown until the data loading is finished. I've searched other posts but can't find the solution. I appreciate any help, thanks.
This is my MainWindow XAML:
<mui:ModernWindow x:Class="ModernUINavigationApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
Title="Media Copy Manager" IsTitleVisible="True"
WindowStartupLocation="CenterScreen"
ContentSource="/gui/Pages/PHome.xaml" WindowState="Maximized">
<mui:ModernWindow.MenuLinkGroups>
( ...)
</mui:ModernWindow.MenuLinkGroups>
Here is my PHome.xaml page:
<UserControl x:Class="MCP.gui.Pages.PHome"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d">
<Grid Style="{StaticResource ContentRoot}" Margin="0 0 0 0" Name="_contentRoot">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TabControl Grid.Row="0" Grid.Column="0" Name="_TAB" TabStripPlacement="Left"/>
</Grid>
This is my Home.cs code :
{
public PHome()
{
InitializeComponent();
this.Loaded += ContentLoaded;
}
private void ContentLoaded(object sender, RoutedEventArgs e)
{
Thread t = new Thread(() =>
{
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
(Action)(() =>
{
new LoadingDialog().Show();
}));
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
Task.Run(() =>
{
_TAB.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
(Action)(() =>
{
Populate_Tab(_TAB);
}));
});
}
private async void Populate_Tab(TabControl tabControl)
{
tabControl.Items.Clear();
tabControl.ClipToBounds = true;
List<categoria> ListaCategorias = await DBManager.CategoriasRepo.ListAsync;
foreach (categoria categ in ListaCategorias)
{
TabItem tabitem = new TabItem();
tabitem.Header = categ.categoria1;
Thread.Sleep(1000); //Do some long execution process
tabControl.Items.Add(tabitem);
}
}
}
Here is my LoadingDialog XAML:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d" d:DesignWidth="300"
Title="ModernDialog" Background="{x:Null}" Height="197.368">
<StackPanel>
<TextBlock>LOADING</TextBlock>
<mui:ModernProgressRing IsActive="True" Name="_LoaderGif" Width="100" Height="100" Style="{StaticResource ThreeBounceProgressRingStyle}" Margin="47,43,68,65" />
</StackPanel>
</mui:ModernDialog>
You need to get a better understanding of the methods you're using, specifically the ones you are using to try and move things onto other threads. Before I explain, keep these things in mind:
The main, default thread in a WPF application can be called the UI thread. All the code for the rendering and layout of the application is processed on this thread. All the code you write is also processed on this thread unless you say otherwise.
A thread runs one method at a time, so any any long-running method on the UI thread will prevent it from running the methods necessary to layout and render the application (i.e. it will "freeze").
Changes to UI elements- the visual aspects of the application- must be done from the UI thread.
The thing you don't seem to understand is Dispatcher.Invoke.
Just like Task.Run moves code execution onto a new thread, Dispatcher.Invoke moves code execution back onto the UI thead.
Let's take your method ContentLoaded. In your code, you attempt to use both the Thread and Task classes to move some of your code onto a separate thread. But all you do from those new threads is immediately call Dispatcher.Invoke, which executes code back on the UI thread. This actually makes your app slower, in theory, because it's wasting time making new threads and jumping between them for no reason.
What you need to do is take Populate_Tab and separate it into two (or more) different methods: one that effects the UI and one that doesn't.
Calls like tabControl.Items.Clear and tabitem.Header = categ.categoria1 have to be done on the UI thread because they directly reference UI elements, but your long-running operations, like accessing your database, can and should be moved onto another thread.
The sequence shoud be:
Show the loading icon.
Start a long running operation on another thread. I would recommend using Task with async and await, as that is the current best practice.
Get all the data you need and make any calculations on that other thread.
Return the needed data back to the UI thread (using await) and update the UI as needed.
Remove the loading icon.

Icon not showing when asked to

My app is blocking when i want to navigate to certain view, so i want to show a load icon. The problem is the icon never shows when expected. The view changes and i never see the icon, but if i go back the icon is there.
I tried using an async task to do the navigation, but the navigation doesn't work in a task, i guess.
Any suggestions or ideas?
XAML Code:
<UserControl xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:fa="http://schemas.fontawesome.io/icons/">
<Grid>
<Listbox ItemsSource={Binding ItemsList}>
</Listbox>
<Canvas Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="-550, 180, 0, 0">
<fa:ImageAwesome Visibility="{Binding LoadingIcon, UpdateSourceTrigger=PropertyChanged}"
Icon="Spinner"
Spin="True"
Canvas.Left="56"
Canvas.Top="-17" />
</Canvas>
</Grid>
<UserControl.InputBindings>
<KeyBinding Key="Enter" Command="{Binding NavigateToMainMenuCommand}"/>
</UserControl.InputBindings>
</UserControl>
ViewModel:
public class LoginViewModel: ViewModelBase, INotifyPropertyChanged, INavigationAware
{
public InicioContableViewModel(IRegionManager regionManager,
IEventAggregator eventAggregator)
{
_regionManager = regionManager;
_eventAggregator = eventAggregator;
NavigateToMainMenuCommand = new DelegateCommand(NavigateToMainMenu);
LoadingIcon = Visibility.Hidden;
}
public DelegateCommand NavigateToMainMenuCommand { get; private set; }
private Visibility loadingIcon;
public Visibility LoadingIcon
{
get
{
return loadingIcon;
}
set
{
SetProperty(ref loadingIcon, value, nameof(LoadingIcon));
NotifyPropertyChanged(nameof(LoadingIcon));
}
}
private void NavigateToMainMenu()
{
LoadingIcon = Visibility.Visible;
string mainUri = nameof(SomeRoute.MainMenu);
_regionManager.RequestNavigate(Regions.MainRegion, mainUri);
}
}
In your Icon's properties, try to set Build Action to Resource.
WPF uses the UI thread to do things like handle mouse movement, change icons when you tell it to and to switch out one view for another. All one thread.
When that thread is busy doing something then nothing is likely to change in your UI until it's finished.
Hence, if you tell it to make something visible AND navigate, you can well find all that happens visually is the navigation. Because it has no time to show your icon.
Try making your method async and giving it a little time:
private async void NavigateToMainMenu()
{
LoadingIcon = Visibility.Visible;
await Task.Delay(60);
string mainUri = nameof(SomeRoute.MainMenu);
_regionManager.RequestNavigate(Regions.MainRegion, mainUri);
}
That task.delay should give it enough time to redraw a bit of ui.
If you need a loading icon and the thing you are navigating to is blocking then I suspect you have other problems. It's likely that whatever this new view is doing to initialise should be asynchronous. Maybe with data access etc on background threads.
You may most likely make use of the functions:- InvalidateVisual() or UpdateLayout(). Those will force to redraw and may resolve your icon visibility issues.
My app is blocking when i want to navigate to certain view, so i want to show a load icon.
If the app is blocking, you cannot show the icon, because, you know, the app is blocked. You have to remove the blocking, that is, make navigation itself quick, and do whatever initialization asynchronously. The view you're navigating to may need stuff from a database or connection to a usb device, but it won't need it immediately. You can still query the database or whatever in the background after the view is shown.
show wait indicator-> navigate -> creates view model -> starts initializion task -> initialization completes -> hide wait indicator

Changing Text On Button Click, Then Again After Runtime

I am writing a program that corrects values in text files. In my xaml I have a textbox that is supposed to report the status/progress. "waiting"=before program starts, "processing"= program is processing files, "done"= files have been processed.
Here is my xaml code for the text field and the RUN button:
<TextBox x:Name="statusBox" HorizontalAlignment="Left" Height="23" Margin="130,332,0,0" TextWrapping="Wrap" Text="waiting" VerticalAlignment="Top" Width="120" FontSize="14" TextAlignment="Center" IsReadOnlyCaretVisible="True" >
<Button Content="Run"
Name="Run"
HorizontalAlignment="Left"
Margin="562,27,0,0"
VerticalAlignment="Top"
Width="53"
Click="Run_Click"
RenderTransformOrigin="-0.305,0.487" Height="19">
<Button.BindingGroup>
<BindingGroup Name="btnOpen"/>
</Button.BindingGroup>
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-0.848"/>
<TranslateTransform/>
</TransformGroup>
</Button.RenderTransform>
</Button>
Here is the problem I am having:
I have tried this many different ways. What I have below is the most recent attempt.
//In XAML, the status defaults to "waiting".
public class Operate
{
public void operations()
{
statusBox.Text = "processing";
... Perform Text Operations
status.Text = "finished";
}
}
When I run the program, I get see "waiting" in the status text box. However, when I click Run, the program begins but the text still says "waiting". Then, when the program has finished running, the text changes to "finished" without changing to "processing" in between.
Any help is greatly appreciated. I have programmed many console applications, but am relatively new to wpf.
Thanks.
When you start running, your UI won't be updated until it ends running the current thread.
After completing the task, your status will be updated very fast from processing to finished, so you won't see the change.
I recommend using a background worker to perform longer operations and still have the UI operational.
See this howto: Use a background worker
You may want to take a look into using a BackgroundWorker. It appears the UI is being blocked while processing your requests.
MSDN BackgroundWorker Class
As others have said the problem is that all your code is executing on the same thread. Your operations are running on the UI thread, which means it cannot respond to things like changes as you are trying to do.
The way around this is move your work onto a new thread.
(This assumes a newer version of the .NET framework that supports async/await syntax)
public class Operate
{
public async void operations()
{
statusBox.Text = "processing";
await Task.Run(()=>{
//do work here
});
status.Text = "finished";
}
}
Keep in mind that since that Task.Run command executes on a different thread it cannot access the UI. If you need it to have some information from the UI, you can pass it in the Task.Run call like so:
await Task.Run(mydata=>{
//do stuff, with mydata variable
});
If you need to return something to display on the UI then you can do this:
var myReturnValue = await Task.Run(mydata=>{
//do work
return myNewReturnData;
});
//can do something with your return data here.
If you need the ability for the user to click a different button to cancel the transaction look up the CancellationToken class.

How to update UI before (and after) long running UI process in WPF?

I apologize if this is a duplicate, but I have not been able to find a question with a similar situation. If this is a duplicate please provide a link.
I would like to show a "Loading..." overlay in my WPF application, when I am dynamically creating a lot of tabs. The overlay visibility is bound to a property called "ShowIsLoadingOverlay". However, the overlay is never shown.
Due to the fact that the tabs are visual elements I can't move the creation into a BackgroundWorker.
I have created a small prototype trying to explain the situation. This is the xaml:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label HorizontalAlignment="Center" VerticalAlignment="Center"
Visibility="{Binding ShowIsLoadingOverlay, Converter={StaticResource BooleanToVisibilityConverter}}"
Content="Loading..." />
<Button Grid.Row="1" Content="Load" Click="Button_Click" />
</Grid>
</Window>
And this is the code behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool m_ShowIsLoadingOverlay;
public bool ShowIsLoadingOverlay
{
get
{
return m_ShowIsLoadingOverlay;
}
set
{
if ( m_ShowIsLoadingOverlay == value )
{
return;
}
m_ShowIsLoadingOverlay = value;
NotifyPropertyChanged( "ShowIsLoadingOverlay" );
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click( object sender, RoutedEventArgs e )
{
ShowIsLoadingOverlay = true;
CreateTabs();
ShowIsLoadingOverlay = false;
}
private void CreateTabs()
{
// Simulate long running process to create tabs
Thread.Sleep( 3000 );
}
// Implementation of INotifyPropertyChanged has been left out.
}
The problem is that the overlay is never shown. I know that it has something to do with the UI not updated correctly before and after the ShowIsLoadingOverlay property has changed. And I believe it also has something to do with the lack of using the dispatcher.
I have tried many, many combinations of Dispatcher.Invoke, Dispatcher.BeginInvoke surrounding when changing the property and/or surrounding the CreateTabs call. And I have tried changing the DispatcherPriority to "force" the overlay to show before starting to create the tabs. But I just can't make it work...
Could you please tell me how to accomplish this task? And more importantly; provide an explanation, because I do not get this.
In advance,
thank you.
Best regards,
Casper Korshøj
You cannot manipulate UI controls in a background thread. If you are using the main UI thread to create your TabItems, then you also cannot have a 'Busy' or 'Loading' indicator... this will only work if you are using a alternative thread for your long running process. This is because your 'Busy' indicator will only become updated once the long running process has completed if it runs on the same UI thread.

WPF BusyIndicator on a background thread

I'm using the wpf toolkit busy indicator which provides an overlay on top of my UI while a background operation is taking place. There's a progress bar in the control which is set to indeterminate which is fine while my background task is going on. Once the background task is complete, the UI needs to update which can take 1-2 seconds. This of course causes the progress bar to freeze which looks ugly.
My question is, how can I spin up the busy indicator on a background thread so that the progress bar carries on moving all the way up until the UI becomes responsive? I'm open to other solutions as well as long as the progress bar doesn't freeze.
Here's some sample code:
<xctk:BusyIndicator IsBusy="{Binding IsBusy}" Style="{StaticResource BusyIndicatorStyle}">
<DockPanel Margin="3">
<TextBlock DockPanel.Dock="Top" Style="{StaticResource WorkspaceHeaderStyle}" Text="User Management"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ToolBar Grid.Row="0">
<Button Content="Save changes" Command="{Binding SaveChangesCommand}"/>
</ToolBar>
<TabControl Grid.Row="1">
<TabItem Header="Users" DataContext="{Binding UsersViewModel}">
<users:UsersView />
</TabItem>
<TabItem Header="Roles" DataContext="{Binding RolesViewModel}">
<roles:RolesView />
</TabItem>
</TabControl>
</Grid>
</DockPanel>
</xctk:BusyIndicator>
private void LoadDays()
{
ProgressIsBusy = true;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var loadDaysTask = GetLoadDaysTask(uiScheduler);
loadDaysTask.ContinueWith(antecedent =>
{
RaisePropertyChanged(() => ForecastViewModel);
RaisePropertyChanged(() => AverageHandleTimeViewModel);
RaisePropertyChanged(() => GeneralOptionsViewModel);
RaisePropertyChanged(() => ScheduledHoursViewModel);
IsUserEditing = true;
ProgressIsBusy = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
loadDaysTask.Start();
}
No, It is not possible to show the BusyIndicator animations when UI is updating / using Background thread or something.
Refer the related post here.
busy indicator during long wpf interface drawing operation
You can show some static image to indicate the busy sign until your UI updation complete.
I don't believe that you can do that. You can't access UI controls from a background thread. Also, you may find that it is the UI thread that is busy rendering or notifying and making the progress bar freeze.
I have a similar setup in a large scale WPF application and it suffers from the same problem. The busy indicator displays fine while data is being fetched from the database, but then when the application starts to render the data in the UI, the busy indicator freezes.
I even tried using an animated Gif to get around this issue, but of course they don't animate (by themselves) in WPF applications. Having written the code to animate the frames in the Gif, I was very disappointed to find that it also suffered from the same problem.
Good luck all the same.

Categories