Para .NET 2.0, aquí hay un buen código que escribí que hace exactamente lo que quieres y funciona para cualquier propiedad en Control
:
private delegate void SetControlPropertyThreadSafeDelegate(
Control control,
string propertyName,
object propertyValue);
public static void SetControlPropertyThreadSafe(
Control control,
string propertyName,
object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate
(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(
propertyName,
BindingFlags.SetProperty,
null,
control,
new object[] { propertyValue });
}
}
Llámalo así:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Si está utilizando .NET 3.0 o superior, podría volver a escribir el método anterior como un método de extensión de la Control
clase, que luego simplificaría la llamada a:
myLabel.SetPropertyThreadSafe("Text", status);
ACTUALIZACIÓN 10/05/2010:
Para .NET 3.0, debe usar este código:
private delegate void SetPropertyThreadSafeDelegate<TResult>(
Control @this,
Expression<Func<TResult>> property,
TResult value);
public static void SetPropertyThreadSafe<TResult>(
this Control @this,
Expression<Func<TResult>> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;
if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(
propertyInfo.Name,
propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>
(SetPropertyThreadSafe),
new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(
propertyInfo.Name,
BindingFlags.SetProperty,
null,
@this,
new object[] { value });
}
}
que usa expresiones LINQ y lambda para permitir una sintaxis mucho más limpia, simple y segura:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Ahora no solo se verifica el nombre de la propiedad en tiempo de compilación, el tipo de propiedad también lo es, por lo que es imposible (por ejemplo) asignar un valor de cadena a una propiedad booleana y, por lo tanto, causar una excepción en tiempo de ejecución.
Desafortunadamente, esto no impide que nadie haga cosas estúpidas como pasar Control
la propiedad y el valor de otra persona, por lo que lo siguiente se compilará felizmente:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Por lo tanto, agregué las comprobaciones de tiempo de ejecución para garantizar que la propiedad transferida realmente pertenezca a la Control
que se llama al método. No es perfecto, pero sigue siendo mucho mejor que la versión .NET 2.0.
Si alguien tiene más sugerencias sobre cómo mejorar este código para la seguridad en tiempo de compilación, ¡comente!