Rx Framework Part III - LINQ To Events - Generating GetEventName() Wrapper Methods using T4 Text Templates
Before you being, you may Read Part I and Part II
Soon after writing my last post on LINQ to Events, I thought about creating a quick text template to automatically generate those GetEventName wrapper methods – so that we don’t end up in hand coding those GetMouseDown(), GetMouseMove() and all again. You may use it to quickly generate extension methods to create event to observable wrappers for a specific type. Here we go. Click 'view plain' to grab the source code
<#+ //Generate the Event wrappers to create an observable from //an event //Generate a GetEventName wrapper method for events in this type void GenerateEventGetters(Type type) { var events = type.GetEvents(); foreach (var e in events) { Type tDelegate = e.EventHandlerType; var parameters=GetDelegateParameterTypes(tDelegate); if (parameters.Length==2 && typeof(EventArgs).IsAssignableFrom(parameters[1])) { #> public static IObservable<Event<<#=parameters[1].Name#>>> Get<#=e.Name#> (this <#=type.Name#> el) { var allevents = Observable.FromEvent<<#=GetCorrectTypeName(tDelegate)#>, <#=parameters[1].Name#>> ( h => new <#=GetCorrectTypeName(tDelegate)#>(h), h => el.<#=e.Name#> += h, h=> el.<#=e.Name#> -= h ); return allevents; } <#+ } } } //Get the parameter types for delegates private Type[] GetDelegateParameterTypes(Type d) { if (d.BaseType != typeof(MulticastDelegate)) throw new ApplicationException("Not a delegate."); MethodInfo invoke = d.GetMethod("Invoke"); if (invoke == null) throw new ApplicationException("Not a delegate."); ParameterInfo[] parameters = invoke.GetParameters(); Type[] typeParameters = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { typeParameters[i] = parameters[i].ParameterType; } return typeParameters; } //Get the delegate return type private Type GetDelegateReturnType(Type d) { if (d.BaseType != typeof(MulticastDelegate)) throw new ApplicationException("Not a delegate."); MethodInfo invoke = d.GetMethod("Invoke"); if (invoke == null) throw new ApplicationException("Not a delegate."); return invoke.ReturnType; } // Fixes up a Generic Type name so that it displays properly for output. public static string GetCorrectTypeName(Type genericType) { var fullTypeName=false; if (!genericType.IsGenericType) return genericType.Name; //Make sure the type is indeed generic in which case the` is in the name int index = genericType.Name.IndexOf("`"); if (index == -1) { if (!fullTypeName) return genericType.Name; return genericType.Namespace + "." + genericType.Name; } // Strip off the Genric postfix string TypeName = genericType.Name.Substring(0, index); string formatted = TypeName; //Parse the generic type arguments Type[] genericArgs = genericType.GetGenericArguments(); string genericOutput = "<"; bool Start = true; foreach (Type arg in genericArgs) { if (Start) { genericOutput += arg.Name; Start = false; } else genericOutput += "," + arg.Name; } genericOutput += ">"; formatted += genericOutput; if (!fullTypeName) return formatted; return genericType.Namespace + "." + formatted; } #>And now, add a new tt file UIElementExtensions.tt to your project, and import the above include file as below. Make sure the Custom Tool property of the tt file is set to TextTemplatingFileGenerator.
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #> <#@ output extension=".cs" #> <#@ Assembly Name="System.dll" #> <#@ Assembly Name="System.Core.dll" #> <#@ Assembly Name="System.Windows.Forms.dll" #> <#@ Assembly Name="WindowsBase.dll" #> <#@ Assembly Name="PresentationFramework.dll" #> <#@ Assembly Name="PresentationCore.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Diagnostics" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Collections" #> <#@ import namespace="System.Windows" #> <#@ import namespace="System.Windows.Controls" #> <#@ import namespace="System.Reflection" #> <#@ import namespace="System.Collections.Generic" #> <#@include file="EventWrapperGen.ttinclude" #> using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Reflection; using System.Diagnostics; namespace ReactiveExtensions { public static class <#=typeof(UIElement).Name#>Extensions { <# GenerateEventGetters(typeof(UIElement)); #> } }
You can replace the UIElement type above with any type, to generate the related extension methods. If you havn’t yet done, Read Part I and Part II as well. Enjoy Coding!!