Pour .NET 2.0, voici un petit bout de code que j'ai écrit et qui fait exactement ce que vous voulez, et qui fonctionne pour n'importe quelle propriété d'un objet de type 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 });
}
}
Disons-le comme ça :
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Si vous utilisez .NET 3.0 ou une version plus récente, vous pouvez réécrire la méthode ci-dessus en tant que méthode d'extension de la méthode Control
ce qui simplifierait 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 LINQ et les expressions lambda pour permettre une syntaxe beaucoup plus propre, plus simple et plus sûre :
// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);
Non seulement le nom de la propriété est désormais vérifié à la compilation, mais le type de la propriété l'est également, de sorte qu'il est impossible (par exemple) d'attribuer une valeur de type chaîne à une propriété de type booléen et de provoquer ainsi une exception à l'exécution.
Malheureusement, cela n'empêche pas les gens de faire des choses stupides, comme de passer dans un autre pays. Control
de la propriété et de la valeur, de sorte que le texte suivant se compilera sans problème :
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
C'est pourquoi j'ai ajouté des contrôles d'exécution afin de m'assurer que la propriété transmise appartient bien à l'élément Control
sur laquelle la méthode est appelée. Ce n'est pas parfait, mais c'est 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, merci de commenter !
26 votes
La classe BackgroundWorker de .net 2.0+ n'est-elle pas prévue pour cela ? Elle prend en compte les threads de l'interface utilisateur. 1. Créer un BackgroundWorker 2. Ajoutez deux délégués (un pour le traitement et un pour l'achèvement).
14 votes
Peut-être un peu tard : codeproject.com/KB/cs/Threadsafe_formupdating.aspx
4 votes
Voir la réponse pour .NET 4.5 et C# 5.0 : stackoverflow.com/a/18033198/2042090
5 votes
Cette question ne s'applique pas à l'interface graphique Gtk#. Pour Gtk#, voir ce et ce réponse.
3 votes
Attention : les réponses à cette question sont maintenant un fouillis d'OT ("voici ce que j'ai fait pour mon application WPF") et d'artefacts historiques .NET 2.0.
0 votes
Poste connexe - Forcer la mise à jour de l'interface graphique à partir du fil d'interface utilisateur