【问题标题】:WPF: Implement ICommandSource Without Breaking IsEnabledWPF:在不中断 IsEnabled 的情况下实现 ICommandSource
【发布时间】:2014-12-10 20:13:54
【问题描述】:

我创建了一个实现 ICommandSource 的 TextBox,在大多数情况下,我使用 Slider 遵循 Microsoft 的示例。此文本框将在按下“Enter”键时执行绑定命令。这部分行为正常工作,我遇到的问题是 IsEnabled 不能再在 XAML 中设置?我不确定这是为什么,您仍然可以在 Button 等原生 Microsoft 类上设置 IsEnabled。

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?

我们将不胜感激。

【问题讨论】:

  • “不能再设置”?你对此有何假设?每次调用 CanExecuteChanged 时,您都会在代码中设置 IsEnabled,因此您在 XAML 中设置的任何值都会在您启动应用程序时立即被覆盖......
  • @Patrick 我已经包含了一些 XAML 来显示这个问题,我希望 CommandTextBox 的 IsEnabled 实现能够反映 Button 的实现。
  • @FrumRoll IsEnabledCanExecuteChanged 处理程序中被覆盖。可能您的命令调用 CanExecuteChanged。您最好在CanExecuteChanged 上设置断点并查看整个堆栈跟踪以找到原因。
  • @EldarDordzhiev 你是对的,CanExecuteChanged 处理程序覆盖了 IsEnabled。最好问我的问题,我的实现与 Button 中的实现有何不同?如何解决这种行为差异?
  • @FrumRoll 好的,我知道了,检查下面的答案。

标签: c# wpf xaml


【解决方案1】:

首先,您需要创建一个私有属性或一个字段,如果该命令可以执行,则将设置该属性:

private bool _canExecute;

其次,需要覆盖IsEnabledCore属性:

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

最后,您只需将IsEnabled 替换为_canExecute 并强制IsEnabledCore 即可修复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);
        }
        // If a not RoutedCommand. 
        else
        {
            _canExecute = Command.CanExecute(CommandParameter);
        }
    }

    CoerceValue(UIElement.IsEnabledProperty);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多