简体   繁体   中英

WPF: Implement ICommandSource Without Breaking IsEnabled

I have created a TextBox that implements ICommandSource, for the most part I followed Microsoft's example using a Slider . This TextBox will execute the bound command upon pressing the "Enter" key. This portion of the behavior is working correctly, the issue I am having is that IsEnabled can no longer be set in XAML? I am not sure why this is, you can still set IsEnabled on native Microsoft classes like Button.

public class CommandTextBox : TextBox, ICommandSource
{
    // The DependencyProperty for the Command.
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandTextBox), new PropertyMetadata(OnCommandChanged));

    // The DependencyProperty for the CommandParameter.
    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(CommandTextBox));

    // The DependencyProperty for the CommandTarget.
    public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(CommandTextBox));

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public bool CommandResetsText { get; set; }

    public IInputElement CommandTarget
    {
        get { return (IInputElement)GetValue(CommandTargetProperty); }
        set { SetValue(CommandTargetProperty, value); }
    }

    // Command dependency property change callback. 
    private static void OnCommandChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        CommandTextBox ctb = (CommandTextBox)dp;
        ctb.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
    }

    // Add a new command to the Command Property. 
    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);
    }

    // Remove an old command from the Command Property. 
    private void RemoveCommand(ICommand command)
    {
        EventHandler handler = CanExecuteChanged;
        command.CanExecuteChanged -= handler;
    }

    // Add the command. 
    private void AddCommand(ICommand command)
    {
        var handler = new EventHandler(CanExecuteChanged);
        canExecuteChangedHandler = handler;
        if (command != null)
            command.CanExecuteChanged += canExecuteChangedHandler;
    }

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

            // If a RoutedCommand. 
            if (command != null)
            {
                if (command.CanExecute(CommandParameter, CommandTarget))
                    this.IsEnabled = true;
                else
                    this.IsEnabled = false;
            }
            // If a not RoutedCommand. 
            else
            {
                if (Command.CanExecute(CommandParameter))
                    this.IsEnabled = true;
                else
                    this.IsEnabled = false;
            }
        }
    }

    // If Command is defined, pressing the enter key will invoke the command; 
    // Otherwise, the textbox will behave normally. 
    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

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

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

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

    private EventHandler canExecuteChangedHandler;
}

XAML

<Button Command="{Binding GenericCommand}" IsEnabled="False" /> // This is disabled(desired).
<CommandTextBox Command="{Binding GenericCommand}" IsEnabled="False" /> // This is enabled?

Any feedback would be appreciated.

First, you need to create a private property or a field that will be set if the command can execute:

private bool _canExecute;

Second, you need to override IsEnabledCore property:

protected override bool IsEnabledCore
{
    get
    {
        return ( base.IsEnabledCore && _canExecute );
    }
}

And finally, you just fixing CanExecuteChanged method by replacing IsEnabled with _canExecute and coercing IsEnabledCore after all:

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);
        }
        // If a not RoutedCommand. 
        else
        {
            _canExecute = Command.CanExecute(CommandParameter);
        }
    }

    CoerceValue(UIElement.IsEnabledProperty);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM