第一次调用 CanExecute 时,WPF CommandParameter 为 NULL [英] WPF CommandParameter is NULL first time CanExecute is called

查看:39
本文介绍了第一次调用 CanExecute 时,WPF CommandParameter 为 NULL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了绑定到 ItemsControl 的 DataTemplate 内的按钮的 WPF 和命令的问题.这个场景非常简单.ItemsControl 绑定到一个对象列表,我希望能够通过单击一个按钮来删除列表中的每个对象.按钮执行一个命令,命令负责删除.CommandParameter 绑定到我要删除的对象.这样我就知道用户点击了什么.用户应该只能删除他们自己的"对象——所以我需要在命令的CanExecute"调用中做一些检查,以验证用户是否拥有正确的权限.

I have run into an issue with WPF and Commands that are bound to a Button inside the DataTemplate of an ItemsControl. The scenario is quite straight forward. The ItemsControl is bound to a list of objects, and I want to be able to remove each object in the list by clicking a Button. The Button executes a Command, and the Command takes care of the deletion. The CommandParameter is bound to the Object I want to delete. That way I know what the user clicked. A user should only be able to delete their "own" objects - so I need to do some checks in the "CanExecute" call of the Command to verify that the user has the right permissions.

问题是传递给 CanExecute 的参数在第一次被调用时为 NULL - 所以我无法运行逻辑来启用/禁用命令.但是,如果我让它始终启用,然后单击按钮执行命令,则 CommandParameter 会正确传入.所以这意味着对 CommandParameter 的绑定正在起作用.

The problem is that the parameter passed to CanExecute is NULL the first time it's called - so I can't run the logic to enable/disable the command. However, if I make it allways enabled, and then click the button to execute the command, the CommandParameter is passed in correctly. So that means that the binding against the CommandParameter is working.

ItemsControl 和 DataTemplate 的 XAML 如下所示:

The XAML for the ItemsControl and the DataTemplate looks like this:

<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 对象.

So as you can see I have a list of Comments objects. I want the CommandParameter of the DeleteCommentCommand to be bound to the Command object.

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

So I guess my question is: have anyone experienced this problem before? CanExecute gets called on my Command, but the parameter is always NULL the first time - why is that?

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

Update: I was able to narrow the problem down a little. I added an empty Debug ValueConverter so that I could output a message when the CommandParameter is data bound. Turns out the problem is that the CanExecute method is executed before the CommandParameter is bound to the button. I have tried to set the CommandParameter before the Command (like suggested) - but it still doesn't work. Any tips on how to control it.

Update2: 有什么方法可以检测绑定何时完成",以便我可以强制重新评估命令?另外 - 我有多个按钮(ItemsControl 中的每个项目一个)绑定到命令对象的同一实例是否有问题?

Update2: Is there any way to detect when the binding is "done", so that I can force re-evaluation of the command? Also - is it a problem that I have multiple Buttons (one for each item in the ItemsControl) that bind to the same instance of a Command-object?

Update3:我已将错误的复制品上传到我的 SkyDrive:http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip

Update3: I have uploaded a reproduction of the bug to my SkyDrive: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip

推荐答案

我偶然发现了一个类似的问题,并使用我值得信赖的 TriggerConverter 解决了它.

I stumbled upon a similar problem and solved it using my trusty TriggerConverter.

public class TriggerConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // First value is target value.
        // All others are update triggers only.
        if (values.Length < 1) return Binding.DoNothing;
        return values[0];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

这个值转换器接受任意数量的参数并将它们中的第一个作为转换后的值传回.在您的情况下在 MultiBinding 中使用时,它看起来如下所示.

This value converter takes any number of parameters and passes the first of them back as the converted value. When used in a MultiBinding in your case it looks like the following.

<ItemsControl 
    x:Name="commentsList"
    ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
    Width="Auto" Height="Auto">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Button                             
                    Content="Delete"
                    FontSize="10"
                    CommandParameter="{Binding}">
                    <Button.Command>
                        <MultiBinding Converter="{StaticResource TriggerConverter}">
                            <Binding Path="DataContext.DeleteCommentCommand"
                                     ElementName="commentsList" />
                            <Binding />
                        </MultiBinding> 
                    </Button.Command>
                </Button>
            </StackPanel>                                       
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

您必须在某处添加 TriggerConverter 作为资源才能使其正常工作.现在,Command 属性不是在 CommandParameter 的值可用之前设置的.您甚至可以绑定到 RelativeSource.Self 和 CommandParameter 而不是 .达到同样的效果.

You will have to add TriggerConverter as a resource somewhere for this to work. Now the Command property is set not before the value for the CommandParameter has become available. You could even bind to RelativeSource.Self and CommandParameter instead of . to achieve the same effect.

这篇关于第一次调用 CanExecute 时,WPF CommandParameter 为 NULL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆