Au cours de mon entretien pour un ancien de l'application qui en a bien violé la croix-fil de mise à jour des règles en winforms, j'ai créé l'extension suivante de la méthode comme un moyen de résoudre rapidement illégale des appels lorsque j'ai découvert d'eux:
/// <summary>
/// Execute a method on the control's owning thread.
/// </summary>
/// <param name="uiElement">The control that is being updated.</param>
/// <param name="updater">The method that updates uiElement.</param>
/// <param name="forceSynchronous">True to force synchronous execution of
/// updater. False to allow asynchronous execution if the call is marshalled
/// from a non-GUI thread. If the method is called on the GUI thread,
/// execution is always synchronous.</param>
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
if (uiElement == null)
{
throw new ArgumentNullException("uiElement");
}
if (uiElement.InvokeRequired)
{
if (forceSynchronous)
{
uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
else
{
uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
}
else
{
if (!uiElement.IsHandleCreated)
{
// Do nothing if the handle isn't created already. The user's responsible
// for ensuring that the handle they give us exists.
return;
}
if (uiElement.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
updater();
}
}
Exemple d'utilisation:
this.lblTimeDisplay.SafeInvoke(() => this.lblTimeDisplay.Text = this.task.Duration.ToString(), false);
J'aime la façon dont je peux tirer des fermetures à lire, aussi, bien que forceSynchronous doit être vrai dans ce cas:
string taskName = string.Empty;
this.txtTaskName.SafeInvoke(() => taskName = this.txtTaskName.Text, true);
Je ne doute pas de l'utilité de cette méthode pour la fixation illégale des appels dans l'ancien code, mais qu'un nouveau code?
Est-il bon de conception pour utiliser cette méthode pour mettre à jour l'INTERFACE utilisateur dans un morceau de nouveaux logiciels lorsque vous ne pouvez pas savoir ce thread est de tenter de mettre à jour l'interface utilisateur, ou si de nouveaux Winforms code contiennent généralement un spécifique, dédié méthode appropriée Invoke()
liés à la plomberie pour de telles mises à jour de l'INTERFACE utilisateur? (Je vais essayer d'utiliser l'autre traitement en arrière-plan des techniques, bien sûr, par exemple, BackgroundWorker.)
Il est intéressant de noter cela ne fonctionne pas pour ToolStripItems. Je viens de découvrir qu'elles dérivent directement à partir de Composant au lieu de Contrôle. Au lieu de cela, le contenant ToolStrip
s'invoquer doit être utilisé.
Suivi des commentaires:
Certains commentaires laissent à penser que:
if (uiElement.InvokeRequired)
devrait être:
if (uiElement.InvokeRequired && uiElement.IsHandleCreated)
Considérez les points suivants documentation msdn:
Cela signifie que InvokeRequired peut retourne false si l'Invoquer n'est pas nécessaire (l'appel sur le même thread), ou si le contrôle a été créé sur un thread différent, mais le contrôle de l' la poignée n'a pas encore été créé.
Dans le cas où le contrôle de la poignée de la n'a pas encore été créé, vous devez pas simplement appeler les propriétés, les méthodes,les ou des événements sur le contrôle. Cela pourrait cause le contrôle de la poignée à être créé sur le thread d'arrière-plan, isoler le contrôle sur un fil sans un message de la pompe et de faire le application instable.
Vous pouvez vous protéger contre ce cas, par aussi, en vérifiant la valeur de IsHandleCreated quand InvokeRequired retourne false en cas d'un thread d'arrière-plan.
Si le contrôle a été créé sur un autre fil, mais le contrôle de la poignée n'a pas encore été créé, InvokeRequired
renvoie la valeur false. Cela signifie que si InvokeRequired
retours true
, IsHandleCreated
sera toujours vrai. Tester de nouveau est redondante et incorrect.