复选框未被选中时,复选框命令无法触发

5

我有一个包含复选框的数据表格列。

<DataGridTemplateColumn CanUserResize="False" Header="" Width="auto">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox Style="{StaticResource CheckBoxSelectTypeStyle}" IsChecked="{Binding Path=Selected}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

我有一个绑定了取消选中和选中的命令。

<Style x:Key="CheckBoxSelectTypeStyle" TargetType="{x:Type CheckBox}">
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Command" Value="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
            <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
        </Trigger>
        <Trigger Property="IsChecked" Value="False">
            <Setter Property="Command" Value="{Binding DataContext.UncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
            <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
        </Trigger>
    </Style.Triggers>
</Style>

我的问题是,如果复选框最初为未选中状态,并且您勾选其中一个,则不会触发任何命令(这是问题所在)。如果您然后取消选中同一复选框,则取消选中命令将触发(如预期)。然后,如果您再次选中同一复选框,则选择命令将触发(如预期)。在该点上,该复选框的所有操作都将正常工作,但其他复选框仍然存在相同的问题。
如果复选框最初为选中状态,则它将正常工作。我的问题是,如何使命令在复选框最初为未选中状态时触发。我找不到任何原因导致它无法工作。
对建议的回应:
我尝试添加了Null的触发器,但它有完全相同的问题,没有任何变化。
<Style.Triggers>
    <Trigger Property="IsChecked" Value="True">
        <Setter Property="Command" Value="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
        <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
    </Trigger>
    <Trigger Property="IsChecked" Value="False">
        <Setter Property="Command" Value="{Binding DataContext.UncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
        <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
    </Trigger>
    <Trigger Property="IsChecked" Value="{x:Null}">
        <Setter Property="Command" Value="{Binding DataContext.UncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
        <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
    </Trigger>
</Style.Triggers>

ViewModel 代码

public ICommand CheckedCommand { get; set; }
public ICommand UncheckedCommand { get; set; }

CheckedCommand = new RelayCommand<IList>(Checked);
UncheckedCommand = new RelayCommand<IList>(Unchecked);

private void Checked(IList selectedItems)
{
    ChangedChecked(selectedItems, true);
}

private void Unchecked(IList selectedItems)
{
    ChangedChecked(selectedItems, false);
}

private void ChangedChecked(IList selectedItems, bool selected)
{
    if (selectedItems.HasValue())
        foreach (var item in selectedItems)
            if(item is SelectedTypeModel) (item as SelectedTypeModel).Selected = selected;
}

RelayCommand实现了ICommand接口。如我之前所提到的,当复选框开始未选中时,Checked方法不被调用,但是当它被选中、取消选中并再次选中或者当它开始被选中并且被取消选中时,该方法会被调用。

我的目标

我有一个带有一列复选框的DataGrid。我想要能够突出显示多行,然后在所选行中选中/取消选中一个复选框,并使所有其他行更新为相同状态。我还有一个数据网格的关键字过滤器,因此DataGrid绑定到ListCollectionView。

输出信息

我启用了数据绑定的调试信息,得到了以下消息:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Selected; DataItem=null; target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')

然而,我仍然不确定如何使用这个信息来纠正问题。

解决方案

我没有解决原始问题,我仍然不知道为什么它不能正确工作,但我使用了另一种方法来达到相同的目的。以下是更改后的代码:

<Style x:Key="CheckBoxSelectTypeStyle" TargetType="{x:Type CheckBox}">
    <Setter Property="Command" Value="{Binding DataContext.CheckedChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
    <Setter Property="CommandParameter">
        <Setter.Value>
            <MultiBinding Converter="{StaticResource SelectedItemsCheckedMultiValueConverter}">
                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" Path="SelectedItems"/>
                <Binding RelativeSource="{RelativeSource Self}" Path="IsChecked"/>
            </MultiBinding>
        </Setter.Value>
    </Setter>
</Style>

public ICommand CheckedChangedCommand { get; set; }

CheckedChangedCommand = new RelayCommand<Tuple<IList, bool>>(CheckedChanged);

我可能误解了你的意思,但是我没有为CheckChanged编写事件处理程序。在View中没有后台代码。它应该调用ViewModel中的一个命令,除了第一次未选中时。我甚至没有在WPF中看到CheckBox的CheckChanged事件,只有Checked和Unchecked事件。 - Kurbol
IsChecked 是一个可空类型 - Nathan Hillyer
请在您的ViewModel中发布相关代码(命令属性和处理程序)。我尝试在新应用程序中重现此问题,但命令未触发。 - Nathan Hillyer
另一个建议是将IsChecked属性传递到CommandParameter中,并以另一种方式访问所选项目(可能将SelectedItems绑定到视图模型中的属性)。然后,您将拥有单个命令,无需担心触发器。 - Nathan Hillyer
实际上,测试我的小应用程序已经揭示出当被选中时命令会被触发,但是当取消选中时却不会。我不知道为什么...投票支持这个问题,看看是否有其他人知道。 - Nathan Hillyer
显示剩余3条评论
1个回答

2

解决方案 我认为您可以在不需要触发器的情况下实现您的目标(我已在小样本应用程序中测试了此解决方案): 因此,您的样式看起来应该像这样:

<Style x:Key="CheckBoxSelectTypeStyle" TargetType="{x:Type CheckBox}">
    <Setter Property="Command" Value="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
    <Setter Property="CommandParameter" Value="{Binding Path=SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
  ...

在您的视图模型中,只会有一个命令。作为参数,该命令将接收所选项目的列表。因此,只有一件事情还缺失 - 用户是要勾选还是取消复选框。目前我可以想到一种可能的方法,即使用自定义转换器编写MultiBinding,将SelectedItems和IsChecked的当前值放入类似于Tuple<..., bool>的东西中以传递此信息。

        <Setter Property="CommandParameter">
            <Setter.Value>
                <MultiBinding Converter="{x:Static PackTupleConverter.Instance}">
                    <Binding RelativeSource="{RelativeSource AncestorType=DataGrid}" Path="SelectedItems"/>
                    <Binding RelativeSource="{x:Static RelativeSource.Self}" Path="IsChecked"/>
                </MultiBinding>
            </Setter.Value>
        </Setter>

结束

我可以通过在ListBox中简单地放置相同的复选框来重现您的问题。一旦完成,第一次单击复选框时,命令不会被调用。

这是示例窗口XAML:

<Window.Resources>
    <Style x:Key="CheckBoxSelectTypeStyle" TargetType="{x:Type CheckBox}">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Style.Triggers>
            <Trigger Property="IsChecked" Value="True">
                <Setter Property="Command" Value="{Binding DataContext.Test, RelativeSource={RelativeSource AncestorType=Window}}" />
            </Trigger>
            <Trigger Property="IsChecked" Value="False">
                <Setter Property="Command" Value="{Binding DataContext.Test, RelativeSource={RelativeSource AncestorType=Window}}" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid>
    <ListBox>
        <ListBox.Items>
            <CheckBox Style="{StaticResource CheckBoxSelectTypeStyle}" />
        </ListBox.Items>
    </ListBox>

</Grid>

而实际上,背后还有更多。
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Test = new DelegateCommand(TestCommand);
    }

    public ICommand Test { get; set; }

    private void TestCommand()
    {
    }
}

以下是我在绑定方面发现的输出:

System.Windows.Data Information: 10 : 无法使用绑定检索值,并且不存在有效的备用值;默认情况下使用。BindingExpression:Path=DataContext.Test; DataItem=null; 目标元素为“CheckBox”(名称=''); 目标属性为'ICommand'类型的'Command'


你是否已经打开了“WPF跟踪设置部分”\“数据绑定”的信息消息? - Woodman
你能简要描述一下你想要实现什么吗?因为通常在ItemsControl的派生项中使用RelativeSource来获取ItemsControl外部的某些控件并不是一个好主意。 - Woodman
我在问题结尾更新了我的问题,说明了我试图做什么。我相对新于使用 XAML 中的命令,所以我不完全理解应该使用什么和不应该使用什么。任何帮助都将不胜感激。我不认为我已经打开了信息消息,所以我会调查一下,并查看是否会得到您在示例中收到的相同消息。 - Kurbol
我认为你的解决方案会有效,但是我明天必须完成实现它。如果它有效,我会将其标记为解决方案。谢谢。 - Kurbol
你的解决方案有效。我想给它投票,但是我没有足够的声望。 - Kurbol
显示剩余2条评论

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