I seem to have a problem executing commands in the correct order, I have a method in my Program:
private void GenerateButton_Click(object sender, EventArgs e)
{
Statuslabel.Text = "Working...";
LongMethod();
//Call to another Method of another class which takes 15-20 seconds to execute
Statuslabel.Text = "Done";
}
the problem seems to be that instead of assigning "Working" to the status label and THEN calling the LongMethod, the Program seems to execute LongMethod() first, and then it changes Status Label's text to "Working" for a split second and then immediately changes it to "Done".
Oh, and the UI is locked up during the LongMethod() execution, because the Program is SingleThreaded.
I tried threads earlier, but for the life of me I couldn't get the syntax right, I tried:
Thread MyThread = new Thread(LongClass.LongFunction);
Thread MyThread = new Thread(new ThreadStart(LongClass.LongFunction));
Where LongClass is the class which contains LongFunction as a static method.
I will check out the background worker now.
You should execute LongMethod on another thread so that the UI thread doesn't block while it's running.
Remember, updating the UI is running code just like anything else. While your long-running method is running, that thread is not doing any of the tasks necessary to redraw the user interface. Changing a UI element does not stop everything and re-draw it because suppose you changed a thousand UI elements; you wouldn't expect a redraw after each one; you'd expect them all to happen at once, after you'd made all the changes.
Long story short, if you want to refresh the UI after the update but before the long-running code -- that is, you don't care about hanging the UI but you at least want it to update -- then insert a call that explicitly refreshes the UI.
Some have suggested "DoEvents" as a workaround. This can work, but it is super dangerous. For two reasons. First, suppose the user clicks a button twice. During the processing of the first click, you do a DoEvents, and so you then recurse and hey, now you have suspended the processing of the first button click so that you can process the second button click... and that can't be good.
Second, suppose you're processing an event, and you do a DoEvents, which causes you to start processing another event, and then while you're doing that, you do a DoEvents, and that causes you to start processing a third event... and this keeps going forever. When do you finish processing the first event? Potentially never. Remember "DoEvents" basically means "concentrate on what just happened at the expense of what you were already working on".
While I think Jason's answer to use another thread is the way to go, there is another "evil" option.
Statuslabel.Text = "Working...";
Application.DoEvents();
LongMethod();
Statuslabel.Text = "Done";
Related
I have a simple app that read a database and then aftersome manipulation write the results on another one.
The first lines of code update the ui with a message for the user and an onscreen log, then is all wrapped inside a try/catch construct with usings and other try/catch annidated.
message.AppendText("** Message for the user that appear only after the try block's execution **\n");
message.ScrollToEnd();
try
{
using(SqlConnection...)
{
business code
}
}
catch
{
bbbb...
}
In the end it works, but the ui is only updated when it finishes all.
I can understand why what's inside the try must wait the end, but why the first lines don't affect the ui till the end of the successive block?
And how can I create more responsive ui?
I first tried creating a thread for any connection (one has a timout of 5 seconds), and one for the businness code.
Ok, it was overkill, but was experimenting.
I had so much problems sharing the connections between threads and interacting with the main window's ui that abandoned the idea and rewrited all as described above.
People here have suggested creating a responsive UI. This is one way to do that. At the top of your code file, add:
using System.Threading;
Move all the stuff that takes a long time to a new method:
public void LoadStuff()
{
// Do some stuff that takes a while here
}
Replace the original stuff with this code:
Thread callThread = new Thread(new ThreadStart(LoadStuff));
callThread.Start();
Now, anytime you need to update your UI from LoadStuff you have to encapsulate it (surround it) with this code. The reason for this is only the thread that creates the UI can modify it. So, we have to tell our new thread to refer back to the old thread to execute the code. Therefore, inside LoadStuff, after you compute a bunch of data, to update your UI use this:
this.Dispatcher.Invoke(new Action(() =>
{
// Code to update UI here
}));
Like others have suggested, there are others ways to increase UI speed, and I was not the first to suggest using a different thread to compute. But I just wanted to show you a way to do it.
In addition to moving long-running processes off of the UI thread, there are some UI tricks that you can do to help make user interaction feel a little better. For example, if an action takes more than about 0.1 seconds, try fading in a message (e.g. "Loading...") to let the user know that there is something happening. Once you get the data back, fade this message back out.
You may also want to try animating the UI update to avoid the "stuttering" sensation.
I'll be short and to the point. I basically need a way I can take a timer, check when the timer is up and execute something at the end of it, then loop it. For example (this code isn't serious, just example code) :
Timer1.start();
If (timer1.TimeRanOut) {
Timer2.start()
}
Or some other way to pause the timer without the GUI freezing up (I'll be running some things at a fast pace and freezing would get in the way). I'll be making this more complex by looping the entire process, like :
if (Checkbox1.checked == true; )
{
Timer1.start();
Next If (timer1.TimeRanOut) {
Timer2.start()
}
Next If (timer2.TimeRanOut) {
Timer3.start()
}
And so on. Any solutions?
I would suggset working with Tasks. you set up a task to do something (it can just wait for X seconds, than it is a timer) than you set continueWith to assign a new task to run when the first one is finshed.
You can read more about this here:
http://msdn.microsoft.com/en-us/library/dd537612.aspx
And by the way, you really should not run heavy calculations on the UI thread itself.
If you decide to use tasks - that would be fine. Otherwise , you need to create background thread and do the work there.
Edit:
After some clarification from the OP , I will try to explain the basics or working with UI and background threads:
When you run a winforms/WPF application, all of the user interface events are handled in a single thread - the UI thread. it goes over all of the events and processes them.
If long calculation occupy this thread, the UI will become "stuck" and o responsive. see:
UI freezes on heavy calculation
That is why, any long calculations should be done on another thread, in the background.
In the above post's answer there is an example on how to do this.
You could use the System.Threading.Timer. You would then make use of its single shot capability (see Change method). Such you may chain several timers.
The callback of this timer runs on the thread pool so your UI doesn't freeze.
I have window with button which triggers lengthy processing. I put processing in a separate thread, but -- to my surprise -- it makes GUI frozen anyway. No control is refreshed, I cannot even move the window.
So the question is how to start the thread, so it won't interfere with GUI, i.e. so the GUI would always be up to date (while processing I change the data, and GUI displays some pieces of it)?
That is how I start thread currectly:
var thread = new Thread(doLearn);
thread.IsBackground = true;
thread.Start();
Edit 1
Jon:
I don't use any locks at all
No Join calling
The UI thread is left alone -- it simply sits there
The processing is a big loop with math operations, not even allocating memory, on UI side I have controls with binding (WPF) to data, like the number of current iteration of the main loop. It should be refreshed each time the main loop "ticks". The counter of the loop is a property which triggers OnPropertyChanged with each change (classic WPF binding).
Edit 2 -- Almost there!
Ok, so Jon hit the nail at the head (who is surprises? ;-D) -- thank you! The problem comes from changing the Counter. When I used instead the Counter, local counter the GUI was refreshed -- I mean I could move windows, but... I couldn't see display of the Counter.
What I have here -- a WPF GUI, with such data-binding
<TextBlock Text="{Binding Path=Counter"/>
and I have Counter property of course which on each change sends event PropertyChanged. One of the listeners is for sure GUI.
So, Jon answer is valid "the answer", but from good design POV not exactly, because if GUI part has to pull up the info about Counter and update the display every (let's say) 3 seconds, why would anyone use data binding? For me such approach invalidates data binding idea.
I could, theoretically, pass to the processing thread the GUI dispatcher, and do all the sending in GUI thread, and it could work (I didn't try it) but it would mean tight coupling of non-GUI part and GUI part.
So far, I have no idea how to do it "right" way. The best guess so far is to create TimerDispatcher but not at GUI side but inside the processing library, and update Counter value immediately but do all the sending from time to time (I didn't try it yet though).
Small remark: I have more properties binded actually, like IsRunning, which is changed at the beginning and at the end of processing. And those changes DO affect the display correctly -- but the Counter change triggers around 3000 notifications in 3-4 seconds. So it looks like jamming problem. I did another test -- I killed the data binding partially, so notifications were sent, but GUI was not "receiving" them -- but was listening to them. In such case the GUI was also frozen.
So, I am still listening to all advices -- thank you advance for sharing.
Edit 3
The saga continues here:
How to do the processing and keep GUI refreshed using databinding?
It should be fine as it is. Things which may be freezing your UI:
Are you locking within the UI thread, and locking on the same lock in your other thread?
Are you calling Join on the thread from your UI thread?
Are you doing some other heavy work in the UI thread?
If you could come up with a short but complete program which shows the problem, I'm sure we could help to fix it... but it certainly should be okay.
EDIT: Okay, now you've added this:
The counter of the loop is a property which triggers OnPropertyChanged with each change (classic WPF binding).
So you're updating the property from the non-UI thread? I would expect that to cause problems, because it will trigger UI changes from the wrong thread.
I suggest you take an approach such as:
Periodically update the counter via Dispatcher.BeginInvoke
Have the "UI counter" and the "worker counter" - and copy the value from the "worker counter" to the "UI counter" in the UI thread via a DispatcherTimer, essentially polling it.
There are numerous methods to run functions off the UI thread, but the easiest and generally most suitable is to look at the BackgroundWorker component. Many decent tutorials can be found. For example, here.
I put processing in a separate
thread, but -- to my surprise -- it
makes GUI frozen anyway.
I really hate to tell you, but then you did NOT put it into a separate thread. That simlpe.
There was a poster here that had a similar issue some time ago and through a mistake in his invoking code he basically had all processing before the thread started, with the thread jsut returning the result.
I faced the same situation, and solved it by two ways...
Use the thread in other class and invoke it in ur main application by creating Thread, either in its constructor OR in any method.
if u want do the it in same class, then create a Thread that call your function, and that function should invoke the Delegate.
See the examples:
public partial class Form1 : Form
{
private delegate void TickerDelegate();
TickerDelegate tickerDelegate1;
public Form1()
{
InitializeComponent();
}
//first solution
// This button event call other class having Thread
private void button1_Click(object sender, EventArgs e)
{
f = new FormFileUpdate("Auto File Updater", this);
f.Visible = true;
this.Visible = false;
}
// Second Solution
private void BtnWatch_Click(object sender, EventArgs e)
{
tickerDelegate1 = new TickerDelegate(SetLeftTicker);
Thread th = new Thread(new ThreadStart(DigitalTimer));
th.IsBackground = true;
th.Start();
}
private void SetLeftTicker()
{
label2.Text=DateTime.Now.ToLongTimeString();
}
public void DigitalTimer()
{
while (true)
{
label2.BeginInvoke(tickerDelegate1, new object[] {});
Thread.Sleep(1000);
}
}
}
I've got a simple method that does this:
private void searchButton_Click(object sender, EventArgs e)
{
searchResultsBox.Hide();
doSomething();
}
searchResultsBox is a listbox, and when I call its Hide method, it doesn't actually completely vanish until 'doSomething' finishes processing. It kind of leaves artifacts (in fact you can still see any part of the box that had an empty form surface behind it.
If I comment out 'doSomething', it vanishes promptly.
Any ideas on how to fix this? It's just a bit ugly.
You could try calling this.refresh() after calling searchResultsBox.Hide();
You should not do significant work in the GUI event thread, as it will cause the UI to freeze up while it is busy running your event handling code. It is a good practice to do any long-running tasks in another thread, either by signaling another already-running thread to do the work or by starting a new thread on the spot.
new Thread(new ThreadStart(doSomething)).Start();
Sound like doSomething is process intensive and blocking the GUI thread, not sure why it would not finishing hiding the list before executing doSomething.
I would try putting doSomething in to a separate thread.
The separate thread or background worker process is the best answer. You can also try calling Application.DoEvents(). That seems to work at times for this specific issue although I'm not in favor of using that call often.
EDIT: It is not a listbox. My mistake. It is a list view.
I have a list view control that's driving me nuts. It is a multi-select list box, so if the user selects 5000 rows, then de-selects them by selecting a single row, the SelectedIndexChanged fires 5001 times. This causes my app to hang.
I'm trying to use threads to count the number of times that the event WOULD have fired, and then letting the last iteration do all the actual work.
Here's the code I started with. The big catch: I need the "do fancy calculations" to be in the same thread as the calling events due to items out of my control.
EDIT: I know that this code doesn't work. The Join() blocks the current thread which negates the entire purpose of creating the thread. My question is : How do I do something LIKE this.
My biggest problem isn't creating the thread. It's that my "do fancy" has to be in the same thread.
void IncrPaintQueue()
{
PaintQueue++;
Thread.Sleep(100);
}
int PaintQueue = 0;
private void SegmentList_SelectedIndexChanged(object sender, EventArgs e)
{
// We need to know how many threads this may possibly spawn.
int MyQueue = PaintQueue;
// Start a thread to increment the counter.
Thread Th = new Thread(IncrPaintQueue);
Th.IsBackground = true;
Th.Start();
Th.Join();
// if I'm not the last thread, then just exit.
// The last thread will do the right calculations.
if (MyQueue != PaintQueue - 1)
return;
// Reset the PaintQueue counter.
PaintQueue = 0;
// ... do fancy calculations here...
}
I remember solving this issue before:
A better way perhaps for you would be
to put a minimal delay in your
ItemSelectionChange Handler. Say --
50ms. Use a timer, Once the selection
changes, restart the timer. If the
selection changed more than once
within the delay period, then the
original is ignored, but after the
delay has expired, the logic is
executed.
Like this:
public class SelectionEndListView : ListView
{
private System.Windows.Forms.Timer m_timer;
private const int SELECTION_DELAY = 50;
public SelectionEndListView()
{
m_timer = new Timer();
m_timer.Interval = SELECTION_DELAY;
m_timer.Tick += new EventHandler(m_timer_Tick);
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
// restart delay timer
m_timer.Stop();
m_timer.Start();
}
private void m_timer_Tick(object sender, EventArgs e)
{
m_timer.Stop();
// Perform selection end logic.
Console.WriteLine("Selection Has Ended");
}
}
A possible solution is to delay the work, so you know whether or not more events have fired. This assumes the order of the selections is not important; all that matters is the current state.
Instead of doing the work as soon as the event fires, set up a timer to do it a couple milliseconds after the event fires. If the timer is already running, do nothing. In this way the user should perceive no difference, but the actions will not hang.
You could also do the work on another thread, but have a flag to indicate work is being done. If, when the selection event fires, work is still being done you set a flag that indicates the work should be repeated. Setting 'repeat_work' to true 5000 times is not expensive.
I get the impression that you're trying to solve a problem through brute force. I would suggest trying a different event:
private void myListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
{
// do your logic here
}
}
I would suggest avoiding creating threads if at all possible, since they have overheaad. I couldn't see from your example where there's any need for parallelism.
First, while you are properly synchronizing access to PaintQueue, I feel it was more by chance in this situation as opposed to design. If you have other code accessing PaintQueue on other threads, then you have a problem.
Second, this code makes no sense. You are spooling up a new thread, incrementing the value on that thread, and then waiting for 1/10th of a second. The thing is, the code that kicks off the thread is waiting on that thread to complete. Because of this, you are just waiting in the UI thread for nothing.
Even if you queue the SelectedIndexChange events, you aren't going to be able to prevent your app from hanging. The SelectedIndexChange event is going to fire every time that you select an item, and if the user selects 5000 items, then you need to process all 5000 events. You could give them a window (process every n seconds or whatever) but that's rather arbitrary and you put the user on a timer, which is bad.
What you should do is not tie the operation to the SelectedIndexChanged event. Rather, have the user select the items and then have them perform some other action (click a button, for example) which will work on the selected items.
Your app will still hang though if you have to process a number of items for a lengthy period of time on the UI thread, but at least selecting the items won't hang.
You don't really achieve any kind of concurrency by starting a new thread and then immediately Joining it. The only "effect" of the code above is that you method is run by another thread.
Additionally, if you want to use a background thread and safe the rather expensive cost of newing a thread, you should employ the thread pool.