As you can see in this discussion, it is not possible to pass a “dotted” expression in an ActionMessage parameter with CM. This is due to the fact that supporting this in a complete and consistent way will force caliburn micro to grow over the micro size. Let’s have a concrete example: we want to invoke an action on the StrokeCollected event of an InkCanvas, and we want to pass the Stroke property of the EventArgs.Here below the wanted syntax:
<InkCanvas>
<i:Interaction.Triggers>
<i:EventTrigger EventName="StrokeCollected">
<cl:ActionMessage MethodName="StrokeCollected" >
<cl:Parameter Value="$eventArgs.Stroke">
</cl:Parameter>
</cl:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</InkCanvas>
If we use the standard behavior, only the $eventArgs portion of the expression is resolved, adding the dot forces CM to just emit the whole string as a parameter. Since we can deal in an application with a simplified strategy, that not necessary cover all scenarios, but is good enough for our purpose, we can redefine the MessageBinder.EvaluateParameter strategy:
var microEvaluator = MessageBinder.EvaluateParameter;
MessageBinder.EvaluateParameter = (s, t, c) =>
{
string[] dotted = s.Split('.');
if (dotted.Length == 1)
{
// use default CM strategy
return microEvaluator(s, t, c);
}
else
{
// let's CM happy with any type
var first = microEvaluator(dotted[0], typeof(object), c);
var dw = Dig(first,dotted.Skip(1));
return dw;
}
};
As you can see, we leverage the inner evaluator in the case we have a non dotted separated argument, or for the first chunk of the expression if the dot exists. The Dig function, by the old plain reflection work recursively to return the proper value:
private object Dig(object first, IEnumerable<string> iEnumerable)
{
if (iEnumerable.Count() == 0)
return first;
else
{
PropertyInfo pi = first.GetType().GetProperty(iEnumerable.First());
if( null == pi )
{
Exception e = new Exception("Property not found:"+iEnumerable.First());
LogManager.GetLog(GetType()).Error(e);
throw e;
}
return Dig(pi.GetValue(first, null),iEnumerable.Skip(1));
}
}
As you can see it is pretty simple, despites some fault intolerance, that we can accept since this code will not be part of the library, but is under our control.