WPF loading indicator when Entity Framework loads data [duplicate] - wpf

This question already has an answer here:
Displaying a busy indicator when Entity Framework is loading data
(1 answer)
Closed 9 years ago.
When loading data from database with Entity Framework takes a long time I would like to show "loading" indicator on UI (WPF). For the indicator itself, I am using WPF Loading Wait Adorner as shown in the article.
The indicator works fine but is not showing when Entity Framework loads data. In that case the indicator won't show at all on UI.
I run this:
'show Adorner (loading indicator)
LoadingAdorner.IsAdornerVisible = Not LoadingAdorner.IsAdornerVisible
'read data from database with Entity Framework
Persons = _context.persons
'hide Adorner (loading indicator) after loading data is completed
LoadingAdorner.IsAdornerVisible = Not LoadingAdorner.IsAdornerVisible
and
<ac:AdornedControl Name="LoadingAdorner">
<ac:AdornedControl.AdornerContent>
<local:LoadingWait></local:LoadingWait>
</ac:AdornedControl.AdornerContent>
<ListBox>
...code not shown
</ListBox>
</ac:AdornedControl>
Only after the data is loaded, the indicator becomes visible.
What am I missing and how to show the indicator WHILE the data is loaded?

Problem is that you're running your EF call in Main thread. This blocks UI from being updated until you'll receive all data from the DB.
To fix this just add BackgroundWorker or async methods:
var worker = new BackgroundWorker();
worker.DoWork += (s, e) => {
this.IsLoading = true;
this.Persons = _context.persons;
};
worker.RunWorkerCompleted += (s, e) => {
this.IsLoading = false;
};
Important: Keep in mind cross-thread access (DoWork performed in background thread, Completed - UI thread )
And at the end to start/trigger DoWork you will need to execute .RunWorkerAsync() on your worker.

Related

Creating a new UI thread while calling methods on the original thread

I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context

Dispatcher.BeginInvoke Skips loading UI controls in Prism

I have a WPF application developed with Prism. I am facing a typical scenario for a code change I implemented for performance improvement.
I have a UI Grid which shows a list of entities. I select some 10 entities in the Grid and try to open it, which inturn should open a new window and load the corresponding 10 entities by resolving corresponding containers and their dependencies.
The sample code is below,,, I have commented the operations that are occuring..
private void OpenSelectedEntities(List<Entity> entities)
{
foreach (Entity entity in entities)
{
if (Application.Current != null)
{
//I implemented opening entity in async so that 10 entities will be opened without stalling the UI.
Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() =>
{
OpenEntity(entity, false);
}));
}
}
}
public void OpenEntity(Entity entity, boolean val)
{
//Creating Container to load the controls.
//Adding Container to Region
//Loading the other UI related and triggering operation threads
}
The problem I am facing is, When I select some entities and verify it in my local development environment, I am able to see all the 10 entities opened and working well. However, When I deploy the code in Server and test it there,, Only two entities were opened, the first entity and last entity in the selected entities.. I am completely clueless what is causing this problem. I have cross checked twice, I am getting this problem while calling OpenEntity as Asynchronous.
Has anyone encountered situation like this using WPF and Prism.
It is an "Access to Modified Closure" problem. Look into that for more details.
Here's a blog post that discusses it. http://www.jarloo.com/access-to-modified-closure/
As for the fix, create a temp variable.
private void OpenSelectedEntities(List<Entity> entities)
{
foreach (Entity entity in entities)
{
if (Application.Current != null)
{
Entity tempEntity = entity;
Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() =>
{
OpenEntity(tempEntity , false);
}));
}
}
}

Which strategy to employ for long to load pages?

Trying to address the following issue :
To provide a progress indicator that will be shown until the navigation target finishes loading. The target being navigated to can take up to 30 seconds for loading as there are images being fetched from different sources on the Internet.
Problem lies on handling such task using events of NavigationService or Page as they are always raised before the Page has loaded its content which is done inside the Loaded event. The loading process is asynchronous for not blocking UI and as such, cannot be moved to the constructor as it cannot be marked as async.
Is there an efficient pattern for addressing such problem ?
One option here is to have the constructor create a method that returns a Task<T>, and then store this in a class member.
Your Loaded event can then use await on the created task to extract out the data and display it asynchronously.
This will look something like:
Task<YourData> initialDataLoadTask;
// In your constructor:
YourPage()
{
this.InitializeComponent();
// Use async method here to fetch data, but do NOT use await
this.initialDataLoadTask = FetchDataAsync();
this.Loaded += this.OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
// Use await to pull out the data asynchronously now...
var data = await this.initialDataLoadTask;
// Display data as needed...
}
This allows the entire chain to be asynchronous, with the constructor still initializing the fetch of the data (so it comes in as fast as possible).

Getting Progress Updates using HttpWebRequest and TPL (Tasks)

I would like to track the progress of a download taking place on a separate thread. I know that System.Net.WebClient has a DownloadStringAsync method, but it doesn't work directly with the new TPL types (TaskFactory, Task, etc.).
Can progress be tracked using the HttpRequest and HttpResponse classes?
What's the best class for tracking progress? The less overhead the better.
Are there times when the size of the response is unknown, aka, progress can't be tracked?
What's the best way to synchronize with the UI thread whenever progress is made?
Most examples show Tasks updating the UI only after the entire task is complete. These examples use continuations taking a UI synchronization context that avoids needing to work with a Dispatcher directly.
The idea is to show a grid view (in WPF) with all the downloads with progress bars. I am going to adding new rows and updating progress bars all the time. I'm trying to avoid turning this code into a mess.
DownloadStringAsync and the other event methods work very well with TPL in .NET 4.0 (check for EAP and TPL). In general, TPL does support event async programming through the TaskCompletionSource. The Begin/EndXXX model (APM) is supported through the Task.FromAsync method. You can find a detailed description TPL and Traditional .NET Asynchronous Programming.
The ParallelExtensionExtras library has a set of WebClient extensions methods like DownloadStringTask that return a task which completes when the appropriate event is fired.
The following code will create a Task that will complete when download finishes:
public Task<string> DownloadStringTask(WebClient client,Uri uri)
{
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (o, a) => tcs.SetResult(a.Result);
client.DownloadStringAsync(uri);
return tcs.Task;
}
As for updating the UI, you can easily use the DownloadProgressChanged event to provide feedback,eg:
using (var client = new WebClient())
{
client.DownloadProgressChanged += (o, a) => Console.WriteLine("{0}",a.ProgressPercentage);
var task = DownloadStringTask(client,new Uri("http://www.stackoverflow.com"));
var write=task.ContinueWith(t => Console.WriteLine("Got {0} chars", t.Result.Length));
write.Wait();
Console.ReadKey();
}
If you use data binding to provide the progress values to your progress bars, you can just update the progress value properties. If you update the progress bars directly (not a good idea), you will have to marshal the call to the UI thread using the progress bar's dispatcher, eg. like this
void UpdateProgress(int percent)
{
if (progressBar1.CheckAccess())
progressBar1.Value = percent;
else
{
progressBar1.Dispatcher.Invoke(new Action(()=>UpdateProgress(percent)));
}
}
....
client.DownloadProgressChanged += (o, a) => UpdateProgress(a.ProgressPercentage);

How can I force my busy indicator to display? (WPF)

I've created a busy indicator - basically an animation of a logo spinning. I've added it to a login window and bound the Visibility property to my viewmodel's BusyIndicatorVisibility property.
When I click login, I want the spinner to appear whilst the login happens (it calls a web service to determine whether the login credentials are correct). However, when I set the visibility to visible, then continue with the login, the spinner doesn't appear until the login is complete. In Winforms old fashioned coding I would have added an Application.DoEvents. How can I make the spinner appear in WPF in an MVVM application?
The code is:
private bool Login()
{
BusyIndicatorVisibility = Visibility.Visible;
var result = false;
var status = GetConnectionGenerator().Connect(_model);
if (status == ConnectionStatus.Successful)
{
result = true;
}
else if (status == ConnectionStatus.LoginFailure)
{
ShowError("Login Failed");
Password = "";
}
else
{
ShowError("Unknown User");
}
BusyIndicatorVisibility = Visibility.Collapsed;
return result;
}
You have to make your login async. You can use the BackgroundWorker to do this. Something like:
BusyIndicatorVisibility = Visibility.Visible;
// Disable here also your UI to not allow the user to do things that are not allowed during login-validation
BackgroundWorker bgWorker = new BackgroundWorker() ;
bgWorker.DoWork += (s, e) => {
e.Result=Login(); // Do the login. As an example, I return the login-validation-result over e.Result.
};
bgWorker.RunWorkerCompleted += (s, e) => {
BusyIndicatorVisibility = Visibility.Collapsed;
// Enable here the UI
// You can get the login-result via the e.Result. Make sure to check also the e.Error for errors that happended during the login-operation
};
bgWorker.RunWorkerAsync();
Only for completness: There is the possibility to give the UI the time to refresh before the login takes place. This is done over the dispatcher. However this is a hack and IMO never should be used. But if you're interested in this, you can search StackOverflow for wpf doevents.
You can try to run busy indicar in a separate thread as this article explains: Creating a Busy Indicator in a separate thread in WPF
Or try running the new BusyIndicator from the Extended WPF Toolkit
But I'm pretty sure that you will be out of luck if you don't place the logic in the background thread.
Does your login code run on the UI thread? That might block databinding updates.

Resources