/* hrmilo 2009
* Thread-safety for System.Windows.Forms
* For more information:
* "How to: Make Thread-Safe Calls to Windows Forms Controls"
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Reflection;
namespace ThreadSafety
{
/// <summary>
/// Class demonstrates a utility to act upon Forms.Control properties.
/// Can be easily adopted to employ Forms.Control methods.
/// Example: "Text[ctrl]" get or set ctrl.Text using ctrl.Invoke if required.
/// </summary>
static class WFControl
{
/// <summary>
/// Perform the given Action delegate on behalf of the given System.Windows.Forms.Control.
/// Use this method when no return type is expected.
/// </summary>
/// <param name="ctrl">The Forms.Control to invoke upon.</param>
/// <param name="action">The control property or method to invoke.</param>
public static void PerformInvoke(Control ctrl, Action action)
{
if (ctrl.InvokeRequired)
{
ctrl.Invoke(action);
}
else
{
action();
}
}
/// <summary>
/// Perform the given Func delegate on behalf of the given System.Windows.Forms.Control.
/// Use this method when a return type is expected.
/// </summary>
/// <typeparam name="T">The type expected to be returned by the control property or method.</typeparam>
/// <param name="ctrl">The Forms.Control to invoke upon.</param>
/// <param name="action">The control property or method to invoke.</param>
/// <returns></returns>
public static T PerformInvoke<T>(Control ctrl, Func<T> action)
{
return ctrl.InvokeRequired ? (T)ctrl.Invoke(action) : action();
}
/// <summary>
/// Convenient generic access to a Forms.Control property.
/// The control is passed as an indexer argument.
/// Example: "Text[ctrl]" will get or set the ctrl.Text property.
/// </summary>
/// <typeparam name="T">The control property's type</typeparam>
public class Property<T>
{
/// <summary>
/// A Func delegate that employs the generic variant of PerformInvoke to emulate a property get method returning type T.
/// </summary>
Func<Control, T> getter;
/// <summary>
/// An Action delegate that employs the non-generic variant of PerformInvoke to emulate a property set method taking type T.
/// </summary>
Action<Control, T> setter;
/// <summary>
/// Func that performs for getter and setter both.
/// bool is false to invoke the getter, true to invoke the setter.
/// Control is the Forms.Control acted upon.
/// Type T is the argument type returned from the getter or passed to the setter.
/// </summary>
Func<bool, Control, T, T> accessor;
/// <summary>
/// Constructor with individualized get and set delegates
/// </summary>
/// <param name="_get">Func delegate to retrieve a control property</param>
/// <param name="_set">Action delegate to set a control property</param>
public Property(Func<Control, T> _get, Action<Control, T> _set)
{
if (_get == null)
{
getter = delegate(Control ctrl) { throw new NotImplementedException(); };
}
else
{
getter = _get;
}
if (_set == null)
{
setter = delegate(Control ctrl, T arg) { throw new NotImplementedException(); };
}
else
{
setter = _set;
}
}
/// <summary>
/// Constructor allowing one delegate to act as both a get delegate and set delegate.
/// Due to the clunky delegate employed, this constructor should not see too much use.
/// </summary>
/// <param name="_accessor">Func delegate providing get and set access to a control property</param>
public Property(Func<bool, Control, T, T> _accessor)
{
if (_accessor == null)
{
getter = delegate(Control ctrl) { throw new NotImplementedException(); };
setter = delegate(Control ctrl, T arg) { throw new NotImplementedException(); };
}
else
{
accessor = _accessor;
getter = delegate(Control ctrl) { return accessor(false, ctrl, default(T)); };//default(T) is ignored
setter = delegate(Control ctrl, T arg) { accessor(true, ctrl, arg); }; //ignores the accessor return value
}
}
/// <summary>
/// Provide convenient and clean way to pass a variety of Forms.Control objects.
/// </summary>
/// <param name="ctrl"></param>
/// <returns></returns>
public T this[Control ctrl]
{
get { return getter(ctrl); }
set { setter(ctrl, value); }
}
}
/// <summary>
/// Predefined Forms.Control properties many users will want to get or set.
/// To provide the look-and-feel of a property, private Property&;;lt;T&;;gt; members are defined and returned through public read-only class properties.
/// This bit of redirection could be eliminated.
/// </summary>
#region standard Control properties
private static Property<string> _text =
new Property<string>(
delegate(Control ctrl) { return PerformInvoke<string>(ctrl, delegate() { return ctrl.Text; }); },
delegate(Control ctrl, string arg) { PerformInvoke(ctrl, delegate() { ctrl.Text = arg; }); }
);
/// <summary>
/// get or set Forms.Control.Text
/// </summary>
public static Property<string> Text
{
get { return _text; }
}
private static Property<object> _tag =
new Property<object>(
delegate(Control ctrl) { return PerformInvoke<object>(ctrl, delegate() { return ctrl.Tag; }); },
delegate(Control ctrl, object arg) { PerformInvoke(ctrl, delegate() { ctrl.Tag = arg; }); }
);
/// <summary>
/// get or set Forms.Control.Tag
/// </summary>
public static Property<object> Tag
{
get { return _tag; }
}
private static Property<string> _name =
new Property<string>(
delegate(Control ctrl) { return PerformInvoke<string>(ctrl, delegate() { return ctrl.Name; }); },
delegate(Control ctrl, string arg) { PerformInvoke(ctrl, delegate() { ctrl.Name = arg; }); }
);
/// <summary>
/// get or set Forms.Control.Name
/// </summary>
public static Property<string> Name
{
get { return _name; }
}
private static Property<bool> _enabled =
new Property<bool>(
delegate(Control ctrl) { return PerformInvoke<bool>(ctrl, delegate() { return ctrl.Enabled; }); },
delegate(Control ctrl, bool arg) { PerformInvoke(ctrl, delegate() { ctrl.Enabled = arg; }); }
);
/* This demonstrates using the accessor delegate rather than separate getter and setter delegates */
//new Property<bool>(
// delegate(bool get_or_set, Control ctrl, bool arg)
// {
// if (get_or_set)
// {
// PerformInvoke(ctrl, delegate() { ctrl.Enabled = arg; }); //perform a set operation
// return false; //return is ignored
// }
// else //performa get operation
// {
// return PerformInvoke<bool>(ctrl, delegate() { return ctrl.Enabled; });
// }
// });
/// <summary>
/// get or set Forms.Control.Enabled
/// </summary>
public static Property<bool> Enabled
{
get { return _enabled; }
}
private static Property<Control.ControlCollection> _controls =
new Property<Control.ControlCollection>(
delegate(Control ctrl) { return PerformInvoke<Control.ControlCollection>(ctrl, delegate() { return ctrl.Controls; }); },
delegate(Control ctrl, Control.ControlCollection arg) { PerformInvoke(ctrl, delegate() { throw new NotSupportedException(); }); }
);
/// <summary>
/// get Forms.Control.Controls
/// </summary>
public static Property<Control.ControlCollection> Controls
{
get { return _controls; }
}
//add your own here, or define them outside of this class
#endregion
}
}