I'm trying to develop an application using the MVP pattern.
The problem is wiring all the code manually. I was hoping to reduce the code needed. I tried to copy another solution, but I couldn't get to work. I'm using Winforms and the solution I was using as source is using WPF.
It would wire things on some conventions:
View events are wired by name. For example:"Loaded" event on the view will be wired to the "OnLoaded()" method on the presenter Button commands are wired by name. For example: MoveNext" button is wired to the "OnMoveNext()" method. Grids double click is wired by name. For example: Double click on "Actions" will be wired to the "OnActionsChoosen(ToDoAction)"
The working code in WPF is:
private static void WireListBoxesDoubleClick(IPresenter presenter)
{
var presenterType = presenter.GetType();
var methodsAndListBoxes = from method in GetActionMethods(presenterType)
where method.Name.EndsWith("Choosen")
where method.GetParameters().Length == 1
let elementName = method.Name.Substring(2, method.Name.Length - 2 /*On*/- 7 /*Choosen*/)
let matchingListBox = LogicalTreeHelper.FindLogicalNode(presenter.View, elementName) as ListBox
where matchingListBox != null
select new {method, matchingListBox};
foreach (var methodAndEvent in methodsAndListBoxes)
{
var parameterType = methodAndEvent.method.GetParameters()[0].ParameterType;
var action = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(parameterType),
presenter, methodAndEvent.method);
methodAndEvent.matchingListBox.MouseDoubleClick += (sender, args) =>
{
var item1 = ((ListBox)sender).SelectedItem;
if(item1 == null)
return;
action.DynamicInvoke(item1);
};
}
}
private static void WireEvents(IPresenter presenter)
{
var viewType = presenter.View.GetType();
var presenterType = presenter.GetType();
var methodsAndEvents =
from method in GetParameterlessActionMethods(presenterType)
let matchingEvent = viewType.GetEvent(method.Name.Substring(2))
where matchingEvent != null
where matchingEvent.EventHandlerType == typeof(RoutedEventHandler)
select new { method, matchingEvent };
foreach (var methodAndEvent in methodsAndEvents)
{
var action = (Action)Delegate.CreateDelegate(typeof(Action),
presenter, methodAndEvent.method);
var handler = (RoutedEventHandler)((sender, args) => action());
methodAndEvent.matchingEvent.AddEventHandler(presenter.View, handler);
}
}
private static IEnumerable<MethodInfo> GetActionMethods(Type type)
{
return from method in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
where method.Name.StartsWith("On")
select method;
}
private static IEnumerable<MethodInfo> GetParameterlessActionMethods(Type type)
{
return from method in GetActionMethods(type)
where method.GetParameters().Length == 0
select method;
}
Anyway, I tried to port that to a WinForm app, but I wasn't successful. I changed the RoutedEventHandlers
to EventHandlers
, but couldn't find what to do about the LogicalTreeHelper
.
I'm kind of stuck on this. I could do manually but I found this mini-framework so ingenuous that it's almost crazy.
PS: Source is http://msdn.microsoft.com/en-us/magazine/ee819139.aspx
Edit
I just realized something. I'm not gone dumb, the code above is not very test friendly, is it?
Ok. I got it working myself. I'm just posting the answer because at lest one other person found interesting.
First, the view
Now the Presenter:
The "magic" happens at the base class. In the wireEventsTo(IBaseView view)
I've got this working here as it is. It will autowire the EventHandler on the Presenter to the Default Events of the Controls that are on IView.
Also, on a side note, I want to share the BindData method.
This eliminates "magic strings" from the Binding. I think it can also be used on INotificationPropertyChanged.
Anyway, I hope someone finds it useful. And I completely ok if you want to point out code smells.