Guilian
Guilian

Reputation: 129

detect which button was clicked from ViewModel WPF

there are many buttons on the Main window of my WPF-App. The commands of those buttons should have the same implementation/function but depending on which button has been pressed, the file/path on which the function accesses changes. How to detect which button was clicked from ViewModel without using button click event handler (Windows forms)?

Here is the implementation of class RelayCommand:

public class RelayCommand : ICommand
{

    readonly Func<Boolean> _canExecute;
    readonly Action _execute;


    public RelayCommand(Action execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action execute, Func<Boolean> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }


    public event EventHandler CanExecuteChanged
    {
        add
        {

            if (_canExecute != null)
                CommandManager.RequerySuggested += value;
        }

        remove
        {

            if (_canExecute != null)
                CommandManager.RequerySuggested -= value;
        }
    }


    public Boolean CanExecute(Object parameter)
    {
        return _canExecute == null ? true : _canExecute();
    }

    public void Execute(Object parameter)
    {
        _execute();
    }
}

Here is the code of the command in ViewModel:

void DoThisWorkExecute()
    {
        // if Button1 is clicked...do this

        // if Button2 is clicked...do this
    }

    bool CanDoThisWorkExecute()
    {
        return true;
    }

    public ICommand ButtonCommand { get { return new RelayCommand(DoThisWorkExecute, CanDoThisWorkExecute); } }

Upvotes: 0

Views: 2258

Answers (1)

Mat
Mat

Reputation: 2072

You can use the CommandParameter. Something like that:

<Button Content="Open" Command="{Binding Path=ButtonCommand}" CommandParameter="Open"/>
<Button Content="Save" Command="{Binding Path=ButtonCommand}" CommandParameter="Save"/>

For this you need a slightly different implementation of the RelayCommand

/// <summary>
/// https://gist.github.com/schuster-rainer/2648922 
/// Implementation from Josh Smith of the RelayCommand
/// </summary>
public class RelayCommand : ICommand
{
    #region Fields

    readonly Predicate<object> _canExecute;
    readonly Action<object> _execute;
    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="RelayCommand"/> class.
    /// </summary>
    /// <param name="execute">The execute.</param>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="RelayCommand"/> class.
    /// </summary>
    /// <param name="execute">The execute.</param>
    /// <param name="canExecute">The can execute.</param>
    /// <exception cref="System.ArgumentNullException">execute</exception>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members


    /// <summary>
    /// Occurs when changes occur that affect whether or not the command should execute.
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    /// <summary>
    /// Defines the method that determines whether the command can execute in its current state.
    /// </summary>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    /// <returns>true if this command can be executed; otherwise, false.</returns>
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }
    /// <summary>
    /// Defines the method to be called when the command is invoked.
    /// </summary>
    /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}

However: Instead of asking which button was clicked, I would make a Command for each separate action (e.g. open, save, exit). You will have much less troubles when reusing your commands (context menu, KeyBindings, toolbar, etc.). You would always have to provide the ui element. This really breaks the MVVM pattern. You really have to get rid of the old winforms approach, in order to use the full power of the RelayCommand.

I wrote myself a code snippet so I don't have to write all the code.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>RelayCommand</Title>
            <Shortcut>RelayCommand</Shortcut>
            <Description>Code snippet for usage of the Relay Command pattern</Description>
            <Author>Mat</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>name</ID>
                    <ToolTip>Name of the command</ToolTip>
                    <Default>Save</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[   private RelayCommand _$name$Command;
        public ICommand $name$Command
        {
            get
            {
                if (_$name$Command == null)
                {
                    _$name$Command = new RelayCommand(param => this.$name$(param),
                        param => this.Can$name$(param));
                }
                return _$name$Command;
            }
        }

        private bool Can$name$(object param)
        {
            return true;
        }

        private void $name$(object param)
        {
            MessageServiceHelper.RegisterMessage(new NotImplementedException());
        }]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

see also https://msdn.microsoft.com/en-us/library/z41h7fat.aspx

Upvotes: 3

Related Questions