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!!

