Rx Framework Part III - LINQ To Events - Generating GetEventName() Wrapper Methods using T4 Text Templates

By Anoop Madhusudanan

Vote on HN

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

Add a new text file to your project, and name it EventWrapperGen.ttinclude
<#+

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

Shout it
© 2012. All Rights Reserved. Amazedsaint.com