-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathSafeInvoker.cs
135 lines (125 loc) · 4.38 KB
/
SafeInvoker.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using System;
using System.Reflection;
using System.Threading;
namespace WinFormAnimation
{
/// <summary>
/// The safe invoker class is a delegate reference holder that always
/// execute them in the correct thread based on the passed control.
/// </summary>
public class SafeInvoker
{
private MethodInfo _invokeMethod;
private PropertyInfo _invokeRequiredProperty;
private object _targetControl;
/// <summary>
/// Initializes a new instance of the SafeInvoker class.
/// </summary>
/// <param name="action">
/// The callback to be invoked
/// </param>
/// <param name="targetControl">
/// The control to be used to invoke the callback in UI thread
/// </param>
public SafeInvoker(Action action, object targetControl) : this((Delegate) action, targetControl)
{
}
/// <summary>
/// Initializes a new instance of the SafeInvoker class.
/// </summary>
/// <param name="action">
/// The callback to be invoked
/// </param>
/// <param name="targetControl">
/// The control to be used to invoke the callback in UI thread
/// </param>
protected SafeInvoker(Delegate action, object targetControl)
{
UnderlyingDelegate = action;
if (targetControl != null)
{
TargetControl = targetControl;
}
}
/// <summary>
/// Initializes a new instance of the SafeInvoker class.
/// </summary>
/// <param name="action">
/// The callback to be invoked
/// </param>
public SafeInvoker(Action action) : this(action, null)
{
}
/// <summary>
/// Gets or sets the reference to the control thats going to be used to invoke the callback in UI thread
/// </summary>
protected object TargetControl
{
get { return _targetControl; }
set
{
_invokeRequiredProperty = value.GetType()
.GetProperty("InvokeRequired", BindingFlags.Instance | BindingFlags.Public);
_invokeMethod = value.GetType()
.GetMethod(
"Invoke",
BindingFlags.Instance | BindingFlags.Public,
Type.DefaultBinder,
new[] {typeof(Delegate)},
null);
if (_invokeRequiredProperty != null && _invokeMethod != null)
{
_targetControl = value;
}
}
}
/// <summary>
/// Gets the reference to the callback to be invoked
/// </summary>
protected Delegate UnderlyingDelegate { get; }
/// <summary>
/// Invoke the contained callback
/// </summary>
public virtual void Invoke()
{
Invoke(null);
}
/// <summary>
/// Invoke the referenced callback
/// </summary>
/// <param name="value">The argument to send to the callback</param>
protected void Invoke(object value)
{
try
{
ThreadPool.QueueUserWorkItem(
state =>
{
try
{
if (TargetControl != null && (bool)_invokeRequiredProperty.GetValue(TargetControl, null))
{
_invokeMethod.Invoke(
TargetControl,
new object[]
{
new Action(
() => UnderlyingDelegate.DynamicInvoke(value != null ? new[] {value} : null))
});
return;
}
}
catch
{
// ignored
}
UnderlyingDelegate.DynamicInvoke(value != null ? new[] {value} : null);
});
}
catch
{
// ignored
}
}
}
}