interrupting a lengthy sequence

  • Thread starter Thread starter michael sorens
  • Start date Start date
M

michael sorens

I have a lengthy sequence of operations that are executed and reported on
in a status window in a Windows Form application. Some work is done by
background threads but other work is not. I am wondering how to recognize
if the user presses an Escape (or even just a Shift key if that is
simpler) so I may then abort the remaining foreground operations. To
recognize a shift key I tried:
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { ... }
My code included a Thread.Sleep(100) every so often to allow the user
keystrokes to be recognized, or so I thought. But neither keystrokes nor
button clicks are being recognized. What am I missing?
 
I have a lengthy sequence of operations that are executed and reported on
in a status window in a Windows Form application. Some work is done by
background threads but other work is not. I am wondering how to recognize
if the user presses an Escape (or even just a Shift key if that is
simpler) so I may then abort the remaining foreground operations. To
recognize a shift key I tried:
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) { ... }
My code included a Thread.Sleep(100) every so often to allow the user
keystrokes to be recognized, or so I thought. But neither keystrokes nor
button clicks are being recognized. What am I missing?

You need to give your app time to process the message loop. You can do this
by calling Application.DoEvents or by running the entire process in a
thread.

Michael
 
Cool, glad to see your problem is resolved.

Yes, Application.DoEvents is the correct way for this issue. I want to
share some more background information to you:

Winform GUI normally runs in a single thread, so when the GUI thread is
processing a length operation, it can not go back to the GUI message loop
to receive various messages to process. Since the keyboard events are
dispatched to the GUI controls through message loop, this length processing
will cause the application to be unresponsive to keyboard. Another side
effect is that the application can not process WM_PAINT message in the
message loop, which means the application can not re-paint its UI anymore.
These 2 side effects will result in application-hang to the end user.

So, to resolve this problem, the normal solution is calling
Application.DoEvents() method in the length processing code every a few
time. Application.DoEvents() method will try to remove and process all
Windows messages currently in the message queue. So all the pending
WM_PAINT messages and keyboard messages will be processed during the length
operation, which makes the application keyboard aware of and UI
responsible.

Another common solution to this scenario is placing the length operation
work in the background thread so that the GUI thread can continue its
message looping without blocking. Chris Sells demonstrates this technology
in the article below:
"Safe, Simple Multithreading in Windows Forms, Part 1"
http://msdn2.microsoft.com/en-us/library/ms951089.aspx

Note: while doing multithreading in Winform, the key point to remember is
that .Net Windows Forms uses the single-threaded apartment (STA) model
because Windows Forms is based on native Win32 windows that are inherently
apartment-threaded. The STA model implies that a window can be created on
any thread, but it cannot switch threads once created, and all function
calls to it must occur on its creation thread. So while the background
thread wanted to manipulate the GUI controls methods/properties, it must
marshal the calling with Control.Invoke/BeginInvoke methods, or this may
cause some strange and hard to detect thread-safe issue. Please refer to
the the article below for more information:
"Multithreaded Windows Forms Control Sample"
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/htm
l/cpconDevelopingMultithreadedWindowsFormsControl.asp

If you still need any help or anything unclear, please feel free to tell
me, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Just for completeness, in many circles DoEvents() is considered a hack, with
the correct solution being to do the real work on a worker thread with
suitable UI sync code. This can be a little more work, but can help avoid
some interesting "trying to do 5 things at once" on the UI thread (not "at
once" in the CPU sense - but rather 5 separate methods started via a UI
event and interrupted by DoEvents()).

For a "utility" app, or something small, you can probably get away with
it... but once you get the knack, the "proper" way isn't much more effort -
especially if you use (for instance) the BackgroundWorker component as a
easy route in...

Marc
 
Thanks for all the follow-up information; there's quite a bit to digest in
those articles. Just one final point: what is the proper way to process a
TableAdapter.Fill() call in the background? That operation may take quite
a while in my application. If I hand it off to a worker thread
with the Invoke mechanism described, doesn't that still lock up the UI?
 
Jeffrey Tan said:
Cool, glad to see your problem is resolved.

Yes, Application.DoEvents is the correct way for this issue.

Eek, no. Tying up the UI with a long-running task and periodically
calling Application.DoEvents is a nasty way of solving the problem. In
particular, if there are *any* blocking calls (eg file IO which might
be across a network, or a database transaction) then while that
blocking operation is taking place, the UI will be hung.

This kind of problem plagues some applications, eg Visual SourceSafe,
and is a source of great annoyance to users.

Another common solution to this scenario is placing the length operation
work in the background thread so that the GUI thread can continue its
message looping without blocking.

That's a *far* better way of doing things.

<snip>
 
michael sorens said:
Thanks for all the follow-up information; there's quite a bit to digest in
those articles. Just one final point: what is the proper way to process a
TableAdapter.Fill() call in the background? That operation may take quite
a while in my application. If I hand it off to a worker thread
with the Invoke mechanism described, doesn't that still lock up the UI?

I assume you're using data binding? If so, there's a bit of a problem -
filling the table calls UI handlers, so you have to do it in the UI
thread if you have data bindings. This is a pain. You could undo the
binding, fill the table in a different thread, and then redo the
bindings, or take the nasty hit of doing the fill in the UI thread.

Having said this, my experience with data binding is solely on 1.1 - I
believe it's been much improved in 2.0, so there may be some better
solutions there.
 
Hi Jon ,

Yes, I see your point. However, asking the customers to change their design
is not always practical, so I provide 2 solutions to the customer and let
them judge the choice. Sure, the second multithreading solution is better
than first one.

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Jeffrey Tan said:
Yes, I see your point. However, asking the customers to change their design
is not always practical, so I provide 2 solutions to the customer and let
them judge the choice. Sure, the second multithreading solution is better
than first one.

That's a reasonable thing to say - what I was most objecting to was the
idea that "Application.DoEvents is the correct way for this issue" and
"So, to resolve this problem, the normal solution is calling
Application.DoEvents() method".

Application.DoEvents should generally be regarded as a much riskier way
of doing things, IMO.
 
Hi Michael ,

Thanks for the feedback.

Yes, your question makes sense. Just as Jon said, if you invoke
TableAdapter.Fill() method with Control.Invoke in background thread, it
will actually still be executed in GUI thread, because the databinding code
will trigger the UI updating.

Jon's suggestion is a suitable solution. First, cutting of the databinding
by setting DataGridView.DataSource=null. Then, you may call
TableAdapter.Fill() method directly in background thread without marshal it
with Control.Invoke. This is because the TableAdapter.Fill() method totally
executed in non-GUI thread now, it will not touch the UI anymore. After
filling the DataSet in background thread, the background thread can call
Control.Invoke again to set DataGridView.DataSource to this filled dataset.

Also, you may customize the SQL statement in the SqlDataAdapter to
introduce paging algorithm, so that you only need to fill a small fraction
of the total data. You may retrieve the next page of data when the user
clicked "Next Page" button. This will reduce the time of
TableAdapter.Fill() method which will not consume much time in GUI thread
now. Actually, this paging makes sense because end user will not always
need the total large data from Database.

This delay evaluation is widely used in various components of computer.

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Thanks to both Jeffrey and Jon for this insight into cutting and restoring
the databinding. I was not aware that would work, so I will give it a try.
 
I do understand the perils and annoyance of tying up the UI, but--as
suggested--one does not always have the luxury of doing everything the
best way. :-)
 
Hi Michael,

Thank you for feedback the status. Ok, if you need any further help, please
feel free to tell me, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top