web-dev-qa-db-fra.com

MVVM Passing EventArgs en tant que paramètre de commande

J'utilise Microsoft Expression Blend 4
J'ai un navigateur ..,

[XAML] ConnectionView "Code vide derrière"

        <WebBrowser local:AttachedProperties.BrowserSource="{Binding Source}">
            <i:Interaction.Triggers>
                <i:EventTrigger>
                    <i:InvokeCommandAction Command="{Binding LoadedEvent}"/>
                </i:EventTrigger>
                <i:EventTrigger EventName="Navigated">
                    <i:InvokeCommandAction Command="{Binding NavigatedEvent}" CommandParameter="??????"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </WebBrowser>  

[C #] Classe de propriétés attachées

public static class AttachedProperties
    {
        public static readonly DependencyProperty BrowserSourceProperty = DependencyProperty . RegisterAttached ( "BrowserSource" , typeof ( string ) , typeof ( AttachedProperties ) , new UIPropertyMetadata ( null , BrowserSourcePropertyChanged ) );

        public static string GetBrowserSource ( DependencyObject _DependencyObject )
        {
            return ( string ) _DependencyObject . GetValue ( BrowserSourceProperty );
        }

        public static void SetBrowserSource ( DependencyObject _DependencyObject , string Value )
        {
            _DependencyObject . SetValue ( BrowserSourceProperty , Value );
        }

        public static void BrowserSourcePropertyChanged ( DependencyObject _DependencyObject , DependencyPropertyChangedEventArgs _DependencyPropertyChangedEventArgs )
        {
            WebBrowser _WebBrowser = _DependencyObject as WebBrowser;
            if ( _WebBrowser != null )
            {
                string URL = _DependencyPropertyChangedEventArgs . NewValue as string;
                _WebBrowser . Source = URL != null ? new Uri ( URL ) : null;
            }
        }
    }

[C #] Classe ViewViewModel

public class ConnectionViewModel : ViewModelBase
    {
            public string Source
            {
                get { return Get<string> ( "Source" ); }
                set { Set ( "Source" , value ); }
            }

            public void Execute_ExitCommand ( )
            {
                Application . Current . Shutdown ( );
            }

            public void Execute_LoadedEvent ( )
            {
                MessageBox . Show ( "___Execute_LoadedEvent___" );
                Source = ...... ;
            }

            public void Execute_NavigatedEvent ( )
            {
                MessageBox . Show ( "___Execute_NavigatedEvent___" );
            }
    }

[C #] classe ViewModelBaseIci

Finalement :
La liaison avec les commandes fonctionne bien et les boîtes de message sont affichées


Ma question:
Comment passer NavigationEventArgs en tant que paramètres de commande en cas d'événement de navigation?

59
Ahmed Ghoneim

Ce n'est pas facilement supporté. Voici n article avec des instructions sur la manière de passer EventArgs en tant que paramètres de commande.

Vous voudrez peut-être envisager d'utiliser MVVMLight - il supporte EventArgs directement dans la commande; votre situation ressemblerait à quelque chose comme ça:

 <i:Interaction.Triggers>
    <i:EventTrigger EventName="Navigated">
        <cmd:EventToCommand Command="{Binding NavigatedEvent}"
            PassEventArgsToCommand="True" />
    </i:EventTrigger>
 </i:Interaction.Triggers>
61
E.Z. Hart

J'essaie de garder mes dépendances au minimum, alors je l'ai implémenté moi-même au lieu d'utiliser EventToCommand de MVVMLight. Cela fonctionne pour moi jusqu'à présent, mais les commentaires sont les bienvenus.

Xaml:

<i:Interaction.Behaviors>
    <beh:EventToCommandBehavior Command="{Binding DropCommand}" Event="Drop" PassArguments="True" />
</i:Interaction.Behaviors>

ViewModel:

public ActionCommand<DragEventArgs> DropCommand { get; private set; }

this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);

private void OnDrop(DragEventArgs e)
{
    // ...
}

EventToCommandBehavior:

/// <summary>
/// Behavior that will connect an UI event to a viewmodel Command,
/// allowing the event arguments to be passed as the CommandParameter.
/// </summary>
public class EventToCommandBehavior : Behavior<FrameworkElement>
{
    private Delegate _handler;
    private EventInfo _oldEvent;

    // Event
    public string Event { get { return (string)GetValue(EventProperty); } set { SetValue(EventProperty, value); } }
    public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));

    // Command
    public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } }
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));

    // PassArguments (default: false)
    public bool PassArguments { get { return (bool)GetValue(PassArgumentsProperty); } set { SetValue(PassArgumentsProperty, value); } }
    public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));


    private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var beh = (EventToCommandBehavior)d;

        if (beh.AssociatedObject != null) // is not yet attached at initial load
            beh.AttachHandler((string)e.NewValue);
    }

    protected override void OnAttached()
    {
        AttachHandler(this.Event); // initial set
    }

    /// <summary>
    /// Attaches the handler to the event
    /// </summary>
    private void AttachHandler(string eventName)
    {
        // detach old event
        if (_oldEvent != null)
            _oldEvent.RemoveEventHandler(this.AssociatedObject, _handler);

        // attach new event
        if (!string.IsNullOrEmpty(eventName))
        {
            EventInfo ei = this.AssociatedObject.GetType().GetEvent(eventName);
            if (ei != null)
            {
                MethodInfo mi = this.GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
                _handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
                ei.AddEventHandler(this.AssociatedObject, _handler);
                _oldEvent = ei; // store to detach in case the Event property changes
            }
            else
                throw new ArgumentException(string.Format("The event '{0}' was not found on type '{1}'", eventName, this.AssociatedObject.GetType().Name));
        }
    }

    /// <summary>
    /// Executes the Command
    /// </summary>
    private void ExecuteCommand(object sender, EventArgs e)
    {
        object parameter = this.PassArguments ? e : null;
        if (this.Command != null)
        {
            if (this.Command.CanExecute(parameter))
                this.Command.Execute(parameter);
        }
    }
}

ActionCommand:

public class ActionCommand<T> : ICommand
{
    public event EventHandler CanExecuteChanged;
    private Action<T> _action;

    public ActionCommand(Action<T> action)
    {
        _action = action;
    }

    public bool CanExecute(object parameter) { return true; }

    public void Execute(object parameter)
    {
        if (_action != null)
        {
            var castParameter = (T)Convert.ChangeType(parameter, typeof(T));
            _action(castParameter);
        }
    }
}
40
Mike Fuchs

Je reviens toujours ici pour la réponse, alors je voulais en faire une courte et simple.

Il y a plusieurs façons de le faire:

1. Utilisation des outils WPF. Plus facile.

Ajouter des espaces de noms:

  • System.Windows.Interactivitiy
  • Microsoft.Expression.Interactions

XAML:

Utilisez le EventName pour appeler l'événement de votre choix, puis spécifiez votre nom Method dans le MethodName.

<Window>
    xmlns:wi="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
    xmlns:ei="http://schemas.Microsoft.com/expression/2010/interactions">

    <wi:Interaction.Triggers>
        <wi:EventTrigger EventName="SelectionChanged">
            <ei:CallMethodAction
                TargetObject="{Binding}"
                MethodName="ShowCustomer"/>
        </wi:EventTrigger>
    </wi:Interaction.Triggers>
</Window>

Code:

public void ShowCustomer()
{
    // Do something.
}

2. Utilisation de MVVMLight. Plus difficile.

Installez le paquet GalaSoft NuGet.

enter image description here

Obtenez les espaces de noms:

  • System.Windows.Interactivity
  • GalaSoft.MvvmLight.Platform

XAML:

Utilisez EventName pour appeler l'événement de votre choix, puis spécifiez votre nom Command dans votre liaison. Si vous voulez passer les arguments de la méthode, marquez PassEventArgsToCommand à true.

<Window>
    xmlns:wi="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
    xmlns:cmd="http://www.galasoft.ch/mvvmlight">

    <wi:Interaction.Triggers>
       <wi:EventTrigger EventName="Navigated">
           <cmd:EventToCommand Command="{Binding CommandNameHere}"
               PassEventArgsToCommand="True" />
       </wi:EventTrigger>
    </wi:Interaction.Triggers>
</Window>

Délégués chargés de la mise en œuvre du code: Source

Vous devez obtenir le package Prism MVVM NuGet à cet effet.

enter image description here

using Microsoft.Practices.Prism.Commands;

// With params.
public DelegateCommand<string> CommandOne { get; set; }
// Without params.
public DelegateCommand CommandTwo { get; set; }

public MainWindow()
{
    InitializeComponent();

    // Must initialize the DelegateCommands here.
    CommandOne = new DelegateCommand<string>(executeCommandOne);
    CommandTwo = new DelegateCommand(executeCommandTwo);
}

private void executeCommandOne(string param)
{
    // Do something here.
}

private void executeCommandTwo()
{
    // Do something here.
}

Code sans DelegateCommand: Source

using GalaSoft.MvvmLight.CommandWpf

public MainWindow()
{
    InitializeComponent();

    CommandOne = new RelayCommand<string>(executeCommandOne);
    CommandTwo = new RelayCommand(executeCommandTwo);
}

public RelayCommand<string> CommandOne { get; set; }

public RelayCommand CommandTwo { get; set; }

private void executeCommandOne(string param)
{
    // Do something here.
}

private void executeCommandTwo()
{
    // Do something here.
}

3. Utilisation de Telerik EventToCommandBehavior . C'est une option.

Vous devrez télécharger c'est paquet NuGet .

XAML:

<i:Interaction.Behaviors>
    <telerek:EventToCommandBehavior
         Command="{Binding DropCommand}"
         Event="Drop"
         PassArguments="True" />
</i:Interaction.Behaviors>

Code:

public ActionCommand<DragEventArgs> DropCommand { get; private set; }

this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);

private void OnDrop(DragEventArgs e)
{
    // Do Something
}
19
AzzamAziz

Je sais que la question est assez ancienne, mais j’ai rencontré le même problème aujourd’hui et je n’étais pas trop intéressée à référencer l’ensemble de MVVMLight pour pouvoir utiliser les déclencheurs d’événement avec des arguments d’événement. J'ai utilisé MVVMLight par le passé et c'est un excellent framework, mais je ne veux plus l'utiliser pour mes projets.

Ce que j'ai fait pour résoudre ce problème a été de créer un ULTRA minimal, EXTRÊMEMENT personnalisable adaptable action de déclenchement qui me permettrait de me lier à la commande et de fournir un convertisseur d'argument d'événement pour transmettre les arguments aux fonctions CanExecute et Execute de la commande. Vous ne voulez pas transmettre les arguments d'événement tels quels, car des types de couche de vue seraient alors envoyés à la couche de modèle de vue (ce qui ne devrait jamais arriver dans MVVM).

Voici la classe EventCommandExecuter que j'ai créée:

public class EventCommandExecuter : TriggerAction<DependencyObject>
{
    #region Constructors

    public EventCommandExecuter()
        : this(CultureInfo.CurrentCulture)
    {
    }

    public EventCommandExecuter(CultureInfo culture)
    {
        Culture = culture;
    }

    #endregion

    #region Properties

    #region Command

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));

    #endregion

    #region EventArgsConverterParameter

    public object EventArgsConverterParameter
    {
        get { return (object)GetValue(EventArgsConverterParameterProperty); }
        set { SetValue(EventArgsConverterParameterProperty, value); }
    }

    public static readonly DependencyProperty EventArgsConverterParameterProperty =
        DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));

    #endregion

    public IValueConverter EventArgsConverter { get; set; }

    public CultureInfo Culture { get; set; }

    #endregion

    protected override void Invoke(object parameter)
    {
        var cmd = Command;

        if (cmd != null)
        {
            var param = parameter;

            if (EventArgsConverter != null)
            {
                param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
            }

            if (cmd.CanExecute(param))
            {
                cmd.Execute(param);
            }
        }
    }
}

Cette classe a deux propriétés de dépendance, une pour autoriser la liaison à la commande de votre modèle de vue, l'autre vous permettant de lier la source de l'événement si vous en avez besoin lors de la conversion des arguments d'événement. Vous pouvez également définir des paramètres de culture si nécessaire (ils correspondent par défaut à la culture actuelle de l'interface utilisateur).

Cette classe vous permet d'adapter les arguments d'événement afin qu'ils puissent être utilisés par la logique de commande de votre modèle de vue. Cependant, si vous voulez simplement passer les arguments d'événement en mot, n'indiquez tout simplement pas de convertisseur d'arguments d'événement.

L’utilisation la plus simple de cette action de déclenchement en XAML est la suivante:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="NameChanged">
        <cmd:EventCommandExecuter Command="{Binding Path=Update, Mode=OneTime}" EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

Si vous aviez besoin d’un accès à la source de l’événement, vous devez vous connecter au propriétaire de l’événement.

<i:Interaction.Triggers>
    <i:EventTrigger EventName="NameChanged">
        <cmd:EventCommandExecuter 
            Command="{Binding Path=Update, Mode=OneTime}" 
            EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"
            EventArgsConverterParameter="{Binding ElementName=SomeEventSource, Mode=OneTime}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

(cela suppose que le nœud XAML auquel vous attachez les déclencheurs a été assigné x:Name="SomeEventSource"

Ce XAML repose sur l'importation de certains espaces de noms requis

xmlns:cmd="clr-namespace:MyProject.WPF.Commands"
xmlns:c="clr-namespace:MyProject.WPF.Converters"
xmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"

et créer un IValueConverter (appelé NameChangedArgsToStringConverter dans ce cas) pour gérer la logique de conversion réelle. Pour les convertisseurs de base, je crée généralement une instance de convertisseur par défaut static readonly, Que je peux ensuite référencer directement en XAML, comme je l’ai fait ci-dessus.

L'avantage de cette solution est que vous n'avez vraiment besoin d'ajouter qu'une seule classe à un projet pour utiliser le cadre d'interaction de la même manière que vous l'utiliseriez avec InvokeCommandAction. L'ajout d'une classe unique (d'environ 75 lignes) devrait être de loin préférable à une bibliothèque entière pour obtenir des résultats identiques.

NOTE

ceci est un peu similaire à la réponse de @adabyron mais il utilise des déclencheurs d'événements au lieu de comportements. Cette solution fournit également une capacité de conversion d’événements, mais pas que la solution de @ adabyron n’ait pas pu le faire aussi bien. Je n'ai vraiment aucune bonne raison pour laquelle je préfère les déclencheurs aux comportements, juste un choix personnel. La stratégie de l'OMI est un choix raisonnable.

12
pjs

Pour ceux qui viennent de trouver ce post, sachez que dans les versions les plus récentes (pas sûr de la version exacte, car les documents officiels sont minces sur ce sujet), le comportement par défaut de InvokeCommandAction, si aucun paramètre CommandParameter n'est spécifié, consiste à transmettre les arguments du événement auquel il est attaché en tant que CommandParameter. Ainsi, le XAML de l’affiche originale pourrait s’écrire simplement comme suit:

<i:Interaction.Triggers>
  <i:EventTrigger EventName="Navigated">
    <i:InvokeCommandAction Command="{Binding NavigatedEvent}"/>
  </i:EventTrigger>
</i:Interaction.Triggers>

Ensuite, dans votre commande, vous pouvez accepter un paramètre de type NavigationEventArgs (ou le type d'argument d'événement approprié) et il sera automatiquement fourni.

12
joshb

Pour ajouter à ce que joshb a déjà déclaré - cela fonctionne très bien pour moi. Assurez-vous d’ajouter des références à Microsoft.Expression.Interactions.dll et System.Windows.Interactivity.dll et dans votre xaml faire:

    xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"

J'ai fini par utiliser quelque chose comme ça pour mes besoins. Cela montre que vous pouvez également transmettre un paramètre personnalisé:

<i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">

                <i:InvokeCommandAction Command="{Binding Path=DataContext.RowSelectedItem, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 
                                       CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
            </i:EventTrigger>
</i:Interaction.Triggers>
5
EbbnFlow

Je ne pense pas que vous puissiez le faire facilement avec le InvokeCommandAction - je jetterais un coup d'œil à EventToCommand de MVVMLight ou similaire.

3
Tim

Avec les comportements et les actions de Blend pour Visual Studio 2013, vous pouvez utiliser InvokeCommandAction. J'ai essayé ceci avec l'événement Drop et bien qu'aucun CommandParameter n'ait été spécifié dans le code XAML, à ma grande surprise, le paramètre Execute Action contenait le DragEventArgs. Je présume que cela se produirait pour d'autres événements mais je ne les ai pas testés.

1
OzFrog

InvokeCommandAction de Prism passera l'argument d'événement par défaut si CommandParameter n'est pas défini.

https://docs.Microsoft.com/en-us/previous-versions/msp-n-p/gg405494 (v = pandp.40) # passage-eventargs-parameters-à-la-commande

Voici un exemple. Notez l'utilisation de prism:InvokeCommandAction au lieu de i:InvokeCommandAction.

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Sorting">
        <prism:InvokeCommandAction Command="{Binding SortingCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
1
datchung

Voici une version de la réponse de @ adabyron qui empêche l'abstraction qui fuit EventArgs.

Tout d’abord, la classe EventToCommandBehavior modifiée (maintenant une classe abstraite générique et formatée avec le nettoyage de code ReSharper). Notez la nouvelle méthode virtuelle GetCommandParameter et son implémentation par défaut:

public abstract class EventToCommandBehavior<TEventArgs> : Behavior<FrameworkElement>
    where TEventArgs : EventArgs
{
    public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null, OnEventChanged));
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null));
    public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(false));
    private Delegate _handler;
    private EventInfo _oldEvent;

    public string Event
    {
        get { return (string)GetValue(EventProperty); }
        set { SetValue(EventProperty, value); }
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public bool PassArguments
    {
        get { return (bool)GetValue(PassArgumentsProperty); }
        set { SetValue(PassArgumentsProperty, value); }
    }

    protected override void OnAttached()
    {
        AttachHandler(Event);
    }

    protected virtual object GetCommandParameter(TEventArgs e)
    {
        return e;
    }

    private void AttachHandler(string eventName)
    {
        _oldEvent?.RemoveEventHandler(AssociatedObject, _handler);

        if (string.IsNullOrEmpty(eventName))
        {
            return;
        }

        EventInfo eventInfo = AssociatedObject.GetType().GetEvent(eventName);

        if (eventInfo != null)
        {
            MethodInfo methodInfo = typeof(EventToCommandBehavior<TEventArgs>).GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);

            _handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
            eventInfo.AddEventHandler(AssociatedObject, _handler);
            _oldEvent = eventInfo;
        }
        else
        {
            throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().FullName}'.");
        }
    }

    private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = (EventToCommandBehavior<TEventArgs>)d;

        if (behavior.AssociatedObject != null)
        {
            behavior.AttachHandler((string)e.NewValue);
        }
    }

    // ReSharper disable once UnusedMember.Local
    // ReSharper disable once UnusedParameter.Local
    private void ExecuteCommand(object sender, TEventArgs e)
    {
        object parameter = PassArguments ? GetCommandParameter(e) : null;

        if (Command?.CanExecute(parameter) == true)
        {
            Command.Execute(parameter);
        }
    }
}

Ensuite, un exemple de classe dérivée qui cache DragCompletedEventArgs. Certaines personnes ont exprimé leur inquiétude à l'idée de laisser filtrer l'abstraction EventArgs dans leur modèle de vue Assembly. Pour éviter cela, j'ai créé une interface qui représente les valeurs qui nous intéressent. L'interface peut vivre dans la vue modèle Assembly avec l'implémentation privée dans la UI Assembly:

// UI Assembly
public class DragCompletedBehavior : EventToCommandBehavior<DragCompletedEventArgs>
{
    protected override object GetCommandParameter(DragCompletedEventArgs e)
    {
        return new DragCompletedArgs(e);
    }

    private class DragCompletedArgs : IDragCompletedArgs
    {
        public DragCompletedArgs(DragCompletedEventArgs e)
        {
            Canceled = e.Canceled;
            HorizontalChange = e.HorizontalChange;
            VerticalChange = e.VerticalChange;
        }

        public bool Canceled { get; }
        public double HorizontalChange { get; }
        public double VerticalChange { get; }
    }
}

// View model Assembly
public interface IDragCompletedArgs
{
    bool Canceled { get; }
    double HorizontalChange { get; }
    double VerticalChange { get; }
}

Convertissez le paramètre de commande en IDragCompletedArgs, comme dans la réponse de @ adabyron.

0
NathanAldenSr

Ce que je fais est d'utiliser InvokeCommandAction pour lier l'événement de contrôle chargé à une commande dans le modèle de vue, donner l'axe de contrôle: Nom dans Xaml et le transmettre en tant que CommandParameter, puis dans les gestionnaires de modèle de vue liés aux crochets de commande chargés jusqu'aux événements dont j'ai besoin. pour obtenir les arguments de l'événement.

0
DRL

En réponse à la réponse de @Mike Fuchs, voici une solution encore plus petite. J'utilise le Fody.AutoDependencyPropertyMarker pour réduire une partie de la plaque de la chaudière.

La classe

public class EventCommand : TriggerAction<DependencyObject>
{
    [AutoDependencyProperty]
    public ICommand Command { get; set; }

    protected override void Invoke(object parameter)
    {
        if (Command != null)
        {
            if (Command.CanExecute(parameter))
            {
                Command.Execute(parameter);
            }
        }
    }
}

Les EventArgs

public class VisibleBoundsArgs : EventArgs
{
    public Rect VisibleVounds { get; }

    public VisibleBoundsArgs(Rect visibleBounds)
    {
        VisibleVounds = visibleBounds;
    }
}

Le XAML

<local:ZoomableImage>
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="VisibleBoundsChanged" >
         <local:EventCommand Command="{Binding VisibleBoundsChanged}" />
      </i:EventTrigger>
   </i:Interaction.Triggers>
</local:ZoomableImage>

Le ViewModel

public ICommand VisibleBoundsChanged => _visibleBoundsChanged ??
                                        (_visibleBoundsChanged = new RelayCommand(obj => SetVisibleBounds(((VisibleBoundsArgs)obj).VisibleVounds)));
0
Ralt