2
votes

I am trying to attach a command and a commandparameter to a textbox on return key but without success. The parameter is the current text in the same textbox.

<TextBox x:Name="txtSearch">
    <TextBox.InputBindings>
         <KeyBinding  Command="{Binding SearchCommand}" 
                      CommandParameter="{Binding Path=Text, ElementName=txtSearch}" Key="Return" />
    </TextBox.InputBindings>
</TextBox>

Basically I want to execute the command when user clicks on return/enter key and pass as a parameter the current text in the textbox.

I have found this link where it is said that in .NET 3.5 command parameter for keybinding is not accepting bindings. So a solution is proposed by code in code-behind but how can I pass a parameter to the command from the code?

1

1 Answers

2
votes

First, you'll need to add the KeyBinding to your TextBox and set its Command on code-behind. Just add this in the constructor of your View:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MyViewModel();

    KeyBinding kb = new KeyBinding();
    kb.Command = (DataContext as MyViewModel).SearchCommand;                        
    kb.Key = Key.Enter;
    txtSearch.InputBindings.Add(kb);
}

Then, you can bind the Text property of the TextBox named txtSearch to a property of your ViewModel. This way you don't need to pass a parameter as you can use the value of that property in your ViewModel inside the code that executes your Command.

Your ViewModel should look like this:

public class MyViewModel : ObservableObject
{
    private string _txtSearch;
    public string TxtSearch
    {
        get { return _txtSearch; }
        set
        {
            if (value != _txtSearch)
            {
                _txtSearch = value;
                OnPropertyChanged("TxtSearch");
            }
        }
    }

    private ICommand _searchCommand;
    public ICommand SearchCommand
    {
        get
        {
            if (_searchCommand == null)
            {
                _searchCommand = new RelayCommand(p => canSearch(), p => search());
            }
            return _searchCommand;
        }
    }
    private bool canSearch()
    {
        //implement canExecute logic.
    }
    private void search()
    {
        string text = TxtSearch; //here you'll have the string that represents the text of the TextBox txtSearch
        //DoSomething
    }
}

If you have access to C# 6 (Visual Studio 2015 and later versions), you can alter the call to the OnPropertyChanged to: OnPropertyChanged(nameof(TxtSearch));. This way you get rid of the "magic string" and eventual renaming of the property won't cause any problem for you.

And then your XAML should look like this: (Notice that you need to specify that te UpdateSourceTrigger must be PropertyChanged, so that your TxtSearch property of your ViewModel stays up to date when you hit the Enter key on your TextBox.

<TextBox Text="{Binding TxtSearch, UpdateSourceTrigger=PropertyChanged}" x:Name="txtSearch"/>    

Your ViewModel needs to implement INotifyPropertyChanged and you need a proper ICommand implementation. Here I'll use the RelayCommand.

Those implementations are shown below.

Since your framework is .NET 3.5, implement it like this:

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

This is a implementation of the RelayCommand:

public class RelayCommand : ICommand
{
    private Predicate<object> _canExecute;
    private Action<object> _execute;

    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        _canExecute = canExecute;
        _execute = execute;
    }

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

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

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}