Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Access form controls via a different thread (BeginInvoke) 1

Status
Not open for further replies.

RobHudson

Programmer
Apr 30, 2001
172
GB
Hi

I am trying to get access to form controls from a separate thread to that of the application. After some hunting I found this article: which has helped :)

As I have a number of controls that I need to reference and control I thought I would create a property for each control and reference that.

This is what I devised:

Code:
public delegate void MethodInvoker();

public TextBox _txtLink
        {
            get
            {
                if (this.txtLink.InvokeRequired)
                {
                    TextBox txt = null;
                    this.txtLink.BeginInvoke(
                        new MethodInvoker(
                        delegate() { txt = this.txtLink; }));
                    try
                    {
                        MessageBox.Show(txt.Text);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("oops - " + ex.Message);
                    }
                    return txt;
                }
                else
                {
                    return this.txtLink;
                }
            }
            set
            {
                if (this.txtLink.InvokeRequired)
                {
                    this.txtLink.BeginInvoke(
                        new MethodInvoker(
                        delegate() { _txtLink = value; }));
                }
                else
                {
                    this.txtLink = value;
                }
            }
        }

If I use the get accessor I get a null reference exception :( (I have not yet tried the set).

A bit of background:
+ This is being developed using the Compact Framework 2.0 (hence the declaration of MethodInvoker).
+ The calling thread is being created using the OpenNETCF LargeIntervalTimer object.

Any ideas?? What am I doing wrong? I'm a bit of a novice when it comes to threading techniques...

Cheers :)
Rob
 
I posted an FAQ about a responsive UI. faq732-7259
A third approach is to use an EventAggregator in the pub/sub architecture. here is a thread on the subject. the discussion does focus around some specific frameworks, but the concepts of how to approach this are universal.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
Top man!
I followed the first example in your FAQ and got it working!

I did a little modding to get it working by adding:

Code:
public delegate void Action();

And making the Perform call as follows:

Code:
Perform.ThisAction(delegate() { txtLink.Visible = true; }).Against(this);

I wasn't able to do this:
Code:
TextBox _txtLink = null;
Perform.ThisAction(delegate() { _txtLink = txtLink; }).Against(this);
MessageBox.Show(_txtLink.Text);

But was able to do this:
Code:
string sText = String.Empty;
Perform.ThisAction(delegate() { sText = txtLink.Text; }).Against(this);
MessageBox.Show(sText);

Cheers
Rob :)
 
rob, I'm glad my example helpled :) Did you take a look at the AsynchronousExecutor code example? this is alot cleaner and easier to maintain than MethodInvoker on every UI control you want to update.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
Hi Jason. I did look at the AsynchronousExecutor example, but I couldn't understand it :$ The ISynchronizeInvoke (I had to change it to Control for the Compact Framework) made more sense to me so I used that.

Cheers
Rob
 
what doesn't make sense with the executor? I may be able to explain the confusing parts better.

here are some key elements which may help:
delegates are blocks of code. it's like holding a function in a variable. this is the most basic form of functional programming.

because we are using delegates we can defer the execution. in other words where we define the function and when we execute the function are 2 distinct points in the system.

Action and Func<Action> are delegates. I'm assuming you are not using .net 3.5 which is why you needed to define Action. here are there signatures
Code:
public delegate void Action();
public delegate Action Func();
the Executor utilizes ThreadPool.QueueUserWorkItem which is part of System.Threading namespace. this static function pushes work off to background threads.

the Executor also utilizes the abstract class SynchronizationContext. there are at least 2 implementations of this: WinForms and WPF. there may be others as well. this object is responsible for marshaling calls from a background thread onto the UI thread. the difference between SynchronizationContext and ISynchronizeInvoke is scope. SynchronizationContext works with all objects on the UI thread while ISynchronizeInvoke works with a single control. (I have a feeling SynchronizationContext utilizes ISynchronizeInvoke under the hood). when you need to update multiple controls at once from a background thread SynchronizationContext is the preffered approach.

ok, so all that leaves is how the Executor uses these objects in combination.

First it marshals the Func delegate to a background thread.
this is all the work you want to do in the background: remote calls, file IO, long running processes, etc. the result of this will return value(s) that you want to display to the user using control(s) on the UI. the SynchronizationContext will push the result of the Func<> back to the UI thread.

where this become very useful is when you want to update multiple controls at once. say you have a grid with a current status message. with ISyncInvoke you would need to update each row/cell and the current message label individually. with a SyncContext you pass all the code in one delegate.

here is an example of what the delegates might look like use 2.0 syntax
Code:
Func<Action> workDoneOnBackgroundThread = delegate() {
      DataTable results = QueryDatabase();
      string statusMessage = GetStatusOfSystemAfterQueryingDatabase();

      Action workDoneOnMainUiThread = delegate(){
           MyGridView.DataSource = results;
           MyLabel.Text = statusMessage; 
        };

      return workDoneOnMainUiThread;
   };

Executor.Execute(workDoneOnBackgroundThread);
if we were to flatten all this code together it would look like this
Code:
ThreadPool.QueueUserWorkItem(delegate (object item) {
   DataTable results = QueryDatabase();
   string statusMessage = GetStatusOfSystemAfterQueryingDatabase();

   synchronizationContext.Send(delegate (object callBack) {
       MyGridView.DataSource = results;
       MyLabel.Text = statusMessage; 
   }, null);
});
Off the top of my head the delegates in this last code sample may not be object, but some other type. writing 2.0 code from memory:)

by using the executor object we can encapsulate the ulgy parts of thread marshaling into a reusable object. this is the same concept behind encapsulating the ISyncInvoker into the Preform.ThisAction().Against();

last, but not least, this may not be an option of the compact framework. i'm sure a quick google search about threadpool and synchronizationContext would answer that.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top