我知道之前已经问过这个问题,但我正在寻找一种方法:
这是我到目前为止所做的,但我想删除Windows窗体引用。有什么想法吗?
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();
}
}
上述类可能会以这种方式使用:
SafeInvoke _safeInvoker = new SafeInvoke();
void SafeClearItems()
{
_safeInvoker.Invoke(delegate
{
listView1.Items.Clear();
});
}
如何删除SafeInvoke类中的System.Windows.Forms.Control但保留相同的功能?
答案 0 :(得分:90)
您还可以使用扩展方法和lambdas来使代码更清晰。
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);
}
}
}
所以现在你可以在任何ISynchronizeInvoke上使用InvokeEx
,并且能够访问实现类的属性和字段。
this.InvokeEx(f => f.listView1.Items.Clear());
答案 1 :(得分:10)
使用ISynchronizeInvoke
代替Control
。这是Control
与Invoke/BeginInvoke/EndInvoke/InvokeRequired
实现的接口。
另一种方法是使用SynchronizationContext.Current
- 这是BackgroundWorker
使用的内容,我相信。
答案 2 :(得分:7)
现在很容易调用 例如假设我们想要调用Label(lblVal)来获取txtVal的值
lblVal.Invoke((MethodInvoker)delegate{lblVal.Text = txtVal.Text;});
......它很简单:D
答案 3 :(得分:4)
这是在VB.net中,与Samuel的答案非常相似。根据您是否需要子程序或函数以及是否有参数,我有四个重载。为更多参数添加更多重载会很容易。 VB.Net能够推断出类型。
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
用法(在后台工作中运行):
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
答案 4 :(得分:3)
这是我使用的Samuel的答案的VB等效代码。注意我实际上有2个扩展功能,但我必须承认我不知道他们为什么在那里。我多年前复制了我的C#版本(也许来自这个网站)并且它有两个扩展功能,但由于什么原因,我不完全理解。我只是复制了它以及如何使用它,我一半了解所有那些带有这些复杂功能的“引擎盖”。
#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
C#版本是:
#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
快乐的编码!