Click here to Skip to main content
Click here to Skip to main content

Tagged as

Extension of safeInvoke

, 13 Jul 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Extention of the 'safeInvoke' class by Sergiu Josan

Introduction

Modern applications make use of multithreading. One of the reasons is that applications get unresponsive if compute intensive code is executed in the UI-thread. Which is, of course, terrible for the user-experience. Executing such code in a separate (worker) thread solves this problem, but at the same time introduces new problems.

In this article, I will discuss one of these problems: From time to time, there has to be an interaction between the worker and the UI thread, e.g.: the text of a textbox should be updated or a button should be enabled or disabled. This must be done thread-safely, called thread-safe invocation.

Invoke Thread-safe

Thread-safe invocation requires two things: a delegate method and a method that checks if invoke is required and calls the delegate method if true. For example, to set the Button.enabled = false on a Button.click event in a safe-thread way:

// declare a button-enabled delegate  
delegate void buttonEnabled(bool enabled); 

//handles setbutton click event
private void setButton_Click(object sender, EventArgs e)
{
	buttonEnabledInvoke(false)
}  

//method that safe-invokes the button enabled property 
private void buttonEnabledInvoke(bool enabled)
{
	if (setButton.InvokeRequired) //if invoke required ->invoke and call same method
		setButton.Invoke(new buttonEnabled(buttonEnabledInvoke), enabled);
	else // else set enabled directly
		setButton.Enabled = enabled;
}

Of course, it is easy to make delegates and methods in such a way for each property, but the disadvantage is that it requires a lot of code which seems quite similar.

The article by Sergiu-Josan describes a solid way to invoke thread safely with one class. The safeInvoke class in this article has three public methods. In this example, we get and set the Button.Enabled property and call the Button.Performclick() method:

// get a property value in a safe-thread way
bool enabled = (bool)SafeInvoke.GetPropertyValue(setButton, "Enabled"));

// set a property value in a safe-thread way
SafeInvoke.SetPropertyValue(setButton, "Enabled", false);

// Invoke a method in a safe-thread way
 SafeInvoke.InvokeMethod(setButton, "PerformClick");

So, this class handles setting and getting all properties and invoking methods of UI-controls in a thread-safe manner.

Shortcomings

The original code could not handle 'controls' like 'ToolStripMenuItem', because this item cannot be cast into a control.

Solution

Use Form.Invoke instead of Control.Invoke in these cases.

The Extended Code

The three previous public functions all have an overload function now. In this example, we get and set ToolStripMenuItem.Enabled property and call the ToolStripMenuItem.PerformClick() method:

// get a property value in a safe-thread way
bool enabled = (bool)SafeInvoke.GetPropertyValue((Form)this, 
	demoToolStripMenuItem, "Enabled"));

// set a property value in a safe-thread way
SafeInvoke.SetPropertyValue((Form)this, 
	demoToolStripMenuItem,"Enabled", false);

// Invoke a method in a safe-thread way
 SafeInvoke.InvokeMethod((Form)this, 
	demoToolStripMenuItem, "PerformClick"); 

Now follows an example of the getPropertyValue method. In essence, it works the same way as the manual thread-safe invocation method described above. It has a delegate and a method that checks whether invocation is required. However, this function can be used for all controls and all properties. Two cases are implemented in the code:

  • formControl == null
  • formControl != null

In the first case, the Control.Invoke method is used and in the second case, the Form.Invoke method is used in order to handle special controls like 'ToolStripMenuItem':

//delegate for getting a property
private delegate object PropertyGetInvoker
	(Control control, object formControl, string propertyName);

//public method for getting a property 
public static object GetPropertyValue
	(Control control, object formControl, string propertyName)
{
	if (control != null && !string.IsNullOrEmpty(propertyName))
	{
		if (control.InvokeRequired) //if invoke required -> invoke
		{
			return control.Invoke(new PropertyGetInvoker
			(GetPropertyValue), control,formControl, propertyName);
		}
		else //else getting the property directly
		{
			PropertyInfo propertyInfo = GetProperty
				(control, formControl, propertyName);
			
			if (propertyInfo != null) //property exists
			{
				if (propertyInfo.CanRead) //property can be read
				{
					if (formControl != null) //special 
							       //'control' 
						return propertyInfo.GetValue
						(formControl, null); //return 
								// the value
					else //normal control
						return propertyInfo.GetValue
							(control, null);
				}
				else 	// throw an exception that the 
					// property cannot be read
				{ 
					if (formControl != null) 	//special 
								//'control' 
						throw new Exception
						(formControl.GetType().ToString() 
						+ "." + propertyName + " 
						is write-only property.");
					else //normal control
						throw new Exception
						(control.GetType().ToString() 
						+ "." + propertyName + " 
						is write-only property.");
				}
			}

			return null;
		}
	}
	else
	{
		throw new ArgumentNullException(); //one of the arguments is null
	}
}

// getting property info
private static PropertyInfo GetProperty
	(object control, object formControl, string propertyName)
{
	if (control != null && !string.IsNullOrEmpty(propertyName))
	{
		PropertyInfo propertyInfo;
		if (formControl != null) //special 'control' 
			propertyInfo = 
				formControl.GetType().GetProperty(propertyName);
		else //normal control
			propertyInfo = control.GetType().GetProperty(propertyName);

		if (propertyInfo == null) //property does not exist -> throw exception
		{
			throw new Exception(control.GetType().ToString() 
				+ " does not contain '" + 
				propertyName + "' property.");
		}

		return propertyInfo;
	}
	else
	{
		throw new ArgumentNullException(); //one of the arguments is null
	}
} 

History

  • 13th July, 2010: Version 1.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

vicoB
Other Radboud University Nijmegen
Netherlands Netherlands
I completed my master in electrical engineering at the University of Twente in the Netherlands in 2007. My special interest is software/hardware application which can be used for health purposes.
 
Currently, I am PhD-student at the Radboud University in Nijmegen, The Netherlands. I am involved in a project in which an monitoring and feedback application is developed for daily-life usage.
 
My personal interests: bicycling, salsa dancing and gardening

Comments and Discussions

 
GeneralRoom for improvement Pinmemberreflex@codeproject14-Jul-10 7:04 
GeneralThis is not an Extension PinmemberPaw Jershauge13-Jul-10 22:16 
GeneralGood job PinmemberSergiu Josan13-Jul-10 12:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141216.1 | Last Updated 13 Jul 2010
Article Copyright 2010 by vicoB
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid