Programmatically Call WPF TargetedTriggerAction

2019-05-06 23:42发布

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();

3条回答
相关推荐>>
2楼-- · 2019-05-07 00:14

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();
查看更多
小情绪 Triste *
3楼-- · 2019-05-07 00:27

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

查看更多
别忘想泡老子
4楼-- · 2019-05-07 00:30

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);
}
查看更多
登录 后发表回答