Loading UserControl asynchronously - wpf

I have a WPF application which loads a usercontrol (grid) in a content (MetroContentControl) area when an option is clicked:
My MetroContentControl is surrounded by a progress bar so the idea is while the usercontrol is loaded, the IsBusy Progress bar loads:
private void ViewQuotes_OnClick(object sender, RoutedEventArgs e)
{
RadBusyIndicator.IsBusy = true;
Dispatcher.Invoke(new Action(() =>
{
// load datagrid control
main.Content = new Quotes();
}));
//RadBusyIndicator.IsBusy = false;
}
However it the progress bar never displays till after the usercontrol (grid) is loaded, so my assumption of how this should work is wrong.
I know I have previously got the progress bar working as expected when loading data asyncrnously to already initliased datagrid but doesn't seem to be working in this instance
Any ideas what the best approach to this is?

Simply saying user control is extremely hard to render on a different thread.
First, you need to be aware that the animation of progress bar also occupies UI thread that means if your time consuming job is on UI thread, even your progress bar is showing, it does not get animated. Therefore putting a progress bar on loading user control does not work as what you expected.
Second, you put two UI operations in a event handler, WPF can only handle these UI operations after it finishes the current operation. Since you call loading user control using Dispatcher.Invoke with default priority at Normal,9 (I think), and direct call UI is at around Input,5, you see progress bar shows after user control is loaded.
Finally you need a clear separation of sync job and async job. In your case, all UI related are sync jobs and if you have any data related time consuming job such as getting quotes, you can put them on separate thread, showing progress bar when executing the job then load user control after job is finished.

Related

Show progressBar until window.show()

I have problem with showing progress bar while window is loading.
Actually i have window with a lot of items in datagrid.
When im calling window.Show(), it tooks very long time till its open, so im wondering, if its possible to call something like:
ShowProgressBar spb = new ShowProgressBar();
spb.Topmost = true;
spb.Owner = this.Owner;
spb.Show();
while(mainWin.isLoaded)
spb.updatePB(); // this method updating progressbar.value.
mainWindow.Show();
But it doesnt show progresbarr at all, while loading, only show when window is full-loaded.
Is there any helpful code? ;)
Your problem is that both of them are on the UI thread, so you won't be able to update the UI for the progress bar until you finish loading the MainWindow. The solution is to use another thread to update the ProgressBar's UI....
Something like this should work:
Dispatcher progressDisptacher;
var uiThread = new Thread(() =>
{
ShowProgressBar spb = new ShowProgressBar();
spb.Topmost = true;
spb.Show();
progressDisptacher = spb.Dispatcher;
// allowing the main UI thread to proceed
System.Windows.Threading.Dispatcher.Run();
});
uiThread.SetApartmentState(ApartmentState.STA);
uiThread.IsBackground = true;
uiThread.Start();
mainWindow.Show();
progressDisptacher.BeginInvokeShutdown(DispatcherPriority.Send);
As you can see, after the mainWindow loads, you can kill the progress bar thread using:
progressDisptacher.BeginInvokeShutdown(DispatcherPriority.Send);
I assume that you want to update the progress bar when your main window ISN'T loaded. So firstly you should want to invert the while loop.
However its a bit more subtle that single problem.
Welcome to the extremely painful work of STA threads. A quick rule of thumb is that anything that has to do with the what you see on the screen must be done with the main thread. And in fact a lot of what happens is when you aren't using the main thread, .net uses it to redraw anything that has changed (like the Progress bar).
To be able to show the progress bar you are going to have to return control of the UI thread back to what is called the message loop. The easiest way to do so is to return the method call. But before that you will want to setup a timer to periodically check the progress.
Your current code would just spend all day checking if the mainWindow is loaded, and not actually loading the mainWindow.
However the actual loading of the mainWindow is likely going to again need to use the main thread to construct.
Soooo...finally you should IsAsync out the binding that loads the items into the datagrid.

Image not being rendered as soon as I add it to a page

I am a newbie in Windows phone app development. On one of my pages I have a button that does some substantial amount of work. So when I press the button the screen freezes and then resumes when all the work is done. Now I wanted to show some waiting image or page to be shown during this while.
So I created and destroyed an image on top on the page at the start and end of button click action respectively but it still freezes and the screen renders only after all the work is done making it indifferent. I am using Silverlite C#.
You'll want to push the heavy work onto a background thread. What's currently happening is that the work is being done on the UI thread, so it 'freezes' since the UI thread is waiting for the work to be done. There are various ways you can push the work to a background thread. One way would be to use a BackgroundWorker.
A simple example, which doesn't include things like reporting when the work is done, would look like this
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(BW_DoWork);
bw.RunWorkerAsync();
private void BW_DoWork(object sender, DoWorkEventArgs e)
{
//Your heavy work code
}
Important thing to remember when you work on a background thread, is that if you want to change anything on the UI, you need to marshal the data to the UI thread and update it there. You can do that like this:
Deployment.Current.Dispatcher.BeginInvoke(() => {
myTextBlockExample.Text = "Changing the UI";
}
If you tried to change the TextBlock without the Dispatcher.BeginInvoke method, you'll get a Invalid Cross-Thread Access error. If you see that error in WP development, it's likely that you're trying to update a UI element from a non-UI thread.
You can put your code that displays the loading image before calling bw.RunWorkerAsync.

How to show a IsBusy flag when I have a collection of UI intensiv tasks each on its background thread?

I have a DataGrid with a Column datatemplated with a RichTExtBox.
If I bind 10 objects with RTF data that results in 10 RichTextBoxes.
For 1 DINA4 page of RTF a RTBox needs 200 ms to do textrange.Load(data).
Now I have a UI freeze of 2000 ms when I switch my Calendar views. Thats ok If I could show an
IsBusy Adorner, but I have a collection of RTBoxes how can I do this?
I could run each UI task on a background thread and use the Dispatcher to update the UI. But
still then I need a loading adorner/progress bar. All the data comes very fast in 100-200 ms
from my database in async manner. But how would you solve that special Loading Adorner problem?
Why can't you run all of the background work in a single BackgroundWorker? You could set the loading adorner/progress bar immediately after starting the BackgroundWorker and you could remove the loading adorner/progress bar in the RunWorkerCompleted method. The ProgressChanged method can be used to keep the loading adorner/progress bar updated.

WPF Multithreading: Using Dispatcher but the UI still hangs?

I am having a bit of a problem with my UI hanging even though I am using a Dispatcher, and before I get any further I'm wondering if it's the way I'm handling the data retrieval.
Right now I have my main window creating a View and ViewModel. Then, inside of a new Thread (using a Dispatcher) it sets the View.DataContext = ViewModel. A very large ObservableCollection is lazily created when the binding kicks in which is causing the slowdown. However, it seems that some of the other UI items that should be showing up before that slowdow don't actually show up.
private void ButtonClick(Object sender, RoutedEventArgs e)
{
MyView view = new MyView();
MyViewModel vm = new MyViewModel();
TabItem tabItem = new TabItem();
tabItem.Header = "MyView";
tabItem.Content = view;
MyTabCollection.Items.Add(tabItem);
Window working = new Working();
working.Show();
ThreadStart thread = delegate()
{
DispatcherOperation operation = Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(delegate()
{
view.DataContext = vm;
((FrameworkElement)view.Parent).Focus();
working.Close();
}
)
);
};
Thread theThread = new Thread(thread);
theThread.Start();
}
This basically says it's supposed to create a view and a viewmodel, then add the view to the tab collection I have (which means it should show the new tab at the least). And, it should also show a "Working..." window. After that, a separate thread is supposed to link the ViewModel to the view, focus on that tab and close the working window. The problem is that the first portion doesn't show until everything is done; The tab is not displayed and the working window is not shown until after the new Thread actually finishes (which causes the Working window to show/close right away). I'm guessing it might have to do with the way I retrieve the data, but I'm not sure. Here is the way it does it:
Create View
Create ViewModel
Create TabItem with Content set to the View and add the TabItem to the TabCollection.
Create/Show the "Working..." window
Dispatcher: Set the View.DataContext = ViewModel. This event sets off the DataBindings, which in turn grab the ObservableCollection. Since the OC is created Lazily it is now being created (this is the bottleneck). <-- Is this messing up my separate thread/dispatcher?
Dispatcher: Set Focus to the tab
Close the "Working..." window
All your extra thread is doing is marshalling another call back to the dispatcher thread. Presumably you actually want to do work on the extra thread, or there's no point in creating it.
Ideally your extra thread should be fetching all the data appropriately, leaving you only to actually connect it all up in the dispatcher thread. The important thing is to decide which work you need to do on the UI thread and which work you need to do on the background thread.
Obviously your analysis of the problem is correct. Your view model is lazily loading data when it is needed, and this is not happening until the Dispatcher callback, at which point you are back on the UI thread again and everything is locked up.
In my opinion, the solution is to do the threading in the data access layer:
For collections: You can define special collections that return only items that have already been loaded from the upstream data source, then trigger loading of additional items on a separate thread when someone subscribes to INotifyCollectionChanged. When the additional items arreive, fire INotifyCollectionChanged events. When INotifyCollectionChanged is unsubscribed, cancel any pending load.
For totals and the like: Same idea. As data comes in the total increases and events occur (automatically for DependencyProperty or using INotifyPropertyChanged).
In addition, the data layer should have a parallel property to each collection, sum, or other delay-loaded value indicating whether it is fully loaded or not, allowing the UI to gray out sections that aren't fully loaded. It is also convenient to have an overall "loading" flag somewhere that can be used to gray out UI sections when anything at all is loading (easier to write the UI this way).
Note that sometimes an operation must block until the actual data has been retrieved. I think the easiest thing in this case is to provide methods in the data layer to force data to be loaded synchronously.
Your DispatcherPriority is set to Normal - try setting it to Background as this may improve the rendering

How can I get notified that binding has finished?

I have an application to which I want to add a progress bar. It works in most cases. In one case the time is actually taken because of binding and layout work which is asynchronous, so my progress bar is hidden before the work is actually done. Its rather large list of items shown within a scroll view. I can't use virtualizing because I need it to scroll smoothly as it is a touch screen application. So needless to say, it takes forever to bind and layout this list. Is there any way I can get notified that the binding and layout has finished?
I'm using Visual Studio 2010, but because of constraints I'm forced to use .Net 3.5.
If you want to execute code when binding and rendering are completed, use this code.
Dispatcher.Invoke(new Action(() =>
{
// hide progress bar
}), DispatcherPriority.Input);

Resources