-
Notifications
You must be signed in to change notification settings - Fork 0
/
ContextEventWrapper.cs
247 lines (221 loc) · 8.91 KB
/
ContextEventWrapper.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//-----------------------------------------------------------------------
// <copyright file="ContextEventWrapper.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <summary>
// This file contains the internal classes used to track the synchronization
// context and events handling.
// </summary>
//-----------------------------------------------------------------------
namespace Microsoft.Kinect.Toolkit
{
using System;
using System.Threading;
/// <summary>
/// The possible methods for sending data to the synchronization context.
/// </summary>
public enum ContextSynchronizationMethod
{
/// <summary>
/// The send method is used to pass synchronous message to the context.
/// </summary>
Send,
/// <summary>
/// The post method is used to pass an asynchronous message to the context.
/// </summary>
Post
}
/// <summary>
/// Wrapper that holds a collection of event handlers for a specific type and associates
/// a context with them.
/// </summary>
/// <typeparam name="T">EventArgs for the specific event.</typeparam>
/// <remarks>
/// Note that this has the implementation of IDisposable but not the interface.
/// You may call Dispose on it if you need to. Otherwise you can let the GC handle
/// things.
/// </remarks>
public class ContextEventWrapper<T> where T : EventArgs
{
/// <summary>
/// This holds the list of context to handler mappings.
/// </summary>
private readonly ThreadSafeCollection<ContextHandlerPair> actualHandlers;
/// <summary>
/// This is the method used process the message by the synchronization context.
/// </summary>
private readonly ContextSynchronizationMethod method;
/// <summary>
/// This keeps track of the disposed state of the object.
/// </summary>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the ContextEventWrapper class.
/// </summary>
/// <param name="method">Determines whether the context will use Post or Send. Default is Post.</param>
public ContextEventWrapper(ContextSynchronizationMethod method)
{
isDisposed = false;
this.method = method;
actualHandlers = new ThreadSafeCollection<ContextHandlerPair>();
}
/// <summary>
/// Initializes a new instance of the ContextEventWrapper class.
/// </summary>
public ContextEventWrapper() : this(ContextSynchronizationMethod.Post)
{
}
/// <summary>
/// Gets a value indicating whether this wrapper has any actual handlers registered.
/// </summary>
public bool HasHandlers
{
get { return actualHandlers.Count > 0; }
}
/// <summary>
/// Adds an event handler and associates it with the current context.
/// </summary>
/// <param name="originalHandler">The new event to add to the list of handlers.</param>
public void AddHandler(EventHandler<T> originalHandler)
{
if (originalHandler != null)
{
actualHandlers.Add(new ContextHandlerPair(originalHandler, SynchronizationContext.Current));
}
}
/// <summary>
/// Removes the event handler associated with the current context.
/// </summary>
/// <param name="originalHandler">The event to remove from the list of handlers.</param>
public void RemoveHandler(EventHandler<T> originalHandler)
{
SynchronizationContext currentContext = SynchronizationContext.Current;
ContextHandlerPair pairToRemove = null;
// Find the first matching pair
foreach (ContextHandlerPair pair in actualHandlers)
{
EventHandler<T> handler = pair.Handler;
SynchronizationContext context = pair.Context;
if (currentContext == context && handler == originalHandler)
{
// Stop on first find
pairToRemove = pair;
break;
}
}
// remove if found
if (pairToRemove != null)
{
actualHandlers.Remove(pairToRemove);
}
}
/// <summary>
/// Invokes all registered event handlers.
/// </summary>
/// <param name="sender">The sender of the message.</param>
/// <param name="eventArgs">The event arguments to be passed to the handler.</param>
public void Invoke(object sender, T eventArgs)
{
if (HasHandlers)
{
// Invoke each handler on the list
// Note: Enumerator is a snapshotting enumerator, so this is thread-safe and reentrency safe.
foreach (ContextHandlerPair pair in actualHandlers)
{
EventHandler<T> handler = pair.Handler;
SynchronizationContext context = pair.Context;
if (context == null)
{
handler(sender, eventArgs);
}
else if (method == ContextSynchronizationMethod.Post)
{
context.Post(SendOrPostDelegate, new ContextEventHandlerArgsWrapper(handler, sender, eventArgs));
}
else if (method == ContextSynchronizationMethod.Send)
{
context.Send(SendOrPostDelegate, new ContextEventHandlerArgsWrapper(handler, sender, eventArgs));
}
}
}
}
/// <summary>
/// This method marks the object as disposed.
/// </summary>
public void Dispose()
{
isDisposed = true;
actualHandlers.Clear();
}
/// <summary>
/// Internal handler that matches the delegates for SynchronizationContext.Post/Send.
/// </summary>
/// <param name="state">State packed as ContextEventHandlerArgsWrapper ( handler + sender + args ).</param>
private void SendOrPostDelegate(object state)
{
// This can get disposed before the marshalling is done
if (isDisposed)
{
return;
}
var currentState = (ContextEventHandlerArgsWrapper)state;
currentState.Handler(currentState.Sender, currentState.Args);
}
/// <summary>
/// Container class to hold event handler, sender and args so that it can be
/// marshalled using the Synchronization context.
/// </summary>
private class ContextEventHandlerArgsWrapper
{
/// <summary>
/// Initializes a new instance of the ContextEventHandlerArgsWrapper class.
/// </summary>
/// <param name="handler">The event handler.</param>
/// <param name="sender">The sending object.</param>
/// <param name="args">The argument object.</param>
public ContextEventHandlerArgsWrapper(EventHandler<T> handler, object sender, T args)
{
Handler = handler;
Sender = sender;
Args = args;
}
/// <summary>
/// Gets the associated event handler.
/// </summary>
public EventHandler<T> Handler { get; private set; }
/// <summary>
/// Gets the sending object.
/// </summary>
public object Sender { get; private set; }
/// <summary>
/// Gets the event arguments object.
/// </summary>
public T Args { get; private set; }
}
/// <summary>
/// Container class to associate an event handler with a context so that they
/// act as a single key in a list.
/// </summary>
private class ContextHandlerPair
{
/// <summary>
/// Initializes a new instance of the ContextHandlerPair class.
/// </summary>
/// <param name="handler">The target handler.</param>
/// <param name="context">The target context.</param>
public ContextHandlerPair(EventHandler<T> handler, SynchronizationContext context)
{
Handler = handler;
Context = context;
}
/// <summary>
/// Gets the associated synchronization context.
/// </summary>
public SynchronizationContext Context { get; private set; }
/// <summary>
/// Gets the associated event handler.
/// </summary>
public EventHandler<T> Handler { get; private set; }
}
}
}