MVVM Passing EventArgs As Command Parameter

2019-01-02 16:54发布

I'm using Microsoft Expression Blend 4
I have a Browser ..,

[ XAML ] ConnectionView " Empty Code Behind "

        <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# ] AttachedProperties class

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# ] ConnectionViewModel Class

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# ] ViewModelBase class Here

Finally :
Binding with commands works well and MessageBoxes shown


My Question :
How to pass NavigationEventArgs as Command Parameters when Navigated Event occurs ?

11条回答
十年一品温如言
2楼-- · 2019-01-02 17:50

To add to what joshb has stated already - this works just fine for me. Make sure to add references to Microsoft.Expression.Interactions.dll and System.Windows.Interactivity.dll and in your xaml do:

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

I ended up using something like this for my needs. This shows that you can also pass a custom parameter:

<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>
查看更多
浮光初槿花落
3楼-- · 2019-01-02 17:50

As an adaption of @Mike Fuchs answer, here's an even smaller solution. I'm using the Fody.AutoDependencyPropertyMarker to reduce some of the boiler plate.

The Class

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

The EventArgs

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

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

The XAML

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

The ViewModel

public ICommand VisibleBoundsChanged => _visibleBoundsChanged ??
                                        (_visibleBoundsChanged = new RelayCommand(obj => SetVisibleBounds(((VisibleBoundsArgs)obj).VisibleVounds)));
查看更多
爱死公子算了
4楼-- · 2019-01-02 17:52

I've always come back here for the answer so I wanted to make a short simple one to go to.

There are multiple ways of doing this:

1. Using WPF Tools. Easiest.

Add Namespaces:

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

XAML:

Use the EventName to call the event you want then specify your Method name in the 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. Using MVVMLight. Most difficult.

Install GalaSoft NuGet package.

enter image description here

Get the namespaces:

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

XAML:

Use the EventName to call the event you want then specify your Command name in your binding. If you want to pass the arguments of the method, mark PassEventArgsToCommand to 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>

Code Implementing Delegates: Source

You must get the Prism MVVM NuGet package for this.

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 Without 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. Using Telerik EventToCommandBehavior. It's an option.

You'll have to download it's NuGet Package.

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
}
查看更多
步步皆殇っ
5楼-- · 2019-01-02 17:55

For people just finding this post, you should know that in newer versions (not sure on the exact version since official docs are slim on this topic) the default behavior of the InvokeCommandAction, if no CommandParameter is specified, is to pass the args of the event it's attached to as the CommandParameter. So the originals poster's XAML could be simply written as:

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

Then in your command, you can accept a parameter of type NavigationEventArgs (or whatever event args type is appropriate) and it will automatically be provided.

查看更多
浪荡孟婆
6楼-- · 2019-01-02 17:57

With Behaviors and Actions in Blend for Visual Studio 2013 you can use the InvokeCommandAction. I tried this with the Drop event and although no CommandParameter was specified in the XAML, to my surprise, the Execute Action parameter contained the DragEventArgs. I presume this would happen for other events but have not tested them.

查看更多
登录 后发表回答