Pour .NET 2.0, voici un joli morceau de code que j'ai écrit qui fait exactement ce que vous voulez, et fonctionne pour n'importe quelle propriété sur 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 });
}
}
Appelez ça comme ceci:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Si vous utilisez .NET 3.0 ou supérieur, vous pouvez réécrire la méthode ci-dessus en tant que méthode d'extension de la Control
classe, ce qui simplifierait alors l'appel à:
myLabel.SetPropertyThreadSafe("Text", status);
MISE À JOUR 05/10/2010:
Pour .NET 3.0, vous devez utiliser ce code:
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 });
}
}
qui utilise les expressions LINQ et lambda pour permettre une syntaxe beaucoup plus propre, plus simple et plus sûre:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Non seulement le nom de la propriété est maintenant vérifié au moment de la compilation, mais le type de la propriété l'est également, il est donc impossible (par exemple) d'attribuer une valeur de chaîne à une propriété booléenne, et donc de provoquer une exception d'exécution.
Malheureusement, cela n'empêche personne de faire des choses stupides telles que transmettre Control
la propriété et la valeur d'un autre, donc les éléments suivants seront joyeusement compilés:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Par conséquent, j'ai ajouté les vérifications d'exécution pour garantir que la propriété transmise appartient réellement à la propriété Control
laquelle la méthode est appelée. Pas parfait, mais toujours beaucoup mieux que la version .NET 2.0.
Si quelqu'un a d'autres suggestions sur la façon d'améliorer ce code pour la sécurité à la compilation, veuillez commenter!