【问题标题】:Why couldn't my Command enable the button?为什么我的命令不能启用按钮?
【发布时间】:2012-01-19 19:10:30
【问题描述】:

我正在学习使用 Prism 的 DelgateCommand....

在我的 UI 中,我有我的用户名文本框和密码框:

<TextBox Name="_UserNameTextBox" Text="{Binding UserName, Mode=TwoWay}" />

<PasswordBox Name="_PasswordBox"></PasswordBox>

还有我的登录按钮:

<Button Name="button1" Command="{Binding LoginCommand, Mode=TwoWay}" CommandTarget="{Binding ElementName=_UserNameTextBox, Path=Text}">Login</Button>

然后我的 ViewModel 我有:

    string _UserName = string.Empty;
    public string UserName
    {
        get
        {
            return _UserName;
        }
        set
        {
            if (value != _UserName)
            {
                _UserName = value;
                RaisePropertyChanged("UserName");
            }
        }

    }

    //For reference the password
    PasswordBox _PasswordBox { get; set; }


    public DelegateCommand<string> LoginCommand { get; set; }

    public LoginViewModel(PasswordBox passwordBox)
    {
        _PasswordBox = passwordBox;

        LoginCommand = new DelegateCommand<string>(
            (
                //Execute
                (str) =>
                {
                    Login(_PasswordBox.Password);
                }
            ),
                //CanExecute Delgate
                (usr) =>
                {
                    if (string.IsNullOrEmpty(usr) || string.IsNullOrEmpty(_PasswordBox.Password))
                        return false;
                    return true;
                }
            );
    }

我可以看到我的用户名已正确绑定,并且我确实将我的 PasswordBox 作为 ViewModel 构造函数中的引用传递了。当我执行应用程序时,按钮被禁用,所以我知道它已绑定到命令。

但是在我在 UserName 和 PasswordBox 中键入内容后,我从未看到我编写的 CanExecute 委托被检查...并且从未启用...

那么我做错了什么?

编辑:

=====

所以最终结果是……这个?

string _UserName = string.Empty;
        public string UserName
        {
            get
            {
                return _UserName;
            }
            set
            {
                if (value != _UserName)
                {
                    _UserName = value;
                    RaisePropertyChanged("UserName");
                    LoginCommand.RaiseCanExecuteChanged();
                }
            }

        }

        //For reference the password
        PasswordBox _PasswordBox { get; set; }


        public DelegateCommand<string> LoginCommand { get; set; }

        public LoginViewModel(PasswordBox passwordBox)
        {
            _PasswordBox = passwordBox;
            _PasswordBox.PasswordChanged += delegate(object sender, System.Windows.RoutedEventArgs e)
            {
                LoginCommand.RaiseCanExecuteChanged();
            };
            LoginCommand = new DelegateCommand<string>(
                (
                    (str) =>
                    {
                        Login(_PasswordBox.Password);
                    }
                ),
                    (usr) =>
                    {
                        if (string.IsNullOrEmpty(usr) || string.IsNullOrEmpty(_PasswordBox.Password))                        
                            return false;
                        return true;
                    }
                );
        }

【问题讨论】:

  • CanExecute 委托在哪里.. 你必须设置 button.Enabled = true;你在哪里做那个..??
  • 嗯? DelgateCommand 中的第二个参数是 Func 我正在传递一个 Lambada 表达式,仅当 UserName 不为 null 或为空,Password 不为 null 或为空时才返回 true。还是我做错了?
  • 您不应该在那里引用 PasswordBox,正如 Jon 所说,将密码也设为属性。
  • @H.B.我试图将其设为属性,但 Binding PasswordBox 存在问题...“无法在“PasswordBox”类型的“Password”属性上设置“Binding”。“Binding”只能在 DependencyProperty 上设置依赖对象。”
  • @KingChan:好吧,祝你好运,我很确定以前有人问过这个问题。另外,问乔恩,毕竟建议过......

标签: c# wpf xaml mvvm prism


【解决方案1】:

一般来说,只要CanExecute 返回的影响值发生变化,您就必须调用RaiseCanExecuteChanged。在这种特定情况下,您需要在用户或密码字段的值更改时调用它。但这非常困难,因为您的 ViewModel 实现完全错误。

你应该这样做:

  1. 在 ViewModel 中公开 UsernamePassword 属性。您需要显式实现 getter 和 setter(即它不能是自动属性)。
  2. 在您的视图中,将用户名和密码输入字段的内容绑定到这些属性。
  3. 在属性设置器中,调用LoginCommand.RaiseCanExecuteChanged

执行此操作时会发生以下情况(我们以密码框为例):

  1. 用户在密码框中键入一个字符。
  2. 由于双向绑定,WPF 设置了LoginViewModel.Password 的值。
  3. 密码设置器调用RaiseCanExecuteChanged,这会在您的命令上引发CanExecuteChanged 事件。
  4. 提交按钮(当您将其绑定到命令时已订阅该事件)会收到通知。
  5. 按钮调用CanExecute查看现在是否允许执行命令。
  6. 您的委托运行并返回true,因此按钮会自行激活。

【讨论】:

  • +1 在我用 CommandParameter 替换了我的 CommandTarget 后,将 PasswordChanged 的​​ EventHandler 添加到 RaiseCanExecuteChanged,在 UserName 设置器中引发 RaiseCanExecuteChanged,它运行良好。 :) 根据这篇文章,我没有将 Password 属性与 PasswordBox 绑定:Accoding this post,stackoverflow.com/questions/1483892/…
  • @KingChan:恕我直言,该帖子被误导了。阅读它下面的高评价 cmets 是怎么说的。如果您认为您的系统上会运行恶意进程试图窃取您的密码,那么您将遇到更大的问题,那就是只需输入 PasswordBox 并认为您以某种方式神奇地使您的应用程序安全。
  • o 嗯,所以我应该为 Password 编写一个 Getter 属性来从 PasswordBox 获取密码?因为我不能直接绑定PasswordBox...
【解决方案2】:

您需要绑定Button.CommandParameter(将传递给ExecuteCanExecute),如果该绑定发生变化,则据我所知,CanExecute 将被重新评估。

(我认为您将CommandParameterCommandTarget 混淆了,CommandTarget 不在命令内部使用,它仅用于在某个元素上引发命令(这可能与命令路由等)

【讨论】:

  • @KingChan:不要这样做。这会让你完全走错方向。通过阅读我的示例,尝试了解这里发生了什么。即使这可以被破解,密码框也会出现完全相同的问题!
  • @Jon: 其实这不是什么问题,你可以很容易地适应这个,你只需要一个MultiBinding
  • @HB:当然你可以适应它,你使用的是图灵完备的语言——但如果那不是混蛋 MVVM 那我不知道是什么。
  • @Jon:如果你问我这与 MVVM 无关,,,
  • @H.B.:我们可以同意不同意。 :)
猜你喜欢
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 2020-03-13
相关资源
最近更新 更多