WPF的CommandParameter在第一次调用CanExecute时为NULL

99
我在使用WPF和绑定到ItemsControl数据模板中的按钮的命令时遇到了问题。情景非常简单,ItemsControl绑定到对象列表,我想通过点击一个按钮来删除列表中的每个对象。该按钮执行一个命令,并且该命令负责删除。CommandParameter绑定到我要删除的对象上,这样我就知道用户单击了什么。用户只能删除自己的对象,因此我需要在命令的“CanExecute”调用中进行一些检查以验证用户是否具有正确的权限。
问题是CanExecute传递的参数第一次调用时为NULL,因此我无法运行启用/禁用命令的逻辑。但是,如果我始终启用它,然后单击按钮执行命令,CommandParameter将被正确传递。这意味着与CommandParameter的绑定正在工作。
ItemsControl和DataTemplate的XAML如下:
<ItemsControl 
    x:Name="commentsList"
    ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
    Width="Auto" Height="Auto">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Button                             
                    Content="Delete"
                    FontSize="10"
                    Command="{Binding Path=DataContext.DeleteCommentCommand, ElementName=commentsList}" 
                    CommandParameter="{Binding}" />
            </StackPanel>                       
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

可以看到我有一个Comments对象列表,我想将DeleteCommentCommand的CommandParameter绑定到Command对象。

所以我的问题是:有人之前遇到过这个问题吗?CanExecute在我的Command上被调用,但是第一次参数总是为NULL - 这是为什么?

更新:我能够稍微缩小问题的范围。我添加了一个空的Debug ValueConverter,以便在数据绑定时输出消息。结果问题是在将CommandParameter绑定到按钮之前就执行了CanExecute方法。我尝试在Command之前设置CommandParameter(如建议的那样)-但仍然不起作用。有关如何控制它的任何提示。

更新2:有没有办法检测绑定何时“完成”,以便我可以强制重新评估命令?另外,是否存在多个按钮(每个项在ItemsControl中都有一个)绑定到同一个Command对象实例的问题?

更新3:我已经将演示错误上传到我的SkyDrive:http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip


我有完全相同的问题,是关于一个ListBox。 - Hadi Eskandari
1
目前有一个针对WPF的已知问题的错误报告:https://github.com/dotnet/wpf/issues/316 - StayOnTarget
16个回答

0

其中一些答案涉及绑定到DataContext以获取Command本身,但问题是CommandParameter为空时不应该为空。 我们也遇到了这个问题。 凭直觉,我们在ViewModel中找到了一种非常简单的方法来解决这个问题。 这特别是针对客户报告的CommandParameter null问题,只需一行代码即可解决。 请注意Dispatcher.BeginInvoke()。

public DelegateCommand<objectToBePassed> CommandShowReport
    {
        get
        {
            // create the command, or pass what is already created.
            var command = _commandShowReport ?? (_commandShowReport = new DelegateCommand<object>(OnCommandShowReport, OnCanCommandShowReport));

            // For the item template, the OnCanCommand will first pass in null. This will tell the command to re-pass the command param to validate if it can execute.
            Dispatcher.BeginInvoke((Action) delegate { command.RaiseCanExecuteChanged(); }, DispatcherPriority.DataBind);

            return command;
        }
    }

0

1
反馈链接已失效。 - Peter Duniho

0

我不确定这个在数据模板中是否能正常工作,但以下是我在ListView上下文菜单中使用的绑定语法,用于将当前项作为命令参数提取:

CommandParameter=
          "{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},
                    Path=PlacementTarget.SelectedItem,
                    Mode=TwoWay}"

在我的列表视图中,我做了完全相同的事情。在这种情况下,它是一个ItemsControl,因此没有明显的属性可以“绑定”(在可视树中)。我想我必须找到一种方法来检测绑定何时完成,并重新评估CanExecute(因为CommandParameter被绑定时机太晚了)。 - Jonas Follesø

-1

这是一个冒险。你可以尝试以下方法来调试:
- 检查PreviewCanExecute事件。
- 使用Snoop/WPF Mole来窥视内部并查看CommandParameter是什么。

希望对你有所帮助,


尝试使用Snoop - 但是它很难调试,因为它在最初加载时只是NULL。如果我在其上运行Snoop,则Command和CommandParameter都被设置...这与在DataTemplate中使用Commands有关。 - Jonas Follesø

-1

该链接已失效。 - Peter Duniho

-2

除了Ed Ball的建议在设置Command之前设置CommandParameter外,确保您的CanExecute方法具有object类型的参数。

private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
{
    // Your goes heres
}

希望这可以防止其他人像我一样花费大量时间来弄清楚如何将SelectedItems作为CanExecute参数接收。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接