UI: Beating UI locks through simple threading techniques

Posted: 2009-12-18 in .Net

UI and Threads
There are some basic things one should do when programming user Interfaces.
Using worker style threads is probably the most important, without threads your UI will lock and freeze and the User will in turn hate you.

Invoke using MethodInvoker
Handling threads requires the use of InvokeRequired, as a rule of thumb any public method in a UI class should be carefully considered for a wrapped Invoke statement to avoid thread exceptions.

Invoke example:

///
 /// Enables all controls.
 ///
 /// if set to true [enable].
 internal void EnableAllControls(bool enable)
 {
 if (InvokeRequired)
 {
 Invoke(new MethodInvoker(() => EnableAllControls(enable)));
 }
 else
 {
 _treeView.Enabled = enable;
 }
 }

This technique should is useful when you create a controls for use with forms or even forms that are controlled from other business rules. It is also used heavily in the MVP pattern due to the massive threading /event handling control usually used in that architecture.

Side note: I personally use a hybrid Model with Presenter and Controller class merged, i find it best of both worlds with few negatives.

How To Handle The UI Calling Business Code
First never make the assumption that calling public methods will be instant this is how the UI locks, (Async methods aside).

With that said here are two basic design options:
1. Have the UI class register to a business event, have the business class for public Async methods always create a thread, name the thread (always name your threads it makes debugging easier) and then run it. When the threaded method returns it raises the event the UI listened too with the required data.

2. A more MVP style. The view/UI class raises an event indicating it requires something. The business/presenter class has a copy of the view instance. It listens for events from the view instance. When an event is received then starts a new thread which coorordinates the nessasary actions to gather the data. When done it access its instance and sets the information using a method in the view (or event). If a method is used the method MUST have thread/invoke handling (see above).

Both techniques have their disadvantages. Both have their advantages.
Technique 1
Disadvantage
Requires knowledge of its business. This makes extending/upgrading/cod reuse much harder (think coupling and cohesion).
Advantage
You have easy access to all your UI component, so for an enable you just reference the list of buttons and set enabled. The code above is used to enable a control, this would not be required in technique 1 as you get your return within the UI class.

Technique 2
Disadvantage
Your UI must expose a lot of commonly used ui component control (not the controls themself) the above code is a good indication of this. The overhead can be justified by better code hiding, which is excellent for code reuse.
Tis a bit weird when controlling the controller. You create the UI reference then pass it to a preseneter instance, which is used for the control. Weird for first time users.
Advantage
The UI become much more decoupled (as noted above) usually this means a very few changes are required for you to be able to recycle the code into other projects. It is also much clear what seequence of events are supposed to do as the presenter is quiet cluttered free and very easy to read. Much easier for future development.

Example of option 2
View control with a presenter/controller class

 //ui controller which contains a UI treeview component
 public partial class MenuTree : UserControl
 {
 internal event EventHandler NodeExpanded;

private void RaiseNodeExpandedEvent(ITreeItem treeItem)
 {
 if (NodeExpanded != null)
 {
 NodeExpanded(this, new TreeItemEventArgs(treeItem));
 }
 }

//method called when the before expand event is raised from the inbuilt .NET treeView component
 private void TreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
 {
 ITreeItem item = e.Node.Tag as ITreeItem;
 if (item != null)
 {
 RaiseNodeExpandedEvent(item);
 }
 }

// Adds the children.
 internal void AddChildren(ITreeItem parentItemWithChildren)
 {
 if (InvokeRequired)
 {
 Invoke(new MethodInvoker(() => AddChildren(parentItemWithChildren)));
 }
 else
 {
 //add the children to the correct position
 //code not provided
 }

}

//presenter to coordinate UI requests and business request
 // in the holding form you would have the menutree ui control added to the form and a instance of the presenter... all controller business goes through the presenter
 public class MenuTreePresenter
 {
 //set during construction time
 protected MenuTree View { get; private set; }

public MenuTreePresenter(MenuTree view)
 {
 View = view;
 View.NodeExpanded += View_NodeExpanded;
 }

//Handles the NodeExpanded event of the View control.
 void View_NodeExpanded(object sender, TreeItemEventArgs e)
 {
 if (e != null)
 {
 Thread th = new Thread(LoadNode)
 {
 Name = "ExpandingChildNode"
 };
 th.Start(e.TreeItem);
 }
 }

//Loads the node.
 private void LoadNode(object treeItem)
 {
 if (!(treeItem is ITreeItem))
 {
 return;
 }
 ITreeItem parent = treeItem as ITreeItem;
 // do something which populates the ITreeItem children
 parent.Children.AddRange(DataFacadeFactory.LoadChildren(parent.id));

//Tell the view that we ahve the information time to do wview things with it

View.AddChildren(parent);
 }
 }

So there you have it, a non locking UI example using a weird presenter view architecture.
Ignoreing the architecture, if you following these simply rules your UI will never lock 🙂
Cheers
Choco

Advertisements

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