Je sais que cette question a déjà été posée, mais je cherche un moyen de:
Voici ce que j'ai jusqu'à présent, mais je souhaite supprimer les références Windows Forms. Des idées?
public delegate void SafeInvokeDelegate(System.Action action);
public class SafeInvoke
{
private readonly System.Windows.Forms.Control _threadControl;
public SafeInvoke()
{
_threadControl = new System.Windows.Forms.Control();
}
public void Invoke(System.Action action)
{
if (_threadControl.InvokeRequired)
_threadControl.Invoke(new SafeInvokeDelegate(Invoke), new object[] {action});
else if (action != null) action();
}
}
La classe ci-dessus peut être utilisée de cette façon:
SafeInvoke _safeInvoker = new SafeInvoke();
void SafeClearItems()
{
_safeInvoker.Invoke(delegate
{
listView1.Items.Clear();
});
}
Comment supprimer le System.Windows.Forms.Control dans la classe SafeInvoke mais conserver les mêmes fonctionnalités?
Vous pouvez également utiliser une méthode d'extension et des lambdas pour rendre votre code beaucoup plus propre.
using System.ComponentModel;
public static class ISynchronizeInvokeExtensions
{
public static void InvokeEx<T>(this T @this, Action<T> action) where T : ISynchronizeInvoke
{
if (@this.InvokeRequired)
{
@this.Invoke(action, new object[] { @this });
}
else
{
action(@this);
}
}
}
Alors maintenant, vous pouvez utiliser InvokeEx
sur n'importe quel ISynchronizeInvoke et être en mesure d'accéder aux propriétés et aux champs de la classe d'implémentation.
this.InvokeEx(f => f.listView1.Items.Clear());
Utilisez ISynchronizeInvoke
au lieu de Control
. C'est l'interface que Control
implémente avec Invoke/BeginInvoke/EndInvoke/InvokeRequired
.
Une alternative consiste à utiliser SynchronizationContext.Current
- qui est ce que BackgroundWorker
utilise, je crois.
De nos jours, il est facile d'invoquer.
par exemple. Disons que nous voulons invoquer un Label (lblVal) pour obtenir la valeur de txtVal
lblVal.Invoke((MethodInvoker)delegate{lblVal.Text = txtVal.Text;});
... c'est aussi simple que ça: D
Le voici dans VB.net, très similaire à la réponse de Samuel. J'ai quatre surcharges selon que vous voulez un sous-programme ou une fonction et qu'il y ait ou non un paramètre. Il serait facile d'ajouter plus de surcharges pour plus de paramètres. VB.Net est capable d'inférer les types.
Module ISynchronizeInvokeExtensions
Public Delegate Function GenericLambdaFunctionWithParam(Of InputType, OutputType)(ByVal input As InputType) As OutputType
Private Delegate Function InvokeLambdaFunctionCallback(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType
Public Function InvokeEx(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType
If c.InvokeRequired Then
Dim d As New InvokeLambdaFunctionCallback(Of InputType, OutputType)(AddressOf InvokeEx)
Return DirectCast(c.Invoke(d, New Object() {f, input, c}), OutputType)
Else
Return f(input)
End If
End Function
Public Delegate Sub GenericLambdaSubWithParam(Of InputType)(ByVal input As InputType)
Public Sub InvokeEx(Of InputType)(ByVal s As GenericLambdaSubWithParam(Of InputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke)
InvokeEx(Of InputType, Object)(Function(i As InputType) As Object
s(i)
Return Nothing
End Function, input, c)
End Sub
Public Delegate Sub GenericLambdaSub()
Public Sub InvokeEx(ByVal s As GenericLambdaSub, ByVal c As System.ComponentModel.ISynchronizeInvoke)
InvokeEx(Of Object, Object)(Function(i As Object) As Object
s()
Return Nothing
End Function, Nothing, c)
End Sub
Public Delegate Function GenericLambdaFunction(Of OutputType)() As OutputType
Public Function InvokeEx(Of OutputType)(ByVal f As GenericLambdaFunction(Of OutputType), ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType
Return InvokeEx(Of Object, OutputType)(Function(i As Object) f(), Nothing, c)
End Function
End Module
Utilisation (exécutez ceci dans un backgroundworker):
InvokeEx(Sub(x As String) Me.Text = x, "foo", Me) 'set form title to foo
InvokeEx(AddressOf MsgBox, Me.Text, Me)
InvokeEx(Sub() Me.Text &= "!", "foo", Me) 'append "!" to form title
InvokeEx(AddressOf MsgBox, Me.Text, Me)
Dim s As String = InvokeEx(Function() Me.Text, Me) & "bar" 'get form title to backgorundworker thread
InvokeEx(AddressOf MsgBox, s, Me) 'display the string from backgroundworker thread
voici le VB code équivalent à la réponse de Samuel que j'utilise. remarquez que j'ai en fait 2 fonctions d'extensions, mais je dois admettre que je ne sais pas pourquoi elles sont là. j'ai copié ma version C # il y a des années (peut-être sur ce site) et il avait les deux fonctions d'extension, mais pour quelle raison, je ne comprends pas complètement. Je viens de le copier et comment l'utiliser, et je comprends à moitié tout ce qui se passe `` sous le capot '' avec ces fonctions compliquées.
#Const System_ComponentModel = True
#Const System_Drawing = False
Option Compare Binary
Option Explicit On
Option Strict On
Imports System.Collections
Imports System.Runtime.CompilerServices ' for Extension() attribute
Imports System.Text
#If System_ComponentModel Then
Imports System.ComponentModel
#End If
#If System_Drawing Then
Imports System.Drawing
#End If
Public Module MyExtensions
' other #Region blocks are removed. i use many in my Extensions
' module/class. the below code is only the 2 relevant extension
' for this 'SafeInvoke' functionality. but i use other regions
' such as "String extensions" and "Numeric extensions". i use
' the above System_ComponentModel and System_Drawing compiler
' directives to include or exclude blocks of code that i want
' to either include or exclude in a project, which allows me to
' easily compare my code in one project with the same file in
' other projects to syncronise new changes across projects.
' you can scrap pretty much all the code above,
' but i'm giving it here so you see it in the full context.
#Region "ISynchronizeInvoke extensions"
#If System_ComponentModel Then
<Extension()>
Public Function SafeInvoke(Of T As ISynchronizeInvoke, TResult)(isi As T, callFunction As Func(Of T, TResult)) As TResult
If (isi.InvokeRequired) Then
Dim result As IAsyncResult = isi.BeginInvoke(callFunction, New Object() {isi})
Dim endresult As Object = isi.EndInvoke(result)
Return DirectCast(endresult, TResult)
Else
Return callFunction(isi)
End If
End Function
''' <summary>
''' This can be used in VB with:
''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = "This is my new Text value.")
''' or:
''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = myTextStringVariable)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="isi"></param>
''' <param name="callFunction"></param>
''' <remarks></remarks>
<Extension()>
Public Sub SafeInvoke(Of T As ISynchronizeInvoke)(isi As T, callFunction As Action(Of T))
If isi.InvokeRequired Then
isi.BeginInvoke(callFunction, New Object() {isi})
Else
callFunction(isi)
End If
End Sub
#End If
#End Region
' other #Region blocks are removed from here too.
End Module
Et la version C # est:
#define System_ComponentModel
#undef System_Drawing
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if System_ComponentModel
using System.ComponentModel;
#endif
#if System_Drawing
using System.Drawing;
#endif
namespace MyCompany.Extensions
{
static partial class MyExtensions
{
// other #Region blocks are removed. i use many in my Extensions
// module/class. the below code is only the 2 relevant extension
// for this 'SafeInvoke' functionality. but i use other regions
// such as "String extensions" and "Numeric extensions". i use
// the above System_ComponentModel and System_Drawing compiler
// directives to include or exclude blocks of code that i want
// to either include or exclude in a project, which allows me to
// easily compare my code in one project with the same file in
// other projects to syncronise new changes across projects.
// you can scrap pretty much all the code above,
// but i'm giving it here so you see it in the full context.
#region ISynchronizeInvoke extensions
#if System_ComponentModel
public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> callFunction) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
{
IAsyncResult result = isi.BeginInvoke(callFunction, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return callFunction(isi);
}
/// <summary>
/// This can be used in C# with:
/// txtMyTextBox.SafeInvoke(d => d.Text = "This is my new Text value.");
/// or:
/// txtMyTextBox.SafeInvoke(d => d.Text = myTextStringVariable);
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="isi"></param>
/// <param name="callFunction"></param>
public static void SafeInvoke<T>(this T isi, Action<T> callFunction) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(callFunction, new object[] { isi });
else
callFunction(isi);
}
#endif
#endregion
// other #Region blocks are removed from here too.
} // static class MyExtensions
} // namespace
Bon codage!