Per .NET 2.0, ecco un bel po 'di codice che ho scritto che fa esattamente quello che vuoi e funziona per qualsiasi proprietà su un 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 });
}
}
Chiamalo così:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Se si utilizza .NET 3.0 o versione successiva, è possibile riscrivere il metodo sopra come metodo di estensione della Control
classe, che semplificherebbe quindi la chiamata a:
myLabel.SetPropertyThreadSafe("Text", status);
AGGIORNAMENTO 05/10/2010:
Per .NET 3.0 dovresti usare questo codice:
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 });
}
}
che utilizza le espressioni LINQ e lambda per consentire una sintassi molto più chiara, semplice e sicura:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Non solo il nome della proprietà ora viene verificato al momento della compilazione, ma anche il tipo della proprietà, quindi è impossibile (ad esempio) assegnare un valore di stringa a una proprietà booleana e quindi causare un'eccezione di runtime.
Sfortunatamente ciò non impedisce a nessuno di fare cose stupide come passare in Control
proprietà e valore di un altro, quindi compileranno felicemente:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Quindi ho aggiunto i controlli di runtime per garantire che la proprietà passata appartenga effettivamente a Control
cui viene chiamato il metodo. Non perfetto, ma comunque molto meglio della versione .NET 2.0.
Se qualcuno ha ulteriori suggerimenti su come migliorare questo codice per la sicurezza in fase di compilazione, si prega di commentare!