Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am creating a 'cleaner' application using windows forms c# and want to have a progress-bar. At the moment I am just cleaning temp files, cache files and browser junk.
I have a progress bar:
private System.Windows.Forms.ProgressBar _myProgressBar;
And here is my cleanup method:
private void button1_Click_1(object sender, EventArgs e)
{
_myProgressBar.Value = 0;
ClearCache();
_myProgressBar.Value = 25;
ClearCookie();
_myProgressBar.Value = 50;
ClearHistory();
_myProgressBar.Value = 75;
TempCleaner();
_myProgressBar.Value = 100;
}
I want it to execute those 4 methods (each of which takes a few seconds to complete) while updating the progress bar as it goes; but all it does is wait until they have finished and then show the progress bar as completed. How can I make the progress bar show the progress as it completes each part?
This is a complicated subject, and the code sample and your reputation indicates that you may be a little inexperienced; which is a tricky combination. But I'm going to try to answer... (other readers please bear in mind I'm going to try to keep this answer simplistic).
The reason it's complicated is that in order to do it properly, you're going to need to use threading. Threading is a massive and tricky topic, which causes problems even for experienced developers. I cannot possibly hope to reasonably cover that topic all here, so I'm just going to stick to the key points.
When your WinForms application is running, unless you arrange it specially, your code will be running on what can be called the "user interface thread" ("UI thread"). Ideally, that thread should be left to do things like processing events on the window (like moving, resizing, mouse clicks, etc). If you want to do anything which is going to take time (like the contents of your "Click" method above), then you need to execute that functionality on a background thread (not directly within the method, as you've done above)
Something you need to know is that although the work is going to be done on a background thread, the changes to the progress bar will have to be done by the UI thread - this is mandatory, you are not allowed to do it from a background thread.
So here are the key points to my proposed solution:
Move the code from the Click method into a background worker, and add the notification of progress from that worker as it progresses
Update the progress bar from the correct thread
So here we go...
1) There are lots of ways of doing this. Here's one... move all your code from the Click event into a method called "BackgroundThread", and modify your click method so you have this...
private void button1_Click_1(object sender, EventArgs e)
{
if (_thread == null)
{
_thread = new Thread(new ThreadStart(BackgroundThread));
_thread.IsBackground = true;
_thread.Start();
}
}
private void BackgroundThread()
{
UpdateProgress(0);
ClearCache();
UpdateProgress(25);
ClearCookie();
UpdateProgress(50);
ClearHistory();
UpdateProgress(75);
TempCleaner();
UpdateProgress(100);
}
You'll need to declare that "_thread" variable as a private member variable on your Form (to prevent it being garbage collected).
Now I'm going to highlight but not solve a couple of problems that you will need to deal with. Since we have moved the code onto a background thread, there will be nothing stopping the user pressing the button lots of times, each of which would fire off a new thread (probably not what you want). How you deal with that problem depends on how the progress bar sits in your UI, and how often you want people to press the button. A simple (but incomplete) answer would be to wrap the contents of the Click method in "if(_thread==null)" (as shown above), meaning that the button would only work the first time you clicked it.
The other problem is notifying your background thread if you wanted it to stop what it was doing (e.g. user wants to close the application). As I've said, it's a big topic, and I'm not going to get into that here.
3) The above code needs us to write the UpdateProgress method...
private void UpdateProgress(int percent)
{
RunOnUiThread(() => _myProgressBar.Value = percent);
}
private void RunOnUiThread(Action action)
{
if (InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
}
You will have guessed the "_myProgressBar.Value = percent"; but the rest of it probably looks bizarre. The RunOnUiThread method is reusable on any WinForm Form or Control, so you might like to keep that in a snippet. Basically, whatever action you give it, it will check whether it's on not the UI thread ("InvokeRequired"). We're passing it a lambda expression "() =>" to do what we need to happen on the UI thread.
If it's on the wrong thread, it will "Invoke" the action (basically queue it up so that the UI thread will do it when it gets the chance). If it was already on the right thread then it will just execute the action on the same thread.
That should do it.
Related
Well, I've been struggling with this for a while and I've been unable to find a solution.
I'm developing one of those cashier apps you see in the supermarket where everything works fast as hell. The cashiers know the program so good they just type at the speed of light so the UI must be incredibly responsive.
Anyway, as I've only coded in WPF I'm using it, if someone says WinForms is faster I'll surely learn it.
This is my code, it's very simple as I'm testing performance
public partial class MainWindow : Window
{
Thread t;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
t = new Thread(HandleThread);
t.Start();
}
private void HandleThread()
{
Thread.Sleep(3000);
}
}
Whenever the button is clicked, a new thread is launched as I plan on connecting to a server in the new thread.
My problem is the method button_Click is VERY SLOW. It takes about 1 second to return so the button looks like it's stuck and the application look sluggish. I've tried threads, BackgroundWorker, changed the framework to 4.0 and tried Tasks. NOTHING. Whatever I do to start some kind of background work the method takes forever.
I need to check the username and password on a remote server, how can I accomplish it without hurting the UI?
You should turn off the IntelliTrace (How-To).
On the Tools menu, click Options.
In the Options dialog box, expand the IntelliTrace node and then click General.
Clear the Enable IntelliTrace check box.
Click OK.
Do you need to spawn a new thread every time you click your button? Is this thread long-lived? Do you really want to allocate 1 meg of stack space (on 64 bit OSes) for this every time you click this button? Really sure you don't want to use the TPL's Task and CancellationTokenSource instead?
In your case, you should really not create a thread every time you click, what you want is start a long-running async task and check the result.
public void OnClick(object src,EventArgs args)
{
var login = tbLogin.Text;// assuming non MVVM coding here
var pwd= tbPass.Text;
Task.Factory.StartNew(()=>{
return _myWebService.CheckAuth(login,pwd); // your login stuff here
}).ContinueWith(wsTask=>{
if(!wsTask.IsCompleted){ // handle errors / cancel }
DisplayLoginState(ws.Result);
}, TaskScheduler.FromCurrentSynchronizationContext()); // this runs on the UI Thread
}
Sorry for the lengthy post, I just want to illustrate my situation as best as possible. Read the items in bold and check the code if you want the quick gist of the issue.
I use ClickOnce to deploy a C# application, and have opted to have my application check for updates manually using the ApplicationDeployment Class rather than letting it do the update checking for me.
The program is a specialized network scanner that searches for network devices made by the company I work for. Once the main window is loaded, a prompt is displayed asking if the user would like to scan the network. If they say Yes, a scan begins which can take a minute or two to complete depending on their network settings; otherwise it just waits for the user to do some action.
One of the last things I do in Form_Load is create a new thread that checks for updates. This had all been working fine for several months through about 12 releases and has suddenly stopped working. I didn't change the update code at all, nor change the sequence of what happens when the app starts.
In staring at the code, I think I see why it is not working correctly and wanted to confirm if what I think is correct. If it is, it begs the question as to why it DID work before - but I'm not too concerned with that either.
Consider the following code:
frmMain.cs
private void Form1_Load(object sender, EventArgs e)
{
// set up ui, load settings etc
Thread t = new Thread(new ParameterizedThreadStart(StartUpdateThread));
t.Start(this);
}
private void StartUpdateThread(object param)
{
IWin32Window owner = param as IWin32Window;
frmAppUpdater.CheckForUpdate(owner);
}
frmAppUpdater.cs
public static void CheckForUpdate(IWin32Window owner)
{
if (ApplicationDeployment.IsNetworkDeployed) {
Console.WriteLine("Going to check for application updates.");
parentWindow = owner;
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(ad_CheckForUpdateCompleted);
ad.CheckForUpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);
ad.CheckForUpdateAsync();
// CAN/WILL THE THREAD CREATED IN FORM1_LOAD BE TERMINATED HERE???
}
}
When the CheckForUpdateAsync() callback completes, if no update is available the method call simply returns; if an update IS available, I use a loop to block until 2 things occur: The user has dismissed the "Would you like to scan prompt" AND no scan is currently running.
The loop looks like this, which takes place in ad_CheckForUpdateCompleted:
while (AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) {
System.Threading.Thread.Sleep(5000);
}
I sleep for 5 seconds because I figured this was happening in a separate thread and it has seemed to work well for a while.
My main question about the above code is:
When ad.CheckForUpdateAsync(); is called from CheckForUpdate does the thread I created in Form1_Load terminate (or might it terminate)? I suspect it may because the subsequent Async call causes the method to return, and then start another thread?
The only reason I am confused is because this method WAS working for so long without hanging the application and now all of the sudden it hangs and my best effort at debugging revealed that it was that Sleep call blocking the app.
I'd be happy to post the full code for frmAppUpdater.cs if it would be helpful.
When ad.CheckForUpdateAsync(); is called from CheckForUpdate does
the thread I created in Form1_Load terminate (or might it terminate)?
If the CheckForUpdateAsync() call is asynchronous then yes, the thread will terminate, no it won't otherwise.
If you suspect the Sleep to have caused the application hang then these two variables AppGlobals.ScanInProgress and AppGlobals.ScanPromptVisible are probably always set to true! You should start looking at the code that is setting them to true and see what is going on there.
In order to avoid an application hang, you could introduce a variable to avoid sleeping indefinitely:
int nTrials = 0;
while ((AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) && (nTrials < 5)) {
System.Threading.Thread.Sleep(5000);
nTrials++;
}
// Check the results and act accordingly
I personally do not like using Sleep for thread synchronization. .NET offers a bunch of classes that are perfect for thread synchronization, WaitHandle being one of them.
See this post at Asynchronous Delegates Vs Thread/ThreadPool?
your form load method seems to be doing synchronous work. you mention that you are using clickonce deployment. Has the binary location changed after the previous release or has permissions on this resource changed. Looks like the work (checkupdates) in the Thread is never finishing and is never handed back to the form.
as an immediate fix, I would change the Thread approach to Delegate - if you use delegate, then this becomes less of a customer issue (the form will respond to end user) but the underlying problem remains.
as the next step, i would go through http://msdn.microsoft.com/en-us/library/ms229001.aspx and do the troubleshoot
I have a multi threaded app where I have created a Producer/Consumer Pattern for the processing of XML files etc
What I want to know is what would be a good approach for updates to the UI and when the process finshed.
Should I go down a threaded approach and create Barrier and wait for all tasks to complete or should I just create an event/delegate and get the UI to catch this for both UI updates/compeltion
You may checkout Signalr which allows you to achieve PUSH notifications to clients.
Sorry, this answer was assuming Windows Forms, because there was no asp.net tag at first. Hopefully someone will provide a good answer for asp.net as well.
You can use Form.Invoke() to update stuff from the non-GUI thread, here's a simple example:
private void Form1_Load(object sender, EventArgs e)
{
Thread thread = new Thread(DoStuff);
thread.Start();
}
void DoStuff()
{
Thread.Sleep(1000);
this.Invoke(new Action(() => MessageBox.Show("Hey, this was invoked")));
}
With Invoke, you can do just about anything with the Form, it runs the invoked action in the form's thread. So you can change your status text or whatever that way. Of course, you shouldn't have your "business logic" inside the Form class, and you shouldn't use Thread.Sleep, I'm just showing the very basic fact that you can call back to the UI thread this way.
As to whether you should wait for all tasks to complete before doing anything, or show status updates as they execute, that's really up to you. I like to show updates on the UI when possible so the user knows what's going on. It's also good to have a progress bar going if there's something happening that the user might be waiting for. The progress bar can just be a marquee if you don't have a good way to indicate real progress.
I want to create a program that does remote installs. I'd like to be able to have a window launch at the start of each installation and give a progress bar with the approximate progress of the job.
If I create the window off the main thread I can access all of it's elements but it's not responsive due to the task of actually running the install. If I launch the window in a new thread I can't seem to get the main thread to recognize any of the elements in the window so I can't update the progress bar.
How can I create a multi-threaded UI that is responsive?
To keep the UI responsive with a second thread (that's doing the work) and be able to update the UI from the second thread, you'll need to use Invoke on the controls. There's a number of code snippets available out there.
Here's a short example: How can I update my user interface from a thread that did not create it?
You can find dozens of examples here on SO as well - just do a search :)
Here's another similar question on SO today - cross threading problem - there's a code example in one of the answers.
There's a simple way to access UI controls from other threads safely - to call lambda with Control.BeginInvoke(lambda) method. For example, suppose you have WinForms application with one form and progress bar with button on it. When button is clicked - some imaginary work is going on in separate thread and progress bar should be updated. The simplest would be to write something like that:
private void button1_Click(object sender, EventArgs e)
{
ThreadStart s = () =>
{
for (int i = 0; i <= 10; i++)
{
Action a = () => progressBar1.Value = i * 10;
BeginInvoke(a);
Thread.Sleep(300); // imitate some workload
}
};
var t = new Thread(s);
t.Start();
}
The same result could be achieved with BackgroundWorker or other techniques, but this is the most simple as for me.
Somewhat new to C# but I have a major problem with getting these things to work because if my background worker is running a long process by using a method from another class, then that class has no access to the background worker in order to update the progress.
For instance:
private void bgWorker_DoWork(object sender DoWorkEventArgs e)
{
bgArgs args = e.Argument as bgArgs;
MyClass objMyClass = new MyClass();
MyClass.MyMethod(strValue, args.Option);
//Do something based on return value of long process.
}
If I try to update bgWorker from the class "MyClass", it cannot "see" bgWorker, it doesn't exist in the context of the class, it's in the UI class because in Visual Studio, that's where you drag it from the toolbox.
The only way I've gotten it to work is to pass the whole UI form to the class, which creates other problems when trying to access that class from anywhere but the main form. From there I just update the progress bar manually via ProgressBar1.PerformStep() as it runs through the loops.
Also, I've already changed the modifier on my progress bar to internal, so it's not that the class doesn't see the progress bar.
I might be able to pass the bgworker by itself to the class through the method, but that just doesn't seem right.
I think your architecture needs revising here. The background worker is for running operations in the background, like out-of-sight-out-of-mind. While it is running, you can accept feedback from it by observing the BackgroundWorker.ProgressChanged event, which will help you increment your progress bar with PerformStep(). However, you shouldn't attempt to alter the BackgroundWorker while it is running. This gets you into Threading issues, which I'm not sure you really want :) You see, the BackgroundWorker uses a different thread to perform its operations when it runs, so changing it while running means you have to access the thread it is performing its work upon. This gets ugly. It is best to just give it a method to execute, let it run, check in on its ProgressChanged, and wait for it to finish.
Assuming I'm understanding your question correctly you probably need to make a method that can access the UI progressBar despite the source thread. The below will do just that saving you from blowing up the application when you try to set the value.
private delegate void UpdateProgressBarCallback(int barValue);
private void UpdateProgressBarHandler(int barValue)
{
if (this.progressBar1.InvokeRequired)
this.BeginInvoke(new UpdateProgressBarCallback(this.UpdateProgressBarHandler), new object[]{ barValue });
else
{
// change your bar
this.progressBar1.Value = barValue;
}
}
[see http://msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspx ]
Then you just call UpdateProgressBar(value); (likewise, if you want this to step you can adjust the arguments/way the method operates)
Next you can go about this a few ways: You can make your background worker (since it's already in another class) event driven and then attach progress changes and update the UI; or you can pass a delegate to the thread workers as a reference so it knows where to adjust the UI.
Comment and leave me a direction to go and I'll see if I can help you (and confirm I understand the question).