2008-11-27

The GetField method

Last month I wrote an article about clicking a button in a winforms application and in my code example I referred to the ReflectionUtil.GetField method.
The ReflectionUtil class is an utility class that I have used for several years and they provide a better/easier way to use reflection. They are in line to be converted as Extension methods for the Type type, but for now they are ordinary static methods.
I'll post the GetField method to allow the example to be complete.

#region public static object GetField(Type type, string name, object instance)
/// <summary>
/// Gets the field from the instance
/// </summary>
/// <param name="type">The <see cref="Type"/> that contains the field</param>
/// <param name="name">The name of the field</param>
/// <param name="instance">The instance to get the value from</param>
/// <returns>The value of the field</returns>
public static object GetField(Type type, string name, object instance) {
return InvokeMember(type, name, instance, BindingFlags.GetField | BindingFlags.Instance);
}
#endregion

The GetField only wraps the InvokeMember method. (I have more wrappers named GetProperty, SetField, SetProperty, InvokeMethod, InvokeStaticMethod etc. that calls the InvokeMember method)
#region public static object InvokeMember(Type type, string name, object instance, BindingFlags flags, params object[] parameters)
/// <summary>
/// Invokes the member
/// </summary>
/// <param name="type">The <see cref="Type"/> that contains the member</param>
/// <param name="name">The name of the member</param>
/// <param name="instance">The instance to invoke on</param>
/// <param name="flags">The <see cref="BindingFlags"/> to use</param>
/// <param name="parameters">The <see cref="object"/> array to pass as parameters</param>
/// <returns>The returnvalue</returns>
public static object InvokeMember(Type type, string name, object instance, BindingFlags flags, params object[] parameters) {
if (instance == null) {
flags |= BindingFlags.Static;
flags &= ~BindingFlags.Instance;
}

try {
return type.InvokeMember(name,BindingFlags.Public | BindingFlags.NonPublic | flags,null,instance,parameters);

//if the target threw an exception, throw this instead.
} catch (TargetInvocationException e) {

// if no exception is found, throw the TIException instead.
if (e.InnerException == null)
throw;
throw e.InnerException;
}
}
#endregion

Hope the click button example can make more sense now :)

2008-11-02

Mvc for Winforms - Mapping the View event to the Controller action Part II

This time I will try to deliver part of the answer to the requirements from my previous post. To recap I would like to be able to connect a component to a controller action by calling the RegisterAction method like below.
Controller.RegisterAction(saveButton, "Save");
Controller.RegisterAction(createBoldTextButton, "CreateText", new {name="Bold", type=4});
Controller.RegisterAction(myTextBox, "ValidateText","Validating", null);
And letting those events call the actions defined below
public void Save() {...}
public void CreateText(string name, int type) {...}
public void ValidateText(CancelEventArgs e, object source) {...}
I will divide the solution in two steps and in this post I will cover the first, that is capturing the event of the object.

We start by creating the RegisterAction method. We get the object which event should be listened to and sometimes also the name of the event that we should listen to. If this argument isn't supplied we need to find the DefaultEvent of the object.

By using reflection we can retrieve the DefaultEventAttribute of the object. The following unit test shows how to get the attribute for a Button object. Notice the true flag on the GetCustomAttributes call. Since the DefaultEventAttribute is not present on the Button class itself, we need to go down in the inheritance chain to look for the attribute. Not until we reach the Control class we find the DefaultEventAttribute.
[Test]
public void GetDefaultEventAttribute() {
object obj = new Button();
DefaultEventAttribute attribute = null;
Attribute[] attributes = obj.GetType().GetCustomAttributes(typeof(DefaultEventAttribute), true) as Attribute[];
if (attributes != null && attributes.Length > 0) {
attribute = attributes[0] as DefaultEventAttribute;
}
Assert.IsNotNull(attribute);
}
When we have the attribute, we just look at the Name property to get the name of the default event.

Now when we have the name of the event (either by parameter or using the DefaultEventAttribute) we should add listener to the event. The listener method is a method in the Controller class, not the controller Action (we will get to that in the next part), but a event hub where all the Views events will pass before they are dispatched to the correct Action. The event hub method is declared as below
public void ExecuteAction(object source, object eventArgs, ActionData actionData)
The ExecuteAction method takes three parameters. The source and arguments of the event that was fired (this is the same values that the original event passes along). The third parameter contains the data for the action that is to take place, like the name of the action and any value parameters (The values that are stated when registering the action).

So we got the object and the name of the event and the target method of the event, but how can we connect them?

My first thought was to generate a delegate to the ExecuteAction and use reflection to get the EventInfo for the event and use the AddEventHandler of the EventInfo class to bind to the ExecuteAction method.
[Test]
public void BindToButtonClickEvent() {
object obj = new Button();
EventInfo info = obj.GetType().GetEvent("Click");
MethodInfo method = GetType().GetMethod("ExecuteAction");
Delegate d = Delegate.CreateDelegate(typeof(MyExecuteAction),method);
info.AddEventHandler(obj,d);
}
private delegate void MyExecuteAction(object source, object eventArgs, ActionData actionData);
public void ExecuteAction(object source, object eventArgs, ActionData actionData) {}
But when I ran this code I got an Exception
System.ArgumentException: Error binding to target method.
Of course this won't work since the Click event cannot directly connect to the ExecuteAction method because the Click event can only add handlers that matches the EventHandler delegate, a method with a void return value and two arguments, object and EventArgs.

The conclusion of this is that if I would like to have a single method that acts as an event hub and it must be able to handle any type of delegate that the event declares (note that events as practice should always return void and take two arguments, object and a instance of an EventArgs derived class), I need to generate this method in runtime.

The first option that comes to mind is using Emit. I have tested this in the past, it has worked but comes not so natural to me. Oren Eini used this technique but since I would like to pass a local variable (the ActionData instance) in the call, I needed to modify this piece of code, and possibly use an external list of ActionData instances if I couldn't pass them along using the dynamic method, I searched a bit more for an alternative (second opinion)...

Finally I came across an answer from Mark Cidade that compiled a method in runtime using lambda expressions and that was fairly easy to modify.
First we need to setup the call to the ExecuteAction method. This is done using a lambda expression and storing it in the Action delegate.
ActionData actionData = new ActionData("Save", null);
//Create the delegate using an lambda expression
Action<object,object> eventHubCall = (source, e) => ExecuteAction(source, e, actionData);
This action will take two parameters and call the ExecuteAction just the way as we would like it to. The problem is that this expression is not typed the correct way as the event is so we need to create a new method using lambda expressions again but with the correct declaration. So we start by getting the information about the event.
Type type = obj.GetType();
EventInfo evt = type.GetEvent("Click");
ParameterInfo[] eventParams = evt.EventHandlerType.GetMethod("Invoke").GetParameters();
Then we create the lambda
ParameterExpression[] parameters = eventParams.
Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
MethodCallExpression body = Expression.Call(Expression.Constant(eventHubCall),
eventHubCall.GetType().GetMethod("Invoke"), parameters);
LambdaExpression lambda = Expression.Lambda(body, parameters);
Now we have a method with two parameters of the correct type, the only thing left is to create a delegate that we can use for adding to the event.
Delegate proxy = Delegate.CreateDelegate(evt.EventHandlerType, lambda.Compile(), "Invoke", false);
evt.AddEventHandler(obj, proxy);
Tada!! No more Error binding to target method errors.
The complete test follows.
[Test]
public void BindToButtonClickEvent2() {
ActionData actionData = new ActionData("Save", null);
//Create the delegate using an lambda expression
Action<object,object> eventHubCall = (source, e) => ExecuteAction(source, e, actionData);

object obj = new Button();
//find the exact definition of the event
Type type = obj.GetType();
EventInfo evt = type.GetEvent("Click");
ParameterInfo[] eventParams = evt.EventHandlerType.GetMethod("Invoke").GetParameters();

//create a new lambda expression using the correct parameters
ParameterExpression[] parameters = eventParams.
Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
//call the event hub
MethodCallExpression body = Expression.Call(Expression.Constant(eventHubCall),
eventHubCall.GetType().GetMethod("Invoke"), parameters);
//create the expression with the correct parameters to match the event
LambdaExpression lambda = Expression.Lambda(body, parameters);
//and then create the delegate that wraps the lambda expression.
Delegate proxy= Delegate.CreateDelegate(evt.EventHandlerType, lambda.Compile(), "Invoke", false);

evt.AddEventHandler(obj, proxy);
}
Not so dumb ey! All credits goes to Mark Cidade for providing the elegant solution. (So I perhaps my greatest talent is to find solutions that others have done before me, and use them ;)
Anyway, that concludes part II and next we will look how to call the action method of the controller from the ExecuteAction method.

2008-11-01

Mvc for Winforms - Mapping the View event to the Controller action

In my current (or rather one of my current projects) I'm writing a small MVC framework for a WinForms application (I mentioned this in my last post). The latest issue that I stumbled upon and found worth mentioning is about connecting the Views events to the Controllers actions.

If we look at the MVC for ASP.NET the events is the requests by the web client and the URL contains the information that the Routing module uses to find an appropriate action to call. It should be nice to have such a feature in the WinForms solution as well so after some thought I summarized a small example.

In my view I can have several objects (not necessary controls) that can/will fire some events (like a button firering the click event when the user clicks it).
In my controller I have several action methods that should be called by the view when the appropriate event occurs. (Like a Save action that should be called when the user clicks the save button, thus triggering the save button click event) (nothing out of the ordinary here;)

Whats the fuzz about this then, you may think? This is normal stuff, no strange things!? (who is this dumb guy anyway...)

Yes I see your point. I could easily use the Forms designer in Visual studio, double click the Save button and enter the code below to call the Save button.
private void saveButton_Click(object sender, System.EventArgs e) {
Controller.Save();
}
But thats not what I want. I don't want to litter the View with a lot of event methods just calling one line of code. The purpose of the MVC pattern is to separate the View from the logic and if we start putting those event handler methods in the view, soon some line of controller logic will be entered in there (just to test) and we'll never find the way to clean them up again.

My vision is to register these events one by one in the Views constructor, on one line that enables me to
  • Listen to an event from an object in the view
  • Capture the event and pass it to an action in the controller
  • Allow the mapping to pass additional parameters to the action (compare the routing in MVC for ASP.NET)
If I would make the same example like the one above but using the one liner instead, it would look like below.
Controller.RegisterAction(saveButton,"Save");
If I would pass some parameters to the action I could use the same syntax as when defining default routes in MVC for ASP.NET.
Controller.RegisterAction(createBoldTextButton,"CreateText", new {name="Bold",type=4});
This would then be mapped to the action CreateText which declaration looks like below.
public void CreateText(string name, int type) {...}
If I want to listen to an event that isn't the DefaultEvent (see the DefaultEventAttribute) I could state this as well in the call
Controller.RegisterAction(myTextBox,"ValidateText","Validating", null);
And if I declare the action to include the event argument and/or source they should be passed along as well
public void ValidateText(CancelEventArgs e, object source) {...}
How do I code this to be like I want it to then???

In my next post I will try to give you a solution to the requirements above and hopefully prove that I'm not a dumb guy after all ;)
See ya!