OWASP O2 Platform Blog

Selecting a TabControl page in a Thread safe way

One of the most annoying (and bug generator) items of programing with WinForms is its amost complete lack of Thread Safety. Basicaly most Controls (TextBox, Button, TabPage) will check if the current caller is on the thread that created that control in the first place and if not, an exception will be thrown. Also, most controls have funcionality that depends on being on the right thread (for example ‘the Drag and Drop registration’ case shown below)

This was a massive problem in the initial stages of the development of the ‘O2 Quick Development Gui’ since the whole thing is multi-threaded where GUIs tend do be dynamically created by scripts running on another thread, for example:

panel.clear().add_TreeView().add_Node("1st node", "2nd node"). insert_Left().add_TextBox().set_Text("some text");

This script would not be possible if O2 didn’t had extensive multi-thread support for manipulating WinForms controls.

To see this problem in actions, lets look at a TabControl with a TableList O2 control.

Start by creating an instance of it with three tabs (with a TextBox, a WebBrowser and Table List):

var topPanel = panel.clear().add_Panel();
var tabControl = topPanel.add_TabControl();
tabControl.add_Tab("A Text Area").add_TextArea("some Content");
tabControl.add_Tab("A Browser").add_WebBrowser_Control().open("<a href="http://www.google.com/">http://www.google.com</a>");
tabControl.add_Tab("A Table List").add_TableList();
tabControl.SelectedIndex = 2;

Now, lets say that you wanted to automatically select the 3nd tab (the one with the TableList control)

Ideally we should be able to just call the TabControl SelectedIndex property setter:

tabControl.SelectedIndex = 2;

But if we do that, we will get following bug (which will crash the UI)

The problem is caused by the fact that the thread that triggered the TabControl selection is different from the the one that was used to create the TabControl items.

There are two ways to deal with this in O2.

The first is to make the call to the SelectedIndex inside the thread that was used to create the TabControl.

This can easily be done using the {controlObject}.invokeOnThread(()=>{ … }); extension method. For example

tabControl.invokeOnThread(
    ()=>{
            tabControl.SelectedIndex = 2;
        });

which will now work without errors:

Note that since there is only one instruction inside the Lambda method, that can also be written as:

tabControl.invokeOnThread(()=>  tabControl.SelectedIndex = 2 );

The better solution is to wrap this code inside an extension method:

public static TabControl selectedIndex(this TabControl tabControl, int index)
        {
            return (TabControl)tabControl.invokeOnThread(
                                            ()=>{
                                                    tabControl.SelectedIndex = index;
                                                    return tabControl;
                                                });
        }

so that we can make this tab page change by just calling:

tabControl.selectedIndex(2)

July 18, 2011 - Posted by | .NET

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: