I have a TargetedTriggerAction from a 3rd party library that would like to call/invoke without attaching it to a button. I have no problem getting it to work with the button, but I want to do it in response to some non-UI event.
Here is the action's class declaration:
public class MeasureAction : TargetedTriggerAction<Map>
Here is my setup code so far:
var measure = new MeasureAction();
measure.TargetObject = _mapControl;
measure.MeasureMode = MeasureAction.Mode.Polyline;
measure.MapUnits = DistanceUnit.Miles;
I want to be able to do something like this, but I know Invoke is protected:
measure.Invoke();
To invoke your trigger action, you need a trigger!
namespace TriggerTest
{
using System.Windows;
/// <summary>
/// A trigger that may be invoked from code.
/// </summary>
public class ManualTrigger : System.Windows.Interactivity.TriggerBase<DependencyObject>
{
/// <summary>
/// Invokes the trigger's actions.
/// </summary>
/// <param name="parameter">The parameter value.</param>
public void Invoke(object parameter)
{
this.InvokeActions(parameter);
}
}
}
The above is a trigger implementation that may be invoked without any UI dependencies. For example:
var measure = new MeasureAction();
measure.TargetObject = _mapControl;
measure.MeasureMode = MeasureAction.Mode.Polyline;
measure.MapUnits = DistanceUnit.Miles;
ManualTrigger trigger = new ManualTrigger();
trigger.Actions.Add(measure);
trigger.Invoke(null);
To make calling this even easier, you could add an extension method to TriggerAction
.
namespace TriggerTest
{
using System.Windows.Interactivity;
/// <summary>
/// Allows a trigger action to be invoked from code.
/// </summary>
public static class TriggerActionExtensions
{
/// <summary>
/// Invokes a <see cref="TriggerAction"/> with the specified parameter.
/// </summary>
/// <param name="action">The <see cref="TriggerAction"/>.</param>
/// <param name="parameter">The parameter value.</param>
public static void Invoke(this TriggerAction action, object parameter)
{
ManualTrigger trigger = new ManualTrigger();
trigger.Actions.Add(action);
try
{
trigger.Invoke(parameter);
}
finally
{
trigger.Actions.Remove(action);
}
}
/// <summary>
/// Invokes a <see cref="TriggerAction"/>.
/// </summary>
/// <param name="action">The <see cref="TriggerAction"/>.</param>
public static void Invoke(this TriggerAction action)
{
action.Invoke(null);
}
}
}
Now you can write what you really wanted:
var measure = new MeasureAction();
measure.TargetObject = _mapControl;
measure.MeasureMode = MeasureAction.Mode.Polyline;
measure.MapUnits = DistanceUnit.Miles;
measure.Invoke();
Is the 3rd party class sealed?
If not, the "best" answer would probably be to derive your own class from MeasureAction
and add a invoker (keep it internal/protected if you want to keep things clean).
If it's sealed/otherwise frozen, your best bet is probably Reflection - there are ways to cache/otherwise speed up the cost of this call, but the basic code would be something like:
// Paste in the code in the original question here, name of instance == measure
var actionMethod = typeof(MeasureAction)
.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
.FirstOrDefault(meth => meth.Name == "Invoke");
if(actionMethod != null)
{
var result = actionMethod.Invoke(measure, null);
}
Old post but may help. I did like this
public class MyMeasureAction : MeasureAction
{
public void Execute()
{
OnTargetChanged(null, (Map)TargetObject);
Invoke(null);
}
}
...
var mymeasure = new MyMeasureAction();
mymeasure.TargetObject = MyMap;
mymeasure.xxxx = xxxx
....
mymeasure.Execute();
The OnTargetChanged() method will initialize the internal Map of the MeasureAction