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.
Related
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.
I am reading a large Txt document into a WPF app for some serious swap/replacemnt operations. The files are actually 3D STP models so they are fairly large, but im working with them as raw text for this project. The files are read into List to avoid having to open them multiple times, and to make comparisons easier.
Anyway, I'm trying to get the listbox to scroll dynamically as lines are added to it, ala a console window so the user can see that something is happening since calculations can take a bit of time depending on filesize. I also added a progress bar to count away as the total line number is read through.
Neither my progress bar, nor ListBox seem to update as work progresses though. The final output simply lands in the listbox completed, and the progress bar goes from 0-max at the same time.
This is the gist of what I am doing, which is fairly simple:
foreach (string Line in OriginalSTPFile.Lines)
{
string NewLine = EvaluateString(Line); //string the modified to whatever here
pBar.Value++; //increment progressbar
OutputWindow.Items.Add(NewLine); //add line to the ListBox
}
I just want the listbox an progress bar to update in realtime as progress changes. I tried using:
Dispatcher.BeginInvoke(new Action(() => OutputWindow.Items.Add(NewLine));
But got the same results. Do I need a more elaborate method of multithreading here? I assumed the first method would've worked since I wasn't generating any cross-thread exceptions either.
This article will give you all the code that you need.
Backgroundworker with Progressbar
It describes very well what to do and which elements to use.
Dispatcher.BeginInvoke signals to invoke a method on the Dispatcher's thread. However, that's essentially like a post message, as it won't occur while the main thread is locked up doing work. And until the main thread is available again, it won't update the UI visually, even if you change values.
You'll need to perform the work in a background thread.
But to update the UI, you'll have to do so on the UI's main thread. This is a limitation of WPF. This is why you were directed to Dispatcher. I'm guessing someone assumed your work was already on a background thread.
To create a thread, you use Thread.Start passing it a delegate to perform. If you use a anonymous delegate or a lambda, you can refer to variables on the stack, but be aware that they will persist until the delegate quits. This is why you cannot use reference variables in a anonymous delegate.
Backgroundworker is a special type of background thread. It automates some of the expectations of a worker thread (notifying of completion, and updating on progress), but you can achieve the same results without it.
To update the UI during the thread's process, you'll need for that thread to be able to access the main UI thread. You can do that by passing it a dispatcher, referring to a dispatcher from outside the anonymous delegate, or by an object that contains a dispatcher. You can always read values from any object on any thread, so accessing the dispatcher by UIElement on another thread is fine.
To update the UI, you'll call Dispatcher.BeginInvoke with a delegate that entails the work to perform.
Here's psuedo-code of the overall scheme
class TestProgress
{
ProgressBar _ProgressBar;
void DoWork()
{
var worker = (Action)(() =>
{
int progress = 0;
// do stuff, delta is change in progress
progress += delta;
_ProgressBar.Dispatcher.BeginInvoke((Action)(() =>
{
_ProgressBar.Value = progress;
}));
});
Thread.Start(worker);
}
}
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.
When my MainPage loads in my Windows Phone 7 application, it triggers a popup splash screen that includes a progress bar and a graphic in a usercontrol. After displaying the spash screen I do some work in a BackgroundWorker thread to load some resources while the spash displays. When the loading is done I dismiss the popup. This technique is well documented on WindowsPhoneGeek.
I noticed today that while this is working flawlessly when running in debug under Visual Studio, if I run the same build directly without the debugger connected, the splash screen animation and progress bar never appear and the DoWork() thread takes at least twice as long to execute the same operations. I can see that the MainPage constructor gets called as well as OnNavigatedTo, but the popup still does't display and the default jpg splash image remains on the screen, until the worker thread completes. Then the popup displays for < 1 second and the mainpage displays. Again, this all works flawlessly when debugging through VS 2010.
This is all in the Emulator, I don't have a device yet. I just noticed this today, and coincidently(?) I just updated the environment to 7.1 last night.
// Constructor
public MainPage()
{
InitializeComponent();
IDictionary<string, object> state = Microsoft.Phone.Shell.PhoneApplicationService.Current.State;
if (!state.ContainsKey(STATE_WAS_LOADED))
{
state[STATE_WAS_LOADED] = "LOADED";
this.LayoutRoot.Visibility = System.Windows.Visibility.Collapsed;
_popup = new Popup();
_popup.Child = new NPGSplash();
System.Diagnostics.Debug.WriteLine("{0}: Displaying Splash Popup", DateTime.Now.ToString("ss.ffff"));
_popup.IsOpen = true;
// Asynchronously load the biggest dataset
StartLoadingData();
}
}
private void StartLoadingData()
{
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
_worker.RunWorkerAsync();
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Dispatcher.BeginInvoke(() =>
{
System.Diagnostics.Debug.WriteLine("{0}: Splash RunWorkerCompleted", DateTime.Now.ToString("ss.ffff"));
this.LayoutRoot.Visibility = System.Windows.Visibility.Visible;
this._popup.IsOpen = false;
});
}
EDIT:
I ended up buying a device on ebay this week to ensure that I don't release an app where I can't confirm that it works properly. I can confirm that whatever the problem is, it does NOT occur when running the application on the device. Good news. Still, I can't explain what appears to be an approximately 3-5 second pause in my application after the consrtuctor is called. I even changed my logic in the constructor to set a System.Windows.Threading.DispatcherTimer to fire in 100ms to kick off my logic. When this code executes, the constructor completes, but the timer doesn't tick for 3-5 seconds. Very odd, and only in the simulator when not attached to the debugger.
The problem here would appear to be that you are never attaching the Popup control to the visual tree. To be honest, I've got no idea why this would work with the debugger attached either, but I'm going off what I can interpret from your code.
What I think you need to do is to add the Popup control as an element in the XAML for MainPage so that it is attached to the visual tree.
One other thing I'd say is that it's perhaps worth moving all the data loading code out of the constructor and into either an override for OnNavigatedTo or a handler for the Loaded event. Generally speaking, it's best to keep constructors and short and simple as possible (and yes I appreciate that you're using a background worker to load the data).
I have a wpf application that takes ~30 seconds to create a map/graphic. I have read there is no easy way to tie into the UI rendering thread to get a progress update. So I was going to use a counter on a value converter which colors my map but that is also on the UI Thread, so my question is has anyone found any slick methods of working with the rendering thread yet?
Thanks.
You could create your map/graphic in a BackgroundWorker which allows you to call ReportProgress in your function, where you can set your percentage of completion and raise the ProgressChanged event to update your UI.
When you say UI rendering thread, you mean that hidden rendering thread from WPF internals or UI thread?
In any case, having a separate thread that builds your map and notifies UI about progress doesn't help you?
im not sure if this is what you are looking for.
I use something similar to the code below to load in around 300 images( about 200 mb ) and have no UI slow down at all. (the user can see each image being loaded in, I just keep an empty placeholder image up till the final image is loaded)
The images are loaded in a background thread, and then the function is called to actually put them into the WPF scene.
here is a simple example using a textbox. You can call this function from any thread and it will work out if it needs to change the to the GUI thread. (for my project of course i am doing it with bitmaps, not a textbox ).
delegate void UpdateUIThreadDelegate(String str);
public void DisplayString(String strMessage)
{
if (this.InvokeRequired)
{
UpdateUIThreadDelegate updateDelegate = DisplayString;
this.BeginInvoke(updateDelegate, strMessage);
return;
}
myTextBox.Text = strMessage;
}
Cheers
Anton
If you use binding to tie your UI with a datasource which can take long time to return, you can set 'IsAsync=True' on your binding so that the binding become asynchronous.
If you want to display some other datas (even an animation I guess) during the time your datasource is loading, you can use a PriorityBinding
HTH
Riana