将WPF TextBox事件绑定到命令

时间:2015-01-08 11:46:45

标签: wpf mvvm routed-commands

我试图找到一个关于如何将一些TextBox事件(PreviewTextInput和PreviewKeyDown)绑定到命令的简单示例,但是我找不到任何明确的示例,到目前为止我发现的所有exmap都强制我使用一些MVVM框架(Light toolkit,Prism等),但是目前我不想使用框架,因为我想更深入地了解业务的运作方式。

  1. 任何人都可以提供一个如何实现这一目标的简单示例吗?
  2. 是否绝对有必要使用MVVM框架?
  3. 提前致谢。

2 个答案:

答案 0 :(得分:-1)

您可以继承TextBox并实施ICommandSource。我做了同样的事情,我的实现看起来像这样。您应该能够将其扩展为在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;
    }
}

答案 1 :(得分:-1)

简单的方法是在XAML中为事件附加公共事件处理程序,并在代码隐藏中调用Command。这样的事件处理程序可能如下所示:

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

在代码隐藏中没有任何代码的情况下运行的替代方案(但实现起来有点棘手,仅适用于.NET 4.5)是实现自己的MarkupExtension,这样就可以编写类似

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

有一些文章描述了这种方法,例如this one