0

I am trying to find a simple example on how to bind some TextBox events (PreviewTextInput and PreviewKeyDown) to a Commands, however I can't find any clear example and all the exmaples I found so far enforce me to use some MVVM framework (Light toolkit, Prism, etc.), however currently I don't want to use a framework because I want to understand more deeply how the business works.

  1. Can anyone please supply a simple example on how this can be achieved?
  2. Is it absolutely necessary to use MVVM framework?

Thanks in advance.

user2429022
  • 15
  • 1
  • 5

2 Answers2

-1

You can inherit TextBox and implement ICommandSource. I have done the same, my implementation looked like this. You should be able to extend this to work on PreviewTextInput.

public class CommandTextBox : TextBox, ICommandSource
{
    private bool _canExecute;
    private EventHandler _canExecuteChanged;

    /// <summary>
    /// DependencyProperty for Command property.
    /// </summary>
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandTextBox), new PropertyMetadata(OnCommandChanged));

    /// <summary>
    /// Gets or sets the command to invoke when the enter key is pressed.
    /// </summary>
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    /// <summary>
    /// DependencyProperty for CommandParameter property.
    /// </summary>
    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(CommandTextBox));

    /// <summary>
    /// Gets or sets the parameter to pass to the Command property.
    /// </summary>
    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    /// <summary>
    /// Gets or sets a value that indicates whether the command resets the text property.
    /// </summary>
    public bool CommandResetsText { get; set; }

    /// <summary>
    /// DependencyProperty for CommandTarget property.
    /// </summary>
    public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CommandTextBox));

    /// <summary>
    /// Gets or sets the element on which to raise the specified command.
    /// </summary>
    public IInputElement CommandTarget
    {
        get { return (IInputElement)GetValue(CommandTargetProperty); }
        set { SetValue(CommandTargetProperty, value); }
    }

    /// <summary>
    /// Gets a value that becomes the return value of
    /// System.Windows.UIElement.IsEnabled in derived classes.
    /// </summary>
    protected override bool IsEnabledCore
    {
        get { return base.IsEnabledCore && _canExecute; }
    }

    /// <summary>
    /// Command dependency property change callback. 
    /// </summary>
    /// <param name="d">Dependency Object</param>
    /// <param name="e">Event Args</param>
    private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CommandTextBox tb = (CommandTextBox)d;
        tb.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
    }

    /// <summary>
    /// If Command is defined, pressing the enter key will invoke the command; 
    /// Otherwise, the textbox will behave normally. 
    /// </summary>
    /// <param name="e">Provides data about the event.</param>
    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        if (e.Key == Key.Enter && Command != null)
        {
            RoutedCommand command = Command as RoutedCommand;

            if (command != null)
                command.Execute(CommandParameter, CommandTarget);
            else
                Command.Execute(CommandParameter);

            if (CommandResetsText)
                this.Text = String.Empty;
        }
    }

    /// <summary>
    /// Add a command to the Command Property. 
    /// </summary>
    /// <param name="command">Command</param>
    private void AddCommand(ICommand command)
    {
        var handler = new EventHandler(CanExecuteChanged);
        _canExecuteChanged = handler;
        if (command != null)
            command.CanExecuteChanged += _canExecuteChanged;
    }

    private void CanExecuteChanged(object sender, EventArgs e)
    {
        if (Command != null)
        {
            RoutedCommand command = Command as RoutedCommand;

            // If a RoutedCommand. 
            if (command != null)
                _canExecute = command.CanExecute(CommandParameter, CommandTarget);
            else
                _canExecute = Command.CanExecute(CommandParameter);
        }

        CoerceValue(UIElement.IsEnabledProperty);
    }

    /// <summary>
    /// Add a new command to the Command Property. 
    /// </summary>
    /// <param name="oldCommand">Old Command</param>
    /// <param name="newCommand">New Command</param>
    private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
    {
        // If oldCommand is not null, then we need to remove the handlers. 
        if (oldCommand != null)
            RemoveCommand(oldCommand);

        AddCommand(newCommand);
    }

    /// <summary>
    /// Remove a command from the Command Property. 
    /// </summary>
    /// <param name="command">Command</param>
    private void RemoveCommand(ICommand command)
    {
        EventHandler handler = CanExecuteChanged;
        command.CanExecuteChanged -= handler;
    }
}
Derrick Moeller
  • 4,808
  • 2
  • 22
  • 48
-1

The easy way is to attach a common event handler to the event in XAML, and invoke the Command in code-behind. Such an event handler could look like the following:

private void TextBox_OnTextChanged(object sender, EventArgs e)
{
    var viewmodel = this.DataContext as MyViewmodel;
    if (viewmodel != null)
    {
        viewmodel.SomeCommand.Execute();
    }
}

An alternative that works without any code in the code-behind (but is a bit tricky to implement, and works only on .NET 4.5) is to implement your own MarkupExtension, such that you can code something like

<TextBox TextChanged="{myMarkupExtension:CommandBinding SomeCommand]">...</TextBox>

There are a few articles out there describing this approach, for example this one

andreask
  • 4,248
  • 1
  • 20
  • 25