Show window with progressbar while doing work - wpf

Because I have a tool that needs to do a lot of work at one point, I want to show a window with some text and a progressbar while doing the work.
My problem is, that because of the huge load of the work, the window never gets drawn or updated.
I know that I usually should use an extra thread for the work, but I have to use 2 collections and the database service from the current thread.
The current code is something like
StatusWindow SW = new StatusWindow();
StatusViewModel SVM = new StatusViewModel();
SVM.MaxNum = BigNumber;
SW.Show();
for (int i=0; i<BigNumber; i++)
{
List<AType> ItemsToCreate = Func1();
List<AType> ItemsToDelete = Func2();
foreach (AType cItem in ItemsToCreate)
DB.CreateItem(cItem);
foreach (AType cItem in ItemsToDelete)
DB:DeleteItem(cItem);
SVM.CurrentNum = i;
}
SW.Close();
I also read about the Dispatcher, but i don't think it is very usable in this scenario, since there would be too much access to the main thread.
Any suggestions?
Thanks

I recommend reading the article (Build More Responsive Apps With The Dispatcher) from MSDN magazine that describes how WPF works with the Dispatcher when using BackgroundWorker.

You can pass the two collections and whatever else you'd like to worker threads as long as the objects you are accessing can be accessed from different threads.
int[] arrayofInt = new int[10]
ThreadPool.QueueUserWorkItem(s =>
{
//access the array in the worker thread
arrayofInt[0] = 10;
});
When finished with the work, you need to send a message to the dialog that it should be closed now. Do that by firing an event in your thread that is handled by your dialog.

Related

Continual (rapid) update of WPF Image

There is a website that contains a single image from a webcam. Each time the site is hit, the most current image of the webcam is displayed. I want to make a real time video by hitting the site continuously.
I have searched and tried several things but cannot get it to refresh at a reasonable rate.
public MainWindow()
{
InitializeComponent();
this.picUri = "http://someurl";
this.thWatchVideo = new Thread(new ThreadStart(Watch));
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.None;
_image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri(this.picUri);
_image.EndInit();
this.imgVideo.Source = _image;
this.thWatchVideo.Start();
}
public void Watch()
{
while(true)
{
UpdateImage();
}
}
public void UpdateImage()
{
if (this.imgVideo.Dispatcher.CheckAccess())
{
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.None;
_image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri(this.picUri);
_image.EndInit();
this.imgVideo.Source = _image;
}
else
{
UpdateImageCallback del = new UpdateImageCallback(UpdateImage);
this.imgVideo.Dispatcher.Invoke(del);
}
}
Problem is, this is too slow and takes too long to refresh and the app just hangs.
I got this to work in Windows Forms with the PictureBox control but cannot get it to work in WPF. I refuse to believe that WPF is inferior to forms.
This app will always just hang (whether winforms or WPF) because you've got an infinite loop running everything it does on the UI thread. Your app hangs because you're not allowing the UI thread any time to process user input (such as resizing the window or trying to close the app).
With regard to your performance: have you tried profiling your code? I suspect that the problem is to do with you repeatedly hammering a webserver for an image, since you're never likely to get enough requests-per-second to make any kind of real-time video from static images. (There's a reason that we have video streaming codecs!)
instead of recreating whole image try to change only UriSource property.
Check out my answer to this: Showing processed images from an IP camera
Also, make sure the communication is done on a separate thread.
I suggest that the Bitmap image is a dependency object being created on a non-GUI thread. You then invoke UpdateImage on the GUI thread. Since the bitmap image dependency object wasn't created on/(owned by) the GUI thread, you get the "different thread owns it" error.
How about this as a workaround?
Copy the image temporarily to a local file location in your Watch routine.
Add a Thread.Sleep to the watch routine so that you don't hammer the CPU with the endless loop on this thread.
Use BeginInvoke instead of Invoke.
Load and update the image in the UpdateImage routine so that the image and the imgVideo objects are on the GUI thread. Update the image by reading it from your local file copy.
Without knowing the specifics of how you make Watch run on its own thread (using Background worker?) I think this approach will work for you.

Complex multi-threaded interface

First of all its not a splash-screen what i want... just to be clear... ok... lets go to the description of the problem:
i have a form that fire N number of threads (i dont know how many, the user must choose)... each thread has a object, and during several moments the objects may fire a event to signal some change... there must be a form for each thread to "report" the messages that the events are sending...
my problem is: the threads create the forms perfectally... but the desappear... out of nowhere... they appear on the screen... and vanish... poof.... gone! how can i avoid that undesired "disposing"?!?!
Your threads must either
use proper InvokeRequired + Invoke logic
or run their own MessagePump (Application.Run)
Which one did you (not) do?
If you create a form in a thread, the form will vanish when the thread is done. If you want the form to survive longer than that you need to either keep the thread alive, or create the form on the application's main thread. The latter would be preferable. Just make sure that each to hook up event listener for the object in the corresponding form, and use Invoke or BeginInvoke as needed when updating the form.
A simple example:
First a worker:
class Worker
{
public event EventHandler SomethingHappened;
protected void OnSomethingHappened(EventArgs e)
{
var evnt = SomethingHappened;
if (evnt != null)
{
evnt(this, e);
}
}
public void Work()
{
// do lots of work, occasionally calling
// OnSomethingHappened
}
}
Then, in a form we have an event handler for the SomethingHappened event:
public void SomethingHappenedHandler(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => SomethingHappenedHandler(sender, e)));
return;
}
// update gui here
}
Then it's really just a matter of wiring it all together:
Worker w = new Worker();
ProgressForm f = new ProgressForm;
w.SomethingHappened += f.SomethingHappenedHandler;
f.Show();
Thread t = new Thread(w.Work);
t.Start();
Disclaimer: this sample is quickly tossed together and somewhat untested (sitting on the train, about to get off ;) ).
A Form must be hosted on a thread with a message loop. You can create a message loop by either calling Application.Run or Form.ShowDialog. However, unless you have really good reason for doing so I would avoid having more than one thread with a windows message loop.
I would also avoid creating N threads. There are better ways to parallelize N operations other than creating one thread per operation. To name only two: 1) queue a work item in the ThreadPool or 2) use the Task Parallel Library via the Task class. The problem with creating N threads is that each thread consumes a certain amount of resources. More threads means more resources will be consumed and more context switching will occur. More is not always better in the world of multithreading.

How can I avoid Thread.Sleep when using the WPF Automation Framework?

I am trying to use WPF UI Automation working without using spurious thread.sleep statements. What I would like to do is have a function GetElementById that continually polls until the control is available (or a timeout occurs). The problem is that it appears to cache the child controls of my parent element. Is it possible to refresh to Children? Or does anyone have an alternative approach?
public AutomationElement GetElementById(string id, int timeout)
{
if (timeout <= 1000) throw new ArgumentException("Timeout must be greater than 1000", "timeout");
AutomationElement element = null;
int timer = 0;
const int delay = 100;
do
{
element = MainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, id));
Thread.Sleep(delay);
timer += delay;
} while (element == null && timer < timeout);
if (element == null) throw new InvalidOperationException("Unable to find element with id: " + id);
return element;
}
If there is a better way than judicious use of Thread.Sleep, I've yet to find it.
UIAutomation does provide Events, and I guess that you could (to use your case as an example) listen for the event that occurs when the TabItem is selected, and only then continue to find the item by Id. But I suspect that this kind of pattern would play havoc with the overall readability of your code.
The DoEvents function that is available in System.Windows.Forms.Application namespace is used to enable the UI to update while a long running process is occuring. It is not a member of the WPF namespaces. You can use something sililar by implementing it yourself as in this article or importing the System.Windows.Forms namespace and calling Application.DoEvents. This will allow your other processing to continue while you are doing your polling. It is a function to be used with caution.

Updating a Progress Bar from Another Thread

I have a windows form on the main thread and another thread that does some calculations. I'd like to update the status bar on my form from the work being done in the other thread. What's the best way to do this?
So far everything I've tried just makes things run really slowly. I'm using Visual Studio 2005.
You can use the marshaling techniques like Control.Invoke to execute a delegate on the UI thread where UI elements can be safely manipulated, but that approach is not very good. Actually, it is a terrible approach if all you want to do is update simple progress information.
By far the best method for doing this is:
Have your worker thread publish progress information to a shared variable.
Have your UI thread poll for it via a System.Windows.Forms.Timers on an interval that works well for you.
Here is what it might look like.
public class Example : Form
{
private volatile int percentComplete = 0;
private void StartThreadButton_Click(object sender, EventArgs args)
{
StatusBarUpdateTimer.Enabled = true;
new Thread(
() =>
{
for (int i = 1; i <= 100; i++)
{
DoSomeWork();
percentComplete = i;
}
}).Start();
}
private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
{
yourStatusBarPanel.Text = percentComplete.ToString() + "%";
StatusBarUpdateTimer.Enabled = percentComplete < 100;
}
}
This works well because:
The percentComplete field is declared 'volatile' ensuring its value can be reliably read from multiple threads.
The UI thread gets to dictate when and how often the UI gets updated...the way it should be!
The worker thread does not have to wait for a response from the UI thread before it can proceed as would be the case with Invoke.
It breaks the tight coupling between the UI and worker threads that Invoke would impose.
It is more efficient...considerably.
You get more throughput on both the UI and worker threads.
There is no chance of saturating the UI message queue as could be the case with BeginInvoke.
You do not have to litter you code with Invoke calls everytime you need to update the UI from the worker thread.
Make sure that you only update the user interface from the main thread or else you will have problems. You can switch your thread context by calling Invoke. There's a good post here on that.
You can send messages to the main thread and get it to update the progress bar, although you then need to check for the messages. You could also do the same sort of thing as a polling function.

Update Label while processing in Windows Forms

What is the best way to update a label on a Windows Forms application while processing?
I have a loop that does some processing to files on the user's system when the user clicks a button.
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
}
What would be some sample code?
What exactly is this called? Is it threading or delegates?
A quick fix for you would be:
Label1.Text = f.Name + " is done processing, now processing...";
Label1.Refresh();
You really want to avoid DoEvents, otherwise you'll have problems if your user repeatedly presses buttons on your form.
You should be doing this on another thread, and then updating your UI thread from that thread. You are blocking further processing by performing this work on the UI thread.
If you can't move this code to the UI thread, then you could always call Application.DoEvents, but I strongly suggest you explore these options first:
System.ComponentModel.BackgroundWorker
System.Threading.ThreadPool
System.Threading.Thread
System.Threading.Tasks namespace
You'll need to get your data from one thread to the other. This can be done in a couple of ways...
First, your "background" thread could update some kind of "CurrentStatus" string variable that it changes as it goes along. You could then put a timer on your form that would then grab the CurrentStatus variable and update the label with it.
Second, you could simply invoke the operation from the background thread to the UI thread with a delegate using the InvokeRequired property of the label control. So for example...
private delegate void UpdateStatusDelegate(string status);
private void UpdateStatus(string status)
{
if (this.label1.InvokeRequired)
{
this.Invoke(new UpdateStatusDelegate(this.UpdateStatus), new object[] { status });
return;
}
this.label1.Text = status;
}
You can call that UpdateStatus() method from any thread (UI or background), and it will detect whether or not it needs to invoke the operation on the main UI thread (and if so, does it).
To actually set up the thread, you can do so like this:
private void StartProcessing()
{
System.Threading.Thread procThread = new System.Threading.Thread(this.Process);
procThread.Start();
}
private void Process() // This is the actual method of the thread
{
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
UpdateStatus("Processing " + f.Name + "...");
}
}
Then when the user clicks the "GO" button you'll simply call StartProcessing().
If your processing is lengthy do it in a backgroundworker thread.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
I also recommend to use :
Application.DoEvents();
Processes all Windows messages currently in the message queue.

Resources