CanExecute() returns true 且按钮仍处于禁用状态

CanExecute() returns true and button is still disabled

我在 Windows Phone 特定页面中有一个 BottomAppBar.AppBarButton,它绑定到中继命令。代码、绑定和视图模型实现在项目的其他页面上都以基本相同的方式使用,并且完全按照预期工作。

此特定场景中的问题是,即使在调用 .RaiseCanExecuteChanged() 方法和 CanExecute() returns true.[=25= 之后按钮仍然处于禁用状态]

我最初认为这可能是由于 属性 更改导致手动引发通知的过多调用,因此收紧了我的代码部分,以便仅在需要时以及何时引发该方法需要更改按钮的状态。尽管如此,尽管 CanExecute() 返回 true,该按钮仍处于禁用状态。 如果我注释掉 CanExecute() 中的所有检查并默认为 true,按钮将按预期启用,并且当点击时会触发预期的 Execute() 函数,因此看起来 [=22= 的初始化】 还可以。如果我然后让检查返回,并且每次 CanExecute() 被触发时 运行 逐步执行,当它 returns 为真时,按钮不会启用。

有什么想法吗?为了它的价值,我在下面添加了代码,但我认为这不是原因。

RelayCommand class 是 VS 中 HubApp 附带的标准 class,所以我将省略该代码。

视图模型构造函数的最后一行是 RelayCommand;

AddStrikeTeamCommand = new RelayCommand(async() => await AddStrikeTeam(), CanAddStrikeTeam);

可以加为;

private bool CanAddStrikeTeam()
{
    //if (NameWorking == string.Empty) return false;
    //if (FactionWorking == string.Empty) return false;
    //if (PointsLimitWorking < 1) return false;
    //if (!IsValidTeamWorking) return false;
    return true;
}

最后,按钮绑定

<AppBarButton x:Name="accept" Icon="Accept" Label="accept"
              Command="{Binding AddStrikeTeamCommand}"/>

我敢打赌您的问题与 RaiseCanExecuteChanged() 有关。如果您习惯于 WPF 以及它如何为您自动刷新 CanExecute,则尤其如此。查看此委托命令实施:

http://codepaste.net/ho9s5a

ICommand 接口定义了事件 CanExecuteChanged 指示按钮(或 UI 元素)刷新其 Enabled 状态。在 WPF 中,这是由静态命令管理器不断引发的。这在 WinRT 中不存在。在 WPF 中,因为它被频繁引发,WPF 开发人员必须小心 CanExecute() 不是一个昂贵的操作。 WinRT 提供昂贵的测试,但因此需要开发人员手动引发事件。我希望这是有道理的。

我处理这个问题的一种方法是:

DelegateCommand _SaveCommand = null;
public DelegateCommand SaveCommand
{
    get
    {
        if (_SaveCommand != null)
            return _SaveCommand;
        _SaveCommand = new DelegateCommand
        (
            () =>
            {
                // TODO
            }, 
            () => true
        );
        this.PropertyChanged += (s, e) => _SaveCommand.RaiseCanExecuteChanged();
        return _SaveCommand;
    }
}

这基本上会根据 属性 中(通常在我的视图模型中)的任何更改来刷新 CanExecute。如果您对 ObservableCollection 中的模型有潜在的更改,这还不够,但这是整个事情的良好开端。

有可能您根本没有这个问题。并且您正在调用以引发事件,它正在返回 true,并且仍然无法正常工作。如果是这种情况,那只能是您的代码,因为命令适用于数以千计的应用程序。但是,如果你想把你的代码发给我,我会看看。

祝你好运!

我知道这是一个迟到的答案,但是这个 post 被链接到另一个问题所以我觉得我应该 post 一个更好的代码示例。

很可能是正确的,问题是 RaiseCanExecuteChanged 不会在 ICommand 的实现中自动引发,但是提供的代码示例重新引入了导致它成为问题的完全相同的问题首先取出 - 它会在 any 属性 更改时引发 CanExecuteChanged ,导致调用 CanExecute 的次数远远超过必要的次数。

PropertyChanged 事件处理程序应包括一项检查,并且仅在 属性 更改用于 CanExecute 时才引发 CanExecuteChanged。

因为你的 CanExecute 是

private bool CanAddStrikeTeam()
{
    if (NameWorking == string.Empty) return false;
    if (FactionWorking == string.Empty) return false;
    if (PointsLimitWorking < 1) return false;
    if (!IsValidTeamWorking) return false;
    return true;
}

然后事件处理程序只需要在其中一个属性更改时引发 CanExecuteChanged

this.PropertyChanged += (s, e) => 
{
    switch (e.PropertyName)
    {
        case "NameWorking":
        case "FactionWorking":
        case "PointsLimitWorking":
        case "IsValidTeamWorking":
            AddStrikeTeamCommand.RaiseCanExecuteChanged();
            break;
    }
}

如果您使用的是 Mvvm Light,请确保包含 GalaSoft.MvvmLight.CommandWpf 命名空间而不是 GalaSoft.MvvmLight.Command 命名空间。 (参见 MVVM RelayCommand CanExecute 上的第二个答案)