bindingcommandcode-behindkey-bindings

KeyBinding to a function in code behind? WPF/MVVM


I have a TextBox, when user presses Enter on their keyboard the value in textbox gets "confirmed" and the string is formatted to the correct amount of decimals.

So if the textbox should have 1 decimal and the user writes "30" without any decimals and presses Enter, then the textbox is automatically updated to "30.0".

The problem is that the CaretIndex is placed at positon 0 when this happens. If i press enter with the Caret after 0 like "30|" then it gets reset to "|30.0" instead of "30.0|" how i want it.

I have a command that gets fired when enter is pressed inside the textbox. However, the command is inside the View Model, and i should not be touching View things (the caret) inside View Model. So how should i go on about doing it?

I was thinking of instead of binding to a command in view model i bind to a function in view (code behind) and from that function i raise the command in VM and set caret, like so:

    private void EnterPressed()
    {
        ((ParamTextNodeVM)DataContext).EnterCmd.Execute(null);
        ValueBox.CaretIndex = ValueBox.Text.Length;
    }

However, this does not work:

   <TextBox>
       <TextBox.InputBindings>
           <KeyBinding Key="Enter" Command="{Binding EnterPressed}"/>
       </TextBox.InputBindings>
    </TextBox>

How do i bind "Command" to "EnterPressed" that is inside code behind?


Solution

  • Binding to a function in the code behind would work, but would violate the MVVM pattern. A relay command allows you to bind a command in the view to the view model.

     /// <summary>
    /// A basic command that runs an Action
    /// </summary>
    public class RelayCommand : ICommand
    {
        #region Private Members
    
        /// <summary>
        /// The action to run
        /// </summary>
        private Action mAction;
    
        #endregion
    
        #region Public Events
    
        /// <summary>
        /// The event thats fired when the <see cref="CanExecute(object)"/> value has changed
        /// </summary>
        public event EventHandler CanExecuteChanged = (sender, e) => { };
    
        #endregion
    
        #region Constructor
    
        /// <summary>
        /// Default constructor
        /// </summary>
        public RelayCommand(Action action)
        {
            mAction = action;
        }
    
        #endregion
    
        #region Command Methods
    
        /// <summary>
        /// A relay command can always execute
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return true;
        }
    
        /// <summary>
        /// Executes the commands Action
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            mAction();
        }
    
        #endregion
    }
    

    Within the view model itself, you will need to declare the command as a parameter

    public ICommand EnterPressedCommand { get; set; }
    

    Finally, within the constructor of the ViewModel, you will need to assign the command to a function

    EnterPressedCommand = new RelayCommand(() => EnterPressed());
    

    This should allow the command to be triggered from the view, and execute your EnterPressed method