Raising complex event using Moq in C# - c#

The following code should be self explanetory: we have an adaptor, who consumes events from the transport (layer), which holds the MessageRegistrar (object type because we can't tell it's type, and basically because this is legacy code :-) ). The transport layer have a concrete which have an event.
I want to test a case where the event is triggered, so..
After hours of trying to figure why it won't pass, I present the following challenge:
[TestFixture]
public class AdaptorTests
{
public delegate void TracksEventHandler(object sender, List<int> trklst);
public class MyEventHolder
{
public virtual event TracksEventHandler EventName;
}
public interface ITransport
{
object MessageRegistrar { get; }
}
public class MyTransport : ITransport
{
private readonly MyEventHolder m_eventHolder;
public MyTransport(MyEventHolder eventHolder)
{
m_eventHolder = eventHolder;
}
public virtual object MessageRegistrar
{
get { return m_eventHolder; }
}
}
public class MyAdaptor
{
private readonly ITransport m_transport;
public MyAdaptor(ITransport transport)
{
EventTriggered = false;
m_transport = transport;
}
public void Connect()
{
MyEventHolder eventHolder = m_transport.MessageRegistrar as MyEventHolder;
if (eventHolder != null)
eventHolder.EventName += EventHolderOnEventName;
}
private void EventHolderOnEventName(object sender, List<int> trklst)
{
EventTriggered = true;
}
public bool EventTriggered { get; private set; }
}
[Test]
public void test1()
{
Mock<MyEventHolder> eventHolderMock = new Mock<MyEventHolder> {CallBase = true};
Mock<MyTransport> transportMock = new Mock<MyTransport>(eventHolderMock.Object) {CallBase = true};
MyAdaptor adaptor = new MyAdaptor(transportMock.Object);
adaptor.Connect();
MyEventHolder eventHolder = transportMock.Object.MessageRegistrar as MyEventHolder;
Mock.Get(eventHolder).Raise(eh => eh.EventName += null, new List<int>());
Assert.IsTrue(adaptor.EventTriggered);
}
[Test]
public void test2()
{
Mock<MyEventHolder> eventHolderMock = new Mock<MyEventHolder> { CallBase = true };
Mock<MyTransport> transportMock = new Mock<MyTransport>(eventHolderMock.Object) { CallBase = true };
MyAdaptor adaptor = new MyAdaptor(transportMock.Object);
adaptor.Connect();
MyEventHolder eventHolder = transportMock.Object.MessageRegistrar as MyEventHolder;
Mock.Get(eventHolder).Raise(eh => eh.EventName += null, null, new List<int>());
Assert.IsTrue(adaptor.EventTriggered);
}
}
My question is: why wont the test (at least one of them) pass?
EDIT #151217-0822 Addded 'adaptor.Connect()' to the original post (still won't fix the issue).
WORKAROUND
Credits to #Patrick Quirk: Thanks!!
For those encountering the same issue: after I understood what Patrick-Quirk detected, and trying couple of failed workarounds, I've ended up adding the following verified fix: 'eventHolder.FireEventNameForTestings(new List());':
public class MyEventHolder
{
public virtual event TracksEventHandler EventName;
public virtual void FireEventNameForTestings(List<int> trklst)
{
TracksEventHandler handler = EventName;
if (handler != null)
handler(this, trklst);
}
}
[Test]
public void test3()
{
Mock<MyEventHolder> eventHolderMock = new Mock<MyEventHolder> { CallBase = true };
Mock<MyTransport> transportMock = new Mock<MyTransport>(eventHolderMock.Object) { CallBase = true };
MyAdaptor adaptor = new MyAdaptor(transportMock.Object);
adaptor.Connect();
MyEventHolder eventHolder = transportMock.Object.MessageRegistrar as MyEventHolder;
eventHolder.FireEventNameForTestings(new List<int>());
Assert.IsTrue(adaptor.EventTriggered);
}
HTH..

It seems that CallBase and Raise() have an unexpected (to me) interaction.
When you are attaching an event handler to a virtual event on a mock, you go through this code in Moq:
if (invocation.Method.IsEventAttach())
{
var delegateInstance = (Delegate)invocation.Arguments[0];
// TODO: validate we can get the event?
var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(4));
if (ctx.Mock.CallBase && !eventInfo.DeclaringType.IsInterface)
{
invocation.InvokeBase();
}
else if (delegateInstance != null)
{
ctx.AddEventHandler(eventInfo, (Delegate)invocation.Arguments[0]);
}
return InterceptionAction.Stop;
}
You can see that if CallBase is true, then it will add your handler to the concrete object's event (via invocation.InvokeBase()). If CallBase is false, it will add it to an invocation list on the mock (via AddEventHandler). Now let's look at the code for Raise(), which gets the event object from the Expression and then calls DoRaise():
internal void DoRaise(EventInfo ev, EventArgs args)
{
// ... parameter validation
foreach (var del in this.Interceptor.InterceptionContext.GetInvocationList(ev).ToArray())
{
del.InvokePreserveStack(this.Object, args);
}
}
See the call to GetInvocationList()? That retrieves the invocation list from the mock that I mentioned above. This code never invokes the actual event on the base object.
So, it seems there's no way to raise an event on a mocked object where CallBase is set to true.
The only workaround I see, if you require CallBase being true, is to add a method to your concrete MyEventHolder to trigger your event. Obviously what you posted is a simplified example so I can't give you more guidance than that, but hopefully I've shown you why what you have does not work.

Related

Set Event-Handler by reflection with Type and Template argument

I would like to set an event handler only by reflection, I can get all the types but I can't achieve it.
public delegate void MyHandler<T>(IMyInterface<T> pParam);
public interface IMyInterface<T>
{
MyHandler<T> EventFired { get; set; }
}
void myFunction()
{
//admit vMyObject is IMyInterface<ClassA>
var vMyObject = vObj as IMyInterface<ClassA>;
//get the generic type => ClassA
var vTypeGeneric = vTypeReturn.GenericTypeArguments.FirstOrDefault();
//build the type handler for the event MyHandler<ClassA>
Type vAsyncOP = typeof(MyHandler<>).MakeGenericType(vTypeGeneric);
// here I don't know how to create the Event handler to set EventFired
// dynamically with the code inside
var vEventFired = vMyObject.GetType().GetProperty("EventFired");
vEventFired.SetMethod etc...
}
I found some code with the usage of Lambda/Expression but I don't understand how to use it in this case.
Full sample:
public delegate void MyHandler<T>(IMyInterface<T> pParam);
public interface IMyInterface<T>
{
MyHandler<T> EventFired { get; set; }
}
public class MyClass : IMyInterface<int>
{
public MyHandler<int> EventFired { get; set;}
}
public void ConcreteHandler(IMyInterface<int> p)
{
Console.WriteLine("I'm here");
}
void Main()
{
var myValue = new MyClass();
var deleg = Delegate.CreateDelegate(typeof(MyHandler<int>), this, "ConcreteHandler");
myValue.GetType().GetProperty("EventFired").SetValue(myValue, deleg);
// Test delegate invocation:
myValue.EventFired.Invoke(new MyClass());
}
Since the question asks about setting an event and the code refers to delegate, here is the code for setting an event using reflection (via extension method):
public delegate void MyHandler<T>(IMyInterface<T> pParam);
public interface IMyInterface<T>
{
event MyHandler<T> EventFired;
}
public class ClassA : IMyInterface<int>
{
public event MyHandler<int> EventFired;
private int _Count = 0;
public void Fire()
{
var handler = EventFired;
if (handler != null) handler(this);
}
public override string ToString()
{
return "Call: " + (++_Count).ToString();
}
}
public static class Extension
{
public static void Add<T>(this IMyInterface<T> i, System.Reflection.MethodInfo method, object method_instance = null)
{
if (method.IsGenericMethodDefinition) method = method.MakeGenericMethod(typeof(T));
Delegate d = Delegate.CreateDelegate(typeof(MyHandler<>).MakeGenericType(typeof(T)), method_instance, method);
i.GetType().GetEvent("EventFired").GetAddMethod().Invoke(i, new object[] { d });
}
}
public class Program
{
public static void Print<T>(IMyInterface<T> val)
{
string output = val.ToString();
Console.WriteLine(output);
System.Diagnostics.Debug.WriteLine(output);
}
static void Main(string[] args)
{
ClassA ca = new ClassA();
ca.EventFired += Print<int>;
ca.Add(typeof(Program).GetMethod("Print", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public));
ca.Fire();
}
}
Sorry for the title, I meant not an event but a delegate property.
I found the solution meanwhile :
public void MyDelegate<T>(IMyInterface<T> pParam)
{
}
void myFunction()
{
//admit vMyObject is IMyInterface<ClassA>
var vMyObject = vObj as IMyInterface<ClassA>;
//get the generic type => ClassA
var vTypeGeneric = vTypeReturn.GenericTypeArguments.FirstOrDefault();
//build the type handler for the event MyHandler<ClassA>
Type vAsyncOP = typeof(MyHandler<>).MakeGenericType(vTypeGeneric);
// SOLUTION here :
// Create MyDelegate<vTypeGeneric>
// Then instanciate it with CreateDelegate and typeof(IMyInterface<vTypeGeneric>)
var vMyDelegate= this.GetType().GetMethod("MyDelegate");
var vMyDelegateGeneric = vMyDelegate.MakeGenericMethod(vTypeGeneric);
Type vTypeHandlerGeneric = typeof(IMyInterface<>).MakeGenericType(vTypeGeneric);
// this => bind to method in the class
var vMethodDelegate = vMyDelegateGeneric.CreateDelegate(vTypeHandlerGeneric, this);
// Set delegate Property
var vEventFired = vMyObject.GetType().GetProperty("EventFired");
vEventFired.SetValue(value, vDelegate);
}

Setup an event aggregator event using Moq

I am trying to unit test a simple method, and verify certain event has been published from that method, but finding hard time to set the mocks up.
//Class under test
public class TreatmentRoomModel : ITreatmentRoomModel
{
public TreatmentRoomModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
//Method under test
public void SetTreatmentInProgress(bool inProgress)
{
if (_isTreatmentInProgress == inProgress) return;
_isTreatmentInProgress = inProgress;
_eventAggregator.Publish(new TreatmentStatus(_isTreatmentInProgress), Execute.OnUIThread);
}
}
//TreatmentStatus event
public class TreatmentStatus
{
public TreatmentStatus(bool isInProgress)
{
IsInProgress = isInProgress;
}
public bool IsInProgress { get; private set; }
}
I am trying to test the method SetTreatmentInProgress and verify that a correct TreatmentStatus event is being published by the eventaggregator.
The event aggregator is typeof(Caliburn.Micro.IEventAggregator)
Below is the test setup
[TestClass]
public class TreatmentRoomModelTests
{
private Mock<IEventAggregator> _mockEventAggregator;
ITreatmentRoomModel _treatmentRoomModel;
private readonly TreatmentStatus _treatmentInProgressEvent = new TreatmentStatus(true);
private readonly TreatmentStatus _treatmentNotInProgressEvent = new TreatmentStatus(false);
[TestInitialize]
public void Initialize()
{
_mockEventAggregator = new Mock<IEventAggregator>();
//I am not sure how to set the property IsInProgress of TreatmentStatus to true? It is a privately set property through constructor.
_mockEventAggregator.Setup(x => x.Publish(It.IsAny<TreatmentStatus>(), Execute.OnUIThread));
//Or should I directly publish a true event, but then how to verify the event object without a reference to it.
//_mockEventAggregator.Setup(x => x.Publish(new TreatmentStatus(true), Execute.OnUIThread));
_treatmentRoomModel = new TreatmentRoomModel(_mockEventAggregator.Object);
}
[TestMethod]
public void SetTreatmentInProgressTest()
{
_treatmentRoomModel.SetTreatmentInProgress(true);
//This works, but I wan't to verify that the object of TreatmentStatus event has the property IsInProgress set to true.
_mockEventAggregator.Verify(x=>x.Publish(It.IsAny<TreatmentStatus>(), Execute.OnUIThread),Times.Once);
_treatmentRoomModel.SetTreatmentInProgress(false);
//Won't work, as it says this is getting called Times.None. I understand this may be because of different TreatmentStatus objects, which are raised and verified.
_mockEventAggregator.Verify(x=>x.Publish(new TreatmentStatus(false), Execute.OnUIThread),Times.Once);
}
}
Fix the expression to use It.Is<>
//This works, but I wan't to verify that the object of TreatmentStatus event has the property IsInProgress set to true.
_mockEventAggregator
.Verify(x=>x.Publish(It.Is<TreatmentStatus>(_ => _.IsInProgress == true), Execute.OnUIThread),Times.Once);
[TestMethod]
public void SetTreatmentInProgressTest()
{
var inProgress = true;
TreatmentStatus resultStatus = null;
_mockEventAggregator.Setup(x => x.Publish(It.IsAny<TreatmentStatus>(), Execute.OnUIThread))
.Callback<object,Action<Action>>((t,s) => resultStatus = (TreatmentStatus)t);
_treatmentRoomModel.SetTreatmentInProgress(inProgress);
Assert.IsNotNull(resultStatus);
Assert.IsTrue(resultStatus.IsInProgress);
Assert.IsTrue(_treatmentRoomModel.IsTreatmentInProgress);
}

Handling event of recursively created Mock in Moq

I'm trying to use recursive mocking feature from the Moq framework but it doesn't work as I expect it to work.
using System;
using NUnit.Framework;
using Moq;
namespace MoqTest
{
public interface IParent
{
IChild Child { get; }
}
public interface IChild
{
event EventHandler SomethingHappened;
}
[TestFixture]
public class UnitTest
{
[Test]
public void RecursiveMockTest()
{
// Arrange
bool isEventHandled = false;
var parentMock = new Mock<IParent>();
parentMock.DefaultValue = DefaultValue.Mock;
var parent = parentMock.Object;
parent.Child.SomethingHappened +=
(sender, args) =>
{
isEventHandled = true;
};
// Act
parentMock.Raise(x => x.Child.SomethingHappened += null, EventArgs.Empty);
// Assert
Assert.IsTrue(isEventHandled);
}
}
}
Could please someone explain to me why SomethingHappened is never handled? I have an assumption that references of parent.Child.SomethingHappened and x.Child.SomethingHappened are not equals. If so then why it's not the same?
It's all correct, you need the following:
[Test]
public void RecursiveMockTest()
{
// Arrange
bool isEventHandled = false;
var parentMock = new Mock<IParent>();
parentMock.DefaultValue = DefaultValue.Mock;
var parent = parentMock.Object;
// get the actual mock which was automatically setup for you
var childMock = Mock.Get(parent.Child);
parent.Child.SomethingHappened +=
(sender, args) =>
{
isEventHandled = true;
};
// Act on the mock which was setup for you
childMock.Raise(x => x.SomethingHappened += null, EventArgs.Empty);
// Assert
Assert.IsTrue(isEventHandled);
}

unit testing a class with event and delegate

I am new to testing please help.
I have the following class
public delegate void OnInvalidEntryMethod(ITnEntry entry, string message);
public class EntryValidator
{
public event OnInvalidEntryMethod OnInvalidEntry;
public bool IsValidEntry(ITnEntry entry, string ticker)
{
if (!IsFieldValid(entry, ticker.Trim().Length.ToString(), "0"))
return false;
return true;
}
private bool IsFieldValid(ITnEntry entry, string actual, string invalidValue)
{
if (actual == invalidValue)
{
RaiseInvalidEntryEvent(entry);
return false;
}
return true;
}
private void RaiseInvalidEntryEvent(ITnEntry entry)
{
if (OnInvalidEntry != null)
OnInvalidEntry(entry, "Invalid entry in list: " + entry.List.Name + ".");
}
}
I have written the test case so far but am struggling with the event and delegate as shown below
[TestFixture]
public class EntryValidatorTests
{
private EntryValidator _entryValidator;
private FakeTnEntry _selectedEntry;
private string _ticker;
[SetUp]
public void Setup()
{
_entryValidator = new EntryValidator();
_ticker = "BOL";
}
private FakeTnEntry MakeEntry(string ticker)
{
return new FakeTnEntry { Ticker = ticker};
}
[Test]
public void IsValidEntry_WithValidValues()
{
_selectedEntry = MakeEntry(_ticker);
Assert.IsTrue(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
}
[Test]
public void IsValidEntry_WithInValidTicker()
{
_selectedEntry = MakeEntry("");
Assert.IsFalse(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
}
}}
Please can someone help? Thanks..
It's probably simplest just to subscribe to the event using an anonymous method:
[Test]
public void IsValidEntry_WithValidValues()
{
_selectedEntry = MakeEntry(_ticker);
_entryValidator.OnInvalidEntry += delegate {
Assert.Fail("Shouldn't be called");
};
Assert.IsTrue(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
}
[Test]
public void IsValidEntry_WithInValidTicker()
{
bool eventRaised = false;
_selectedEntry = MakeEntry("");
_entryValidator.OnInvalidEntry += delegate { eventRaised = true; };
Assert.IsFalse(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
Assert.IsTrue(eventRaised);
}
In the second test you might want to validate that the event arguments were as expected too.
Also note that "invalid" is one word - so your test should be IsValidEntry_WithInvalidTicker. I'd also not bother with the setup - I'd just declare new local variables in each test.
I would restructure your class to make the RaiseInvalidEntryEvent virtual so it can be mocked in your IsValidEntry_WithInValidTicker and then verified it was called when the ticket was invalid.
Then I would have another test that verified RaiseInvalidEntryEvent called the anon delegate separately.
Unit tests should be as atomic as possible, and you would want to verify both of these behaviors in different tests.
public delegate void OnInvalidEntryMethod(ITnEntry entry, string message);
public class EntryValidator
{
public event OnInvalidEntryMethod OnInvalidEntry;
public bool IsValidEntry(ITnEntry entry, string ticker)
{
if (!IsFieldValid(entry, ticker.Trim().Length.ToString(), "0"))
return false;
return true;
}
private bool IsFieldValid(ITnEntry entry, string actual, string invalidValue)
{
if (actual == invalidValue)
{
RaiseInvalidEntryEvent(entry);
return false;
}
return true;
}
public virtual void RaiseInvalidEntryEvent(ITnEntry entry)
{
if (OnInvalidEntry != null)
OnInvalidEntry(entry, "Invalid entry in list: " + entry.List.Name + ".");
}
}
// Had to reverse engineer the following since they were not available in the question
public interface ITnEntry
{
Ticket List { get; set; }
string Ticker { get; set; }
}
public class TnEntry : ITnEntry
{
public Ticket List { get; set; }
public string Ticker { get; set; }
}
public class Ticket
{
public string Name { get; set; }
}
NOTE: Some OOP evangalists have fits when things are declared public instead of private, basically unit testing and TDD have some requirements that pure OOP is at odds with. I've made RaiseInvalidEntryEvent public for simplicity, but normally I would make this internal and then expose the assembly to the unit test via InternalsVisibleTo. I've been doing TDD for the last 4 years now and rarely use private anymore.
And the unit tests would quickly be (note, this is using the MSTEST framework from VS2012)
[TestClass]
public class UnitTest1
{
#region TestHelpers
private ITnEntry MakeEntry(string ticker)
{
return new TnEntry {Ticker = ticker, List = new Ticket()};
}
#endregion
[TestMethod]
public void IsValidEntry_WithValidValues_ReturnsTrue()
{
// ARRANGE
var target = new EntryValidator();
var selectedEntry = MakeEntry("BOL");
// ACT
bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
// ASSERT
Assert.IsTrue(actual);
}
[TestMethod]
public void IsValidEntry_WithInValidTicker_ReturnsFalse()
{
// ARRANGE
var target = new EntryValidator();
var selectedEntry = MakeEntry("");
// ACT
bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
// ASSERT
Assert.IsFalse(actual);
}
[TestMethod]
public void IsValidEntry_WithInvalidTicker_RaisesEvent()
{
// ARRANGE
// generate a dynamic mock which will stub all virtual methods
var target = Rhino.Mocks.MockRepository.GenerateMock<EntryValidator>();
var selectedEntry = MakeEntry("");
// ACT
bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
// ASSERT
// assert that RaiseInvalidEntryEvent was called
target.AssertWasCalled(x => x.RaiseInvalidEntryEvent(Arg<ITnEntry>.Is.Anything));
}
[TestMethod]
public void RaiseInvalidEntryEvent_WithValidHandler_CallsDelegate()
{
// ARRANGE
var target = new EntryValidator();
var selectedEntry = MakeEntry("");
bool delegateCalled = false;
// attach a handler to set delegateCalled to true
target.OnInvalidEntry += delegate
{
delegateCalled = true;
};
// ACT
target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
// ASSERT
Assert.IsTrue(delegateCalled);
}
}
Your test should subscribe to the event OnInvalidEntry with a dummy method, call IsValidEntry and check the result.

Wiring View, Model and Presenter dynamically, by convention / reflection

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
public interface IBaseView
{
void Show();
C Get<C>(string controlName) where C : Control; //Needed to later wire the events
}
public interface IView : IBaseView
{
TextBox ClientId { get; set; } //Need to expose this
Button SaveClient { get; set; }
ListBox MyLittleList { get; set; }
}
public partial class View : Form, IView
{
public TextBox ClientId //since I'm exposing it, my "concrete view" the controls are camelCased
{
get { return this.clientId; }
set { this.clientId = value; }
}
public Button SaveClient
{
get { return this.saveClient; }
set { this.saveClient = value; }
}
public ListBox MyLittleList
{
get { return this.myLittleList; }
set { this.myLittleList = value; }
}
//The view must also return the control to be wired.
public C Get<C>(string ControlName) where C : Control
{
var controlName = ControlName.ToLower();
var underlyingControlName = controlName[0] + ControlName.Substring(1);
var underlyingControl = this.Controls.Find(underlyingControlName, true).FirstOrDefault();
//It is strange because is turning PascalCase to camelCase. Could've used _Control for the controls on the concrete view instead
return underlyingControl as C;
}
Now the Presenter:
public class Presenter : BasePresenter <ViewModel, View>
{
Client client;
IView view;
ViewModel viewModel;
public Presenter(int clientId, IView viewParam, ViewModel viewModelParam)
{
this.view = viewParam;
this.viewModel = viewModelParam;
client = viewModel.FindById(clientId);
BindData(client);
wireEventsTo(view); //Implement on the base class
}
public void OnSaveClient(object sender, EventArgs e)
{
viewModel.Save(client);
}
public void OnEnter(object sender, EventArgs e)
{
MessageBox.Show("It works!");
}
public void OnMyLittleListChanged(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
}
The "magic" happens at the base class. In the wireEventsTo(IBaseView view)
public abstract class BasePresenter
<VM, V>
where VM : BaseViewModel
where V : IBaseView, new()
{
protected void wireEventsTo(IBaseView view)
{
Type presenterType = this.GetType();
Type viewType = view.GetType();
foreach (var method in presenterType.GetMethods())
{
var methodName = method.Name;
if (methodName.StartsWith("On"))
{
try
{
var presenterMethodName = methodName.Substring(2);
var nameOfMemberToMatch = presenterMethodName.Replace("Changed", ""); //ListBoxes wiring
var matchingMember = viewType.GetMember(nameOfMemberToMatch).FirstOrDefault();
if (matchingMember == null)
{
return;
}
if (matchingMember.MemberType == MemberTypes.Event)
{
wireMethod(view, matchingMember, method);
}
if (matchingMember.MemberType == MemberTypes.Property)
{
wireMember(view, matchingMember, method);
}
}
catch (Exception ex)
{
continue;
}
}
}
}
private void wireMember(IBaseView view, MemberInfo match, MethodInfo method)
{
var matchingMemberType = ((PropertyInfo)match).PropertyType;
if (matchingMemberType == typeof(Button))
{
var matchingButton = view.Get<Button>(match.Name);
var eventHandler = (EventHandler)EventHandler.CreateDelegate(typeof(EventHandler), this, method);
matchingButton.Click += eventHandler;
}
if (matchingMemberType == typeof(ListBox))
{
var matchinListBox = view.Get<ListBox>(match.Name);
var eventHandler = (EventHandler)EventHandler.CreateDelegate(typeof(EventHandler), this, method);
matchinListBox.SelectedIndexChanged += eventHandler;
}
}
private void wireMethod(IBaseView view, MemberInfo match, MethodInfo method)
{
var viewType = view.GetType();
var matchingEvent = viewType.GetEvent(match.Name);
if (matchingEvent != null)
{
if (matchingEvent.EventHandlerType == typeof(EventHandler))
{
var eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), this, method);
matchingEvent.AddEventHandler(view, eventHandler);
}
if (matchingEvent.EventHandlerType == typeof(FormClosedEventHandler))
{
var eventHandler = FormClosedEventHandler.CreateDelegate(typeof(FormClosedEventHandler), this, method);
matchingEvent.AddEventHandler(view, eventHandler);
}
}
}
}
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.
protected void BindData(Client client)
{
string nameOfPropertyBeingReferenced;
nameOfPropertyBeingReferenced = MVP.Controller.GetPropertyName(() => client.Id);
view.ClientId.BindTo(client, nameOfPropertyBeingReferenced);
nameOfPropertyBeingReferenced = MVP.Controller.GetPropertyName(() => client.FullName);
view.ClientName.BindTo(client, nameOfPropertyBeingReferenced);
}
public static void BindTo(this TextBox thisTextBox, object viewModelObject, string nameOfPropertyBeingReferenced)
{
Bind(viewModelObject, thisTextBox, nameOfPropertyBeingReferenced, "Text");
}
private static void Bind(object sourceObject, Control destinationControl, string sourceObjectMember, string destinationControlMember)
{
Binding binding = new Binding(destinationControlMember, sourceObject, sourceObjectMember, true, DataSourceUpdateMode.OnPropertyChanged);
//Binding binding = new Binding(sourceObjectMember, sourceObject, destinationControlMember);
destinationControl.DataBindings.Clear();
destinationControl.DataBindings.Add(binding);
}
public static string GetPropertyName<T>(Expression<Func<T>> exp)
{
return (((MemberExpression)(exp.Body)).Member).Name;
}
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.

Categories