Observable.Events.cs 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Reactive.Concurrency;
  3. using System.Threading;
  4. namespace System.Reactive.Linq
  5. {
  6. public static partial class Observable
  7. {
  8. #region + FromEventPattern +
  9. #region Strongly typed
  10. #region Action<EventHandler>
  11. /// <summary>
  12. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler"/>, to an observable sequence.
  13. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  14. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  15. /// </summary>
  16. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  17. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  18. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  19. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  20. /// <remarks>
  21. /// <para>
  22. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  23. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  24. /// </para>
  25. /// <para>
  26. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  27. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  28. /// </para>
  29. /// <para>
  30. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  31. /// making the Subscribe or Dispose call, respectively.
  32. /// </para>
  33. /// <para>
  34. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  35. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  36. /// more concise and easier to understand.
  37. /// </para>
  38. /// </remarks>
  39. /// <seealso cref="Observable.ToEventPattern"/>
  40. #if !NO_EVENTARGS_CONSTRAINT
  41. public static IObservable<EventPattern<EventArgs>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
  42. {
  43. if (addHandler == null)
  44. throw new ArgumentNullException("addHandler");
  45. if (removeHandler == null)
  46. throw new ArgumentNullException("removeHandler");
  47. return s_impl.FromEventPattern(addHandler, removeHandler);
  48. }
  49. #else
  50. public static IObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
  51. {
  52. if (addHandler == null)
  53. throw new ArgumentNullException("addHandler");
  54. if (removeHandler == null)
  55. throw new ArgumentNullException("removeHandler");
  56. return s_impl.FromEventPattern(addHandler, removeHandler);
  57. }
  58. #endif
  59. /// <summary>
  60. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler"/>, to an observable sequence.
  61. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  62. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  63. /// </summary>
  64. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  65. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  66. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  67. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  68. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  69. /// <remarks>
  70. /// <para>
  71. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  72. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  73. /// </para>
  74. /// <para>
  75. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  76. /// accessed from the same context, as required by some UI frameworks.
  77. /// </para>
  78. /// <para>
  79. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  80. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  81. /// parameter. For more information, see the remarks section on those overloads.
  82. /// </para>
  83. /// </remarks>
  84. /// <seealso cref="Observable.ToEventPattern"/>
  85. #if !NO_EVENTARGS_CONSTRAINT
  86. public static IObservable<EventPattern<EventArgs>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IScheduler scheduler)
  87. {
  88. if (addHandler == null)
  89. throw new ArgumentNullException("addHandler");
  90. if (removeHandler == null)
  91. throw new ArgumentNullException("removeHandler");
  92. if (scheduler == null)
  93. throw new ArgumentNullException("scheduler");
  94. return s_impl.FromEventPattern(addHandler, removeHandler, scheduler);
  95. }
  96. #else
  97. public static IObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IScheduler scheduler)
  98. {
  99. if (addHandler == null)
  100. throw new ArgumentNullException("addHandler");
  101. if (removeHandler == null)
  102. throw new ArgumentNullException("removeHandler");
  103. if (scheduler == null)
  104. throw new ArgumentNullException("scheduler");
  105. return s_impl.FromEventPattern(addHandler, removeHandler, scheduler);
  106. }
  107. #endif
  108. #endregion
  109. #region Action<TDelegate>
  110. /// <summary>
  111. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type, to an observable sequence.
  112. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  113. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  114. /// </summary>
  115. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  116. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  117. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  118. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  119. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  120. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  121. /// <remarks>
  122. /// <para>
  123. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  124. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  125. /// </para>
  126. /// <para>
  127. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  128. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  129. /// </para>
  130. /// <para>
  131. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  132. /// making the Subscribe or Dispose call, respectively.
  133. /// </para>
  134. /// <para>
  135. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  136. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  137. /// more concise and easier to understand.
  138. /// </para>
  139. /// </remarks>
  140. /// <seealso cref="Observable.ToEventPattern"/>
  141. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  142. #if !NO_EVENTARGS_CONSTRAINT
  143. where TEventArgs : EventArgs
  144. #endif
  145. {
  146. if (addHandler == null)
  147. throw new ArgumentNullException("addHandler");
  148. if (removeHandler == null)
  149. throw new ArgumentNullException("removeHandler");
  150. return s_impl.FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler);
  151. }
  152. /// <summary>
  153. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type, to an observable sequence.
  154. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  155. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  156. /// </summary>
  157. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  158. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  159. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  160. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  161. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  162. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  163. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  164. /// <remarks>
  165. /// <para>
  166. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  167. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  168. /// </para>
  169. /// <para>
  170. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  171. /// accessed from the same context, as required by some UI frameworks.
  172. /// </para>
  173. /// <para>
  174. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  175. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  176. /// parameter. For more information, see the remarks section on those overloads.
  177. /// </para>
  178. /// </remarks>
  179. /// <seealso cref="Observable.ToEventPattern"/>
  180. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  181. #if !NO_EVENTARGS_CONSTRAINT
  182. where TEventArgs : EventArgs
  183. #endif
  184. {
  185. if (addHandler == null)
  186. throw new ArgumentNullException("addHandler");
  187. if (removeHandler == null)
  188. throw new ArgumentNullException("removeHandler");
  189. if (scheduler == null)
  190. throw new ArgumentNullException("scheduler");
  191. return s_impl.FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler, scheduler);
  192. }
  193. /// <summary>
  194. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler&lt;TEventArgs&gt;"/>, to an observable sequence.
  195. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  196. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  197. /// </summary>
  198. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  199. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  200. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  201. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  202. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  203. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  204. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  205. /// <remarks>
  206. /// <para>
  207. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  208. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  209. /// </para>
  210. /// <para>
  211. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  212. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  213. /// </para>
  214. /// <para>
  215. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  216. /// making the Subscribe or Dispose call, respectively.
  217. /// </para>
  218. /// <para>
  219. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  220. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  221. /// more concise and easier to understand.
  222. /// </para>
  223. /// </remarks>
  224. /// <seealso cref="Observable.ToEventPattern"/>
  225. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Func<EventHandler<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  226. #if !NO_EVENTARGS_CONSTRAINT
  227. where TEventArgs : EventArgs
  228. #endif
  229. {
  230. if (conversion == null)
  231. throw new ArgumentNullException("conversion");
  232. if (addHandler == null)
  233. throw new ArgumentNullException("addHandler");
  234. if (removeHandler == null)
  235. throw new ArgumentNullException("removeHandler");
  236. return s_impl.FromEventPattern<TDelegate, TEventArgs>(conversion, addHandler, removeHandler);
  237. }
  238. /// <summary>
  239. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler&lt;TEventArgs&gt;"/>, to an observable sequence.
  240. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  241. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  242. /// </summary>
  243. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  244. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  245. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  246. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  247. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  248. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  249. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  250. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  251. /// <remarks>
  252. /// <para>
  253. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  254. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  255. /// </para>
  256. /// <para>
  257. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  258. /// accessed from the same context, as required by some UI frameworks.
  259. /// </para>
  260. /// <para>
  261. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  262. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  263. /// parameter. For more information, see the remarks section on those overloads.
  264. /// </para>
  265. /// </remarks>
  266. /// <seealso cref="Observable.ToEventPattern"/>
  267. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Func<EventHandler<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  268. #if !NO_EVENTARGS_CONSTRAINT
  269. where TEventArgs : EventArgs
  270. #endif
  271. {
  272. if (conversion == null)
  273. throw new ArgumentNullException("conversion");
  274. if (addHandler == null)
  275. throw new ArgumentNullException("addHandler");
  276. if (removeHandler == null)
  277. throw new ArgumentNullException("removeHandler");
  278. if (scheduler == null)
  279. throw new ArgumentNullException("scheduler");
  280. return s_impl.FromEventPattern<TDelegate, TEventArgs>(conversion, addHandler, removeHandler, scheduler);
  281. }
  282. /// <summary>
  283. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type with a strongly typed sender parameter, to an observable sequence.
  284. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  285. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  286. /// </summary>
  287. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  288. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  289. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  290. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  291. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  292. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  293. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  294. /// <remarks>
  295. /// <para>
  296. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  297. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  298. /// </para>
  299. /// <para>
  300. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  301. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  302. /// </para>
  303. /// <para>
  304. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  305. /// making the Subscribe or Dispose call, respectively.
  306. /// </para>
  307. /// <para>
  308. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  309. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  310. /// more concise and easier to understand.
  311. /// </para>
  312. /// </remarks>
  313. /// <seealso cref="Observable.ToEventPattern"/>
  314. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  315. #if !NO_EVENTARGS_CONSTRAINT
  316. where TEventArgs : EventArgs
  317. #endif
  318. {
  319. if (addHandler == null)
  320. throw new ArgumentNullException("addHandler");
  321. if (removeHandler == null)
  322. throw new ArgumentNullException("removeHandler");
  323. return s_impl.FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler);
  324. }
  325. /// <summary>
  326. /// Converts a .NET event, conforming to the standard .NET event pattern based on a supplied event delegate type with a strongly typed sender parameter, to an observable sequence.
  327. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  328. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  329. /// </summary>
  330. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  331. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  332. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  333. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  334. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  335. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  336. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  337. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  338. /// <remarks>
  339. /// <para>
  340. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  341. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  342. /// </para>
  343. /// <para>
  344. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  345. /// accessed from the same context, as required by some UI frameworks.
  346. /// </para>
  347. /// <para>
  348. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  349. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  350. /// parameter. For more information, see the remarks section on those overloads.
  351. /// </para>
  352. /// </remarks>
  353. /// <seealso cref="Observable.ToEventPattern"/>
  354. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  355. #if !NO_EVENTARGS_CONSTRAINT
  356. where TEventArgs : EventArgs
  357. #endif
  358. {
  359. if (addHandler == null)
  360. throw new ArgumentNullException("addHandler");
  361. if (removeHandler == null)
  362. throw new ArgumentNullException("removeHandler");
  363. if (scheduler == null)
  364. throw new ArgumentNullException("scheduler");
  365. return s_impl.FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler, scheduler);
  366. }
  367. #endregion
  368. #region Action<EventHandler<TEventArgs>>
  369. /// <summary>
  370. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler&lt;TEventArgs&gt;"/>, to an observable sequence.
  371. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  372. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  373. /// </summary>
  374. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  375. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  376. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  377. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  378. /// <remarks>
  379. /// <para>
  380. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  381. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  382. /// </para>
  383. /// <para>
  384. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  385. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  386. /// </para>
  387. /// <para>
  388. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  389. /// making the Subscribe or Dispose call, respectively.
  390. /// </para>
  391. /// <para>
  392. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  393. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  394. /// more concise and easier to understand.
  395. /// </para>
  396. /// </remarks>
  397. /// <seealso cref="Observable.ToEventPattern"/>
  398. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler)
  399. #if !NO_EVENTARGS_CONSTRAINT
  400. where TEventArgs : EventArgs
  401. #endif
  402. {
  403. if (addHandler == null)
  404. throw new ArgumentNullException("addHandler");
  405. if (removeHandler == null)
  406. throw new ArgumentNullException("removeHandler");
  407. return s_impl.FromEventPattern<TEventArgs>(addHandler, removeHandler);
  408. }
  409. /// <summary>
  410. /// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler&lt;TEventArgs&gt;"/>, to an observable sequence.
  411. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  412. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  413. /// </summary>
  414. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  415. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  416. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  417. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  418. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  419. /// <remarks>
  420. /// <para>
  421. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  422. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  423. /// </para>
  424. /// <para>
  425. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  426. /// accessed from the same context, as required by some UI frameworks.
  427. /// </para>
  428. /// <para>
  429. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  430. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  431. /// parameter. For more information, see the remarks section on those overloads.
  432. /// </para>
  433. /// </remarks>
  434. /// <seealso cref="Observable.ToEventPattern"/>
  435. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler, IScheduler scheduler)
  436. #if !NO_EVENTARGS_CONSTRAINT
  437. where TEventArgs : EventArgs
  438. #endif
  439. {
  440. if (addHandler == null)
  441. throw new ArgumentNullException("addHandler");
  442. if (removeHandler == null)
  443. throw new ArgumentNullException("removeHandler");
  444. if (scheduler == null)
  445. throw new ArgumentNullException("scheduler");
  446. return s_impl.FromEventPattern<TEventArgs>(addHandler, removeHandler, scheduler);
  447. }
  448. #endregion
  449. #endregion
  450. #region Reflection
  451. #region Instance events
  452. /// <summary>
  453. /// Converts an instance .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  454. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  455. /// Reflection is used to discover the event based on the target object type and the specified event name.
  456. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  457. /// </summary>
  458. /// <param name="target">Object instance that exposes the event to convert.</param>
  459. /// <param name="eventName">Name of the event to convert.</param>
  460. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  461. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  462. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  463. /// <remarks>
  464. /// <para>
  465. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  466. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  467. /// </para>
  468. /// <para>
  469. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  470. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  471. /// </para>
  472. /// <para>
  473. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  474. /// making the Subscribe or Dispose call, respectively.
  475. /// </para>
  476. /// <para>
  477. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  478. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  479. /// more concise and easier to understand.
  480. /// </para>
  481. /// </remarks>
  482. /// <seealso cref="Observable.ToEventPattern"/>
  483. #if !NO_EVENTARGS_CONSTRAINT
  484. public static IObservable<EventPattern<EventArgs>> FromEventPattern(object target, string eventName)
  485. {
  486. if (target == null)
  487. throw new ArgumentNullException("target");
  488. if (eventName == null)
  489. throw new ArgumentNullException("eventName");
  490. return s_impl.FromEventPattern(target, eventName);
  491. }
  492. #else
  493. public static IObservable<EventPattern<object>> FromEventPattern(object target, string eventName)
  494. {
  495. if (target == null)
  496. throw new ArgumentNullException("target");
  497. if (eventName == null)
  498. throw new ArgumentNullException("eventName");
  499. return s_impl.FromEventPattern(target, eventName);
  500. }
  501. #endif
  502. /// <summary>
  503. /// Converts an instance .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  504. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  505. /// Reflection is used to discover the event based on the target object type and the specified event name.
  506. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  507. /// </summary>
  508. /// <param name="target">Object instance that exposes the event to convert.</param>
  509. /// <param name="eventName">Name of the event to convert.</param>
  510. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  511. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  512. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  513. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  514. /// <remarks>
  515. /// <para>
  516. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  517. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  518. /// </para>
  519. /// <para>
  520. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  521. /// accessed from the same context, as required by some UI frameworks.
  522. /// </para>
  523. /// <para>
  524. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  525. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  526. /// parameter. For more information, see the remarks section on those overloads.
  527. /// </para>
  528. /// </remarks>
  529. /// <seealso cref="Observable.ToEventPattern"/>
  530. #if !NO_EVENTARGS_CONSTRAINT
  531. public static IObservable<EventPattern<EventArgs>> FromEventPattern(object target, string eventName, IScheduler scheduler)
  532. {
  533. if (target == null)
  534. throw new ArgumentNullException("target");
  535. if (eventName == null)
  536. throw new ArgumentNullException("eventName");
  537. if (scheduler == null)
  538. throw new ArgumentNullException("scheduler");
  539. return s_impl.FromEventPattern(target, eventName, scheduler);
  540. }
  541. #else
  542. public static IObservable<EventPattern<object>> FromEventPattern(object target, string eventName, IScheduler scheduler)
  543. {
  544. if (target == null)
  545. throw new ArgumentNullException("target");
  546. if (eventName == null)
  547. throw new ArgumentNullException("eventName");
  548. if (scheduler == null)
  549. throw new ArgumentNullException("scheduler");
  550. return s_impl.FromEventPattern(target, eventName, scheduler);
  551. }
  552. #endif
  553. /// <summary>
  554. /// Converts an instance .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  555. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  556. /// Reflection is used to discover the event based on the target object type and the specified event name.
  557. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  558. /// </summary>
  559. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  560. /// <param name="target">Object instance that exposes the event to convert.</param>
  561. /// <param name="eventName">Name of the event to convert.</param>
  562. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  563. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  564. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  565. /// <remarks>
  566. /// <para>
  567. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  568. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  569. /// </para>
  570. /// <para>
  571. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  572. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  573. /// </para>
  574. /// <para>
  575. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  576. /// making the Subscribe or Dispose call, respectively.
  577. /// </para>
  578. /// <para>
  579. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  580. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  581. /// more concise and easier to understand.
  582. /// </para>
  583. /// </remarks>
  584. /// <seealso cref="Observable.ToEventPattern"/>
  585. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(object target, string eventName)
  586. #if !NO_EVENTARGS_CONSTRAINT
  587. where TEventArgs : EventArgs
  588. #endif
  589. {
  590. if (target == null)
  591. throw new ArgumentNullException("target");
  592. if (eventName == null)
  593. throw new ArgumentNullException("eventName");
  594. return s_impl.FromEventPattern<TEventArgs>(target, eventName);
  595. }
  596. /// <summary>
  597. /// Converts an instance .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  598. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  599. /// Reflection is used to discover the event based on the target object type and the specified event name.
  600. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  601. /// </summary>
  602. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  603. /// <param name="target">Object instance that exposes the event to convert.</param>
  604. /// <param name="eventName">Name of the event to convert.</param>
  605. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  606. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  607. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  608. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  609. /// <remarks>
  610. /// <para>
  611. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  612. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  613. /// </para>
  614. /// <para>
  615. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  616. /// accessed from the same context, as required by some UI frameworks.
  617. /// </para>
  618. /// <para>
  619. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  620. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  621. /// parameter. For more information, see the remarks section on those overloads.
  622. /// </para>
  623. /// </remarks>
  624. /// <seealso cref="Observable.ToEventPattern"/>
  625. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(object target, string eventName, IScheduler scheduler)
  626. #if !NO_EVENTARGS_CONSTRAINT
  627. where TEventArgs : EventArgs
  628. #endif
  629. {
  630. if (target == null)
  631. throw new ArgumentNullException("target");
  632. if (eventName == null)
  633. throw new ArgumentNullException("eventName");
  634. if (scheduler == null)
  635. throw new ArgumentNullException("scheduler");
  636. return s_impl.FromEventPattern<TEventArgs>(target, eventName, scheduler);
  637. }
  638. /// <summary>
  639. /// Converts an instance .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  640. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  641. /// Reflection is used to discover the event based on the target object type and the specified event name.
  642. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  643. /// </summary>
  644. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  645. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  646. /// <param name="target">Object instance that exposes the event to convert.</param>
  647. /// <param name="eventName">Name of the event to convert.</param>
  648. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  649. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> is null.</exception>
  650. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  651. /// <remarks>
  652. /// <para>
  653. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  654. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  655. /// </para>
  656. /// <para>
  657. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  658. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  659. /// </para>
  660. /// <para>
  661. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  662. /// making the Subscribe or Dispose call, respectively.
  663. /// </para>
  664. /// <para>
  665. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  666. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  667. /// more concise and easier to understand.
  668. /// </para>
  669. /// </remarks>
  670. /// <seealso cref="Observable.ToEventPattern"/>
  671. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(object target, string eventName)
  672. #if !NO_EVENTARGS_CONSTRAINT
  673. where TEventArgs : EventArgs
  674. #endif
  675. {
  676. if (target == null)
  677. throw new ArgumentNullException("target");
  678. if (eventName == null)
  679. throw new ArgumentNullException("eventName");
  680. return s_impl.FromEventPattern<TSender, TEventArgs>(target, eventName);
  681. }
  682. /// <summary>
  683. /// Converts an instance .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  684. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  685. /// Reflection is used to discover the event based on the target object type and the specified event name.
  686. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  687. /// </summary>
  688. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  689. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  690. /// <param name="target">Object instance that exposes the event to convert.</param>
  691. /// <param name="eventName">Name of the event to convert.</param>
  692. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  693. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  694. /// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  695. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  696. /// <remarks>
  697. /// <para>
  698. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  699. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  700. /// </para>
  701. /// <para>
  702. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  703. /// accessed from the same context, as required by some UI frameworks.
  704. /// </para>
  705. /// <para>
  706. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  707. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  708. /// parameter. For more information, see the remarks section on those overloads.
  709. /// </para>
  710. /// </remarks>
  711. /// <seealso cref="Observable.ToEventPattern"/>
  712. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(object target, string eventName, IScheduler scheduler)
  713. #if !NO_EVENTARGS_CONSTRAINT
  714. where TEventArgs : EventArgs
  715. #endif
  716. {
  717. if (target == null)
  718. throw new ArgumentNullException("target");
  719. if (eventName == null)
  720. throw new ArgumentNullException("eventName");
  721. if (scheduler == null)
  722. throw new ArgumentNullException("scheduler");
  723. return s_impl.FromEventPattern<TSender, TEventArgs>(target, eventName, scheduler);
  724. }
  725. #endregion
  726. #region Static events
  727. /// <summary>
  728. /// Converts a static .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  729. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  730. /// Reflection is used to discover the event based on the specified type and the specified event name.
  731. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  732. /// </summary>
  733. /// <param name="type">Type that exposes the static event to convert.</param>
  734. /// <param name="eventName">Name of the event to convert.</param>
  735. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  736. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  737. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  738. /// <remarks>
  739. /// <para>
  740. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  741. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  742. /// </para>
  743. /// <para>
  744. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  745. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  746. /// </para>
  747. /// <para>
  748. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  749. /// making the Subscribe or Dispose call, respectively.
  750. /// </para>
  751. /// <para>
  752. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  753. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  754. /// more concise and easier to understand.
  755. /// </para>
  756. /// </remarks>
  757. /// <seealso cref="Observable.ToEventPattern"/>
  758. #if !NO_EVENTARGS_CONSTRAINT
  759. public static IObservable<EventPattern<EventArgs>> FromEventPattern(Type type, string eventName)
  760. {
  761. if (type == null)
  762. throw new ArgumentNullException("type");
  763. if (eventName == null)
  764. throw new ArgumentNullException("eventName");
  765. return s_impl.FromEventPattern(type, eventName);
  766. }
  767. #else
  768. public static IObservable<EventPattern<object>> FromEventPattern(Type type, string eventName)
  769. {
  770. if (type == null)
  771. throw new ArgumentNullException("type");
  772. if (eventName == null)
  773. throw new ArgumentNullException("eventName");
  774. return s_impl.FromEventPattern(type, eventName);
  775. }
  776. #endif
  777. /// <summary>
  778. /// Converts a static .NET event, conforming to the standard .NET event pattern with an <see cref="EventArgs"/> parameter, to an observable sequence.
  779. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  780. /// Reflection is used to discover the event based on the specified type and the specified event name.
  781. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  782. /// </summary>
  783. /// <param name="type">Type that exposes the static event to convert.</param>
  784. /// <param name="eventName">Name of the event to convert.</param>
  785. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  786. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  787. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  788. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern.</exception>
  789. /// <remarks>
  790. /// <para>
  791. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  792. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  793. /// </para>
  794. /// <para>
  795. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  796. /// accessed from the same context, as required by some UI frameworks.
  797. /// </para>
  798. /// <para>
  799. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  800. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  801. /// parameter. For more information, see the remarks section on those overloads.
  802. /// </para>
  803. /// </remarks>
  804. /// <seealso cref="Observable.ToEventPattern"/>
  805. #if !NO_EVENTARGS_CONSTRAINT
  806. public static IObservable<EventPattern<EventArgs>> FromEventPattern(Type type, string eventName, IScheduler scheduler)
  807. {
  808. if (type == null)
  809. throw new ArgumentNullException("type");
  810. if (eventName == null)
  811. throw new ArgumentNullException("eventName");
  812. if (scheduler == null)
  813. throw new ArgumentNullException("scheduler");
  814. return s_impl.FromEventPattern(type, eventName, scheduler);
  815. }
  816. #else
  817. public static IObservable<EventPattern<object>> FromEventPattern(Type type, string eventName, IScheduler scheduler)
  818. {
  819. if (type == null)
  820. throw new ArgumentNullException("type");
  821. if (eventName == null)
  822. throw new ArgumentNullException("eventName");
  823. if (scheduler == null)
  824. throw new ArgumentNullException("scheduler");
  825. return s_impl.FromEventPattern(type, eventName, scheduler);
  826. }
  827. #endif
  828. /// <summary>
  829. /// Converts a static .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  830. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  831. /// Reflection is used to discover the event based on the specified type and the specified event name.
  832. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  833. /// </summary>
  834. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  835. /// <param name="type">Type that exposes the static event to convert.</param>
  836. /// <param name="eventName">Name of the event to convert.</param>
  837. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  838. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  839. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  840. /// <remarks>
  841. /// <para>
  842. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  843. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  844. /// </para>
  845. /// <para>
  846. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  847. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  848. /// </para>
  849. /// <para>
  850. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  851. /// making the Subscribe or Dispose call, respectively.
  852. /// </para>
  853. /// <para>
  854. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  855. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  856. /// more concise and easier to understand.
  857. /// </para>
  858. /// </remarks>
  859. /// <seealso cref="Observable.ToEventPattern"/>
  860. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Type type, string eventName)
  861. #if !NO_EVENTARGS_CONSTRAINT
  862. where TEventArgs : EventArgs
  863. #endif
  864. {
  865. if (type == null)
  866. throw new ArgumentNullException("type");
  867. if (eventName == null)
  868. throw new ArgumentNullException("eventName");
  869. return s_impl.FromEventPattern<TEventArgs>(type, eventName);
  870. }
  871. /// <summary>
  872. /// Converts a static .NET event, conforming to the standard .NET event pattern with strongly typed event arguments, to an observable sequence.
  873. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  874. /// Reflection is used to discover the event based on the specified type and the specified event name.
  875. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  876. /// </summary>
  877. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  878. /// <param name="type">Type that exposes the static event to convert.</param>
  879. /// <param name="eventName">Name of the event to convert.</param>
  880. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  881. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  882. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  883. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  884. /// <remarks>
  885. /// <para>
  886. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  887. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  888. /// </para>
  889. /// <para>
  890. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  891. /// accessed from the same context, as required by some UI frameworks.
  892. /// </para>
  893. /// <para>
  894. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  895. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  896. /// parameter. For more information, see the remarks section on those overloads.
  897. /// </para>
  898. /// </remarks>
  899. /// <seealso cref="Observable.ToEventPattern"/>
  900. public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Type type, string eventName, IScheduler scheduler)
  901. #if !NO_EVENTARGS_CONSTRAINT
  902. where TEventArgs : EventArgs
  903. #endif
  904. {
  905. if (type == null)
  906. throw new ArgumentNullException("type");
  907. if (eventName == null)
  908. throw new ArgumentNullException("eventName");
  909. if (scheduler == null)
  910. throw new ArgumentNullException("scheduler");
  911. return s_impl.FromEventPattern<TEventArgs>(type, eventName, scheduler);
  912. }
  913. /// <summary>
  914. /// Converts a static .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  915. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  916. /// Reflection is used to discover the event based on the specified type and the specified event name.
  917. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  918. /// </summary>
  919. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  920. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  921. /// <param name="type">Type that exposes the static event to convert.</param>
  922. /// <param name="eventName">Name of the event to convert.</param>
  923. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  924. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> is null.</exception>
  925. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  926. /// <remarks>
  927. /// <para>
  928. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  929. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  930. /// </para>
  931. /// <para>
  932. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
  933. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  934. /// </para>
  935. /// <para>
  936. /// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
  937. /// making the Subscribe or Dispose call, respectively.
  938. /// </para>
  939. /// <para>
  940. /// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  941. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  942. /// more concise and easier to understand.
  943. /// </para>
  944. /// </remarks>
  945. /// <seealso cref="Observable.ToEventPattern"/>
  946. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(Type type, string eventName)
  947. #if !NO_EVENTARGS_CONSTRAINT
  948. where TEventArgs : EventArgs
  949. #endif
  950. {
  951. if (type == null)
  952. throw new ArgumentNullException("type");
  953. if (eventName == null)
  954. throw new ArgumentNullException("eventName");
  955. return s_impl.FromEventPattern<TSender, TEventArgs>(type, eventName);
  956. }
  957. /// <summary>
  958. /// Converts a static .NET event, conforming to the standard .NET event pattern with a strongly typed sender and strongly typed event arguments, to an observable sequence.
  959. /// Each event invocation is surfaced through an OnNext message in the resulting sequence.
  960. /// Reflection is used to discover the event based on the specified type and the specified event name.
  961. /// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
  962. /// </summary>
  963. /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
  964. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  965. /// <param name="type">Type that exposes the static event to convert.</param>
  966. /// <param name="eventName">Name of the event to convert.</param>
  967. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  968. /// <returns>The observable sequence that contains data representations of invocations of the underlying .NET event.</returns>
  969. /// <exception cref="ArgumentNullException"><paramref name="type"/> or <paramref name="eventName"/> or <paramref name="scheduler"/> is null.</exception>
  970. /// <exception cref="InvalidOperationException">The event could not be found. -or- The event does not conform to the standard .NET event pattern. -or- The event's first argument type is not assignable to TSender. -or- The event's second argument type is not assignable to TEventArgs.</exception>
  971. /// <remarks>
  972. /// <para>
  973. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  974. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  975. /// </para>
  976. /// <para>
  977. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  978. /// accessed from the same context, as required by some UI frameworks.
  979. /// </para>
  980. /// <para>
  981. /// It's recommended to lift FromEventPattern calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  982. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEventPattern that omit the IScheduler
  983. /// parameter. For more information, see the remarks section on those overloads.
  984. /// </para>
  985. /// </remarks>
  986. /// <seealso cref="Observable.ToEventPattern"/>
  987. public static IObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TSender, TEventArgs>(Type type, string eventName, IScheduler scheduler)
  988. #if !NO_EVENTARGS_CONSTRAINT
  989. where TEventArgs : EventArgs
  990. #endif
  991. {
  992. if (type == null)
  993. throw new ArgumentNullException("type");
  994. if (eventName == null)
  995. throw new ArgumentNullException("eventName");
  996. if (scheduler == null)
  997. throw new ArgumentNullException("scheduler");
  998. return s_impl.FromEventPattern<TSender, TEventArgs>(type, eventName, scheduler);
  999. }
  1000. #endregion
  1001. #endregion
  1002. #endregion
  1003. #region + FromEvent +
  1004. #region Action<TDelegate>
  1005. /// <summary>
  1006. /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1007. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1008. /// </summary>
  1009. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  1010. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1011. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  1012. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1013. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1014. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1015. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1016. /// <remarks>
  1017. /// <para>
  1018. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1019. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1020. /// </para>
  1021. /// <para>
  1022. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1023. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1024. /// </para>
  1025. /// <para>
  1026. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1027. /// making the Subscribe or Dispose call, respectively.
  1028. /// </para>
  1029. /// <para>
  1030. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1031. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1032. /// more concise and easier to understand.
  1033. /// </para>
  1034. /// </remarks>
  1035. /// <seealso cref="Observable.ToEvent"/>
  1036. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  1037. {
  1038. if (conversion == null)
  1039. throw new ArgumentNullException("conversion");
  1040. if (addHandler == null)
  1041. throw new ArgumentNullException("addHandler");
  1042. if (removeHandler == null)
  1043. throw new ArgumentNullException("removeHandler");
  1044. return s_impl.FromEvent<TDelegate, TEventArgs>(conversion, addHandler, removeHandler);
  1045. }
  1046. /// <summary>
  1047. /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1048. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1049. /// </summary>
  1050. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  1051. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1052. /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
  1053. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1054. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1055. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1056. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1057. /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1058. /// <remarks>
  1059. /// <para>
  1060. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1061. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1062. /// </para>
  1063. /// <para>
  1064. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1065. /// accessed from the same context, as required by some UI frameworks.
  1066. /// </para>
  1067. /// <para>
  1068. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1069. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1070. /// parameter. For more information, see the remarks section on those overloads.
  1071. /// </para>
  1072. /// </remarks>
  1073. /// <seealso cref="Observable.ToEvent"/>
  1074. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  1075. {
  1076. if (conversion == null)
  1077. throw new ArgumentNullException("conversion");
  1078. if (addHandler == null)
  1079. throw new ArgumentNullException("addHandler");
  1080. if (removeHandler == null)
  1081. throw new ArgumentNullException("removeHandler");
  1082. if (scheduler == null)
  1083. throw new ArgumentNullException("scheduler");
  1084. return s_impl.FromEvent<TDelegate, TEventArgs>(conversion, addHandler, removeHandler, scheduler);
  1085. }
  1086. /// <summary>
  1087. /// Converts a .NET event to an observable sequence, using a supplied event delegate type. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1088. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1089. /// </summary>
  1090. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  1091. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1092. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1093. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1094. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1095. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1096. /// <remarks>
  1097. /// <para>
  1098. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1099. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1100. /// </para>
  1101. /// <para>
  1102. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1103. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1104. /// </para>
  1105. /// <para>
  1106. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1107. /// making the Subscribe or Dispose call, respectively.
  1108. /// </para>
  1109. /// <para>
  1110. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1111. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1112. /// more concise and easier to understand.
  1113. /// </para>
  1114. /// </remarks>
  1115. /// <seealso cref="Observable.ToEvent"/>
  1116. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
  1117. {
  1118. if (addHandler == null)
  1119. throw new ArgumentNullException("addHandler");
  1120. if (removeHandler == null)
  1121. throw new ArgumentNullException("removeHandler");
  1122. return s_impl.FromEvent<TDelegate, TEventArgs>(addHandler, removeHandler);
  1123. }
  1124. /// <summary>
  1125. /// Converts a .NET event to an observable sequence, using a supplied event delegate type. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1126. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1127. /// </summary>
  1128. /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
  1129. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1130. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1131. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1132. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1133. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1134. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1135. /// <remarks>
  1136. /// <para>
  1137. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1138. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1139. /// </para>
  1140. /// <para>
  1141. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1142. /// accessed from the same context, as required by some UI frameworks.
  1143. /// </para>
  1144. /// <para>
  1145. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1146. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1147. /// parameter. For more information, see the remarks section on those overloads.
  1148. /// </para>
  1149. /// </remarks>
  1150. /// <seealso cref="Observable.ToEvent"/>
  1151. public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IScheduler scheduler)
  1152. {
  1153. if (addHandler == null)
  1154. throw new ArgumentNullException("addHandler");
  1155. if (removeHandler == null)
  1156. throw new ArgumentNullException("removeHandler");
  1157. if (scheduler == null)
  1158. throw new ArgumentNullException("scheduler");
  1159. return s_impl.FromEvent<TDelegate, TEventArgs>(addHandler, removeHandler, scheduler);
  1160. }
  1161. #endregion
  1162. #region Action<Action<TEventArgs>>
  1163. /// <summary>
  1164. /// Converts a generic Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1165. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1166. /// </summary>
  1167. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1168. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1169. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1170. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1171. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1172. /// <remarks>
  1173. /// <para>
  1174. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1175. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1176. /// </para>
  1177. /// <para>
  1178. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1179. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1180. /// </para>
  1181. /// <para>
  1182. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1183. /// making the Subscribe or Dispose call, respectively.
  1184. /// </para>
  1185. /// <para>
  1186. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1187. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1188. /// more concise and easier to understand.
  1189. /// </para>
  1190. /// </remarks>
  1191. /// <seealso cref="Observable.ToEvent"/>
  1192. public static IObservable<TEventArgs> FromEvent<TEventArgs>(Action<Action<TEventArgs>> addHandler, Action<Action<TEventArgs>> removeHandler)
  1193. {
  1194. if (addHandler == null)
  1195. throw new ArgumentNullException("addHandler");
  1196. if (removeHandler == null)
  1197. throw new ArgumentNullException("removeHandler");
  1198. return s_impl.FromEvent<TEventArgs>(addHandler, removeHandler);
  1199. }
  1200. /// <summary>
  1201. /// Converts a generic Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1202. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1203. /// </summary>
  1204. /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
  1205. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1206. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1207. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1208. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1209. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1210. /// <remarks>
  1211. /// <para>
  1212. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1213. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1214. /// </para>
  1215. /// <para>
  1216. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1217. /// accessed from the same context, as required by some UI frameworks.
  1218. /// </para>
  1219. /// <para>
  1220. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1221. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1222. /// parameter. For more information, see the remarks section on those overloads.
  1223. /// </para>
  1224. /// </remarks>
  1225. /// <seealso cref="Observable.ToEvent"/>
  1226. public static IObservable<TEventArgs> FromEvent<TEventArgs>(Action<Action<TEventArgs>> addHandler, Action<Action<TEventArgs>> removeHandler, IScheduler scheduler)
  1227. {
  1228. if (addHandler == null)
  1229. throw new ArgumentNullException("addHandler");
  1230. if (removeHandler == null)
  1231. throw new ArgumentNullException("removeHandler");
  1232. if (scheduler == null)
  1233. throw new ArgumentNullException("scheduler");
  1234. return s_impl.FromEvent<TEventArgs>(addHandler, removeHandler, scheduler);
  1235. }
  1236. #endregion
  1237. #region Action<Action>
  1238. /// <summary>
  1239. /// Converts an Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1240. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1241. /// </summary>
  1242. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1243. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1244. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1245. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
  1246. /// <remarks>
  1247. /// <para>
  1248. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1249. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1250. /// </para>
  1251. /// <para>
  1252. /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.
  1253. /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
  1254. /// </para>
  1255. /// <para>
  1256. /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread
  1257. /// making the Subscribe or Dispose call, respectively.
  1258. /// </para>
  1259. /// <para>
  1260. /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
  1261. /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
  1262. /// more concise and easier to understand.
  1263. /// </para>
  1264. /// </remarks>
  1265. /// <seealso cref="Observable.ToEvent"/>
  1266. public static IObservable<Unit> FromEvent(Action<Action> addHandler, Action<Action> removeHandler)
  1267. {
  1268. if (addHandler == null)
  1269. throw new ArgumentNullException("addHandler");
  1270. if (removeHandler == null)
  1271. throw new ArgumentNullException("removeHandler");
  1272. return s_impl.FromEvent(addHandler, removeHandler);
  1273. }
  1274. /// <summary>
  1275. /// Converts an Action-based .NET event to an observable sequence. Each event invocation is surfaced through an OnNext message in the resulting sequence.
  1276. /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
  1277. /// </summary>
  1278. /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
  1279. /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
  1280. /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
  1281. /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
  1282. /// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
  1283. /// <remarks>
  1284. /// <para>
  1285. /// Add and remove handler invocations are made whenever the number of observers grows beyond zero.
  1286. /// As such, an event handler may be shared by multiple simultaneously active observers, using a subject for multicasting.
  1287. /// </para>
  1288. /// <para>
  1289. /// Add and remove handler invocations are run on the specified scheduler. This behavior allows add and remove handler operations for thread-affine events to be
  1290. /// accessed from the same context, as required by some UI frameworks.
  1291. /// </para>
  1292. /// <para>
  1293. /// It's recommended to lift FromEvent calls outside event stream query expressions. This best practice reduces clutter of bridging code inside queries,
  1294. /// making the query expressions more concise and easier to understand. This has additional benefits for overloads of FromEvent that omit the IScheduler
  1295. /// parameter. For more information, see the remarks section on those overloads.
  1296. /// </para>
  1297. /// </remarks>
  1298. /// <seealso cref="Observable.ToEvent"/>
  1299. public static IObservable<Unit> FromEvent(Action<Action> addHandler, Action<Action> removeHandler, IScheduler scheduler)
  1300. {
  1301. if (addHandler == null)
  1302. throw new ArgumentNullException("addHandler");
  1303. if (removeHandler == null)
  1304. throw new ArgumentNullException("removeHandler");
  1305. if (scheduler == null)
  1306. throw new ArgumentNullException("scheduler");
  1307. return s_impl.FromEvent(addHandler, removeHandler, scheduler);
  1308. }
  1309. #endregion
  1310. #endregion
  1311. }
  1312. }