【问题标题】:WPF/MVVM binding CheckBox in ListBoxItem template to SelectedItem propertyWPF/MVVM 将 ListBoxItem 模板中的 CheckBox 绑定到 SelectedItem 属性
【发布时间】:2013-07-26 10:00:19
【问题描述】:

我正在构建一个 WPF 应用程序(.NET 4.0),我有以下情况:

<ListBox
    ItemsSource="{Binding Path=Items}"
    SelectedItem="{Binding Path=SelectedItem}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Height" Value="48"/>
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <DockPanel>
                <!-- ... -->
                <CheckBox>
                    <CheckBox.IsChecked>
                        <!-- Something here that behaves like SelectedItem binding -->
                    </CheckBox.IsChecked>
                </CheckBox>
            </DockPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
  • ListBox 的 ItemSource 绑定到 ObservableCollection。
  • ListBox 的 SelectedItem 绑定到 SelectedItem 属性 ViewModel 的。

但是,当 ViewModel.SelectedItem 更新时,会启动一些长时间运行(2-3 秒)的异步任务。 通过简单地选择列表框中的不同项目来排队大量异步任务太容易了,所以我正在研究将 ViewModel.SelectedItem 绑定到 CheckBox(包含在 ItemTemplate 中)。

我对 WPF 相当陌生,但在 Forms 中我会附加一个事件处理程序并编写一些长而丑陋的代码来取消检查除触发事件的项目之外的所有项目,然后以编程方式更新属性。

在 WPF 中,我希望有更好的解决方案,我已经研究过使用 ValueConverter,但我不知道如何绑定到父视图模型(具有 ObservableCollection)。

如果有人遇到类似的要求,我真的很感激一些指点。

干杯!

./弗雷德里克

【问题讨论】:

  • 重复点击复选框让大量异步任务排队不是同样容易吗?我想您需要的是一种机制,可以在启动新任务时取消当前正在运行的任务。并且您可以考虑延迟启动任务,而不是在所选项目更改时立即执行。
  • 嗨克莱门斯,感谢您的回复!是的,它会,但是在异步任务运行时防止更改检查状态应该不会太难。我通过在任务运行时禁用 ListBox 来尝试相同的方法,但这太难看了。添加 CheckBox 的好处是 ListBox 中的选择与任务分离,允许用户在任务运行时浏览列表框。
  • 您还可以考虑在列表框之外有一个专用按钮(而不是所有复选框)来启动当前所选项目的任务。这不仅更容易实现,而且还可能带来更直观的 UI。
  • 一个很好的建议,这是我目前正在使用的后备。不过,我更喜欢 Chckbox,因为选中状态提供了有关“我选择了什么”的信息。使用按钮,我必须添加标签或其他内容以指示单击按钮时选择了哪些项目。

标签: wpf mvvm listbox checkedlistbox


【解决方案1】:

您好,您可以将 CheckBox 绑定到 VM 的命令,当 CheckBox 被选中/取消选中时,该命令将触发。

【讨论】:

  • 您好,感谢您的回复!现在我们接近了。该命令可以让我以编程方式设置 SelectedItem,但 ListBox 中有多个项目,每个项目都以 CheckBox 为模板。我将如何取消选中其他复选框,保留 SelectionMode Single 行为?干杯!
  • 您可以在 ViewModel 中取消选中它们,更改将反映在 UI 上
  • 就像您将布尔值列表绑定到这些复选框的 ISelected 一样,并且当您想要取消选中所有这些复选框时,只需将列表中的所有值设置为 false 并将该列表中的选定项设置为 true。关键在于你在 VM 中做的逻辑有多好
  • 没错,这将是一种(对我而言)舒适的方式,因为它类似于 Forms 方式。我的 ListBoxItems 已经直接绑定到 Model 业务对象,但我可以创建一个像 ListBoxItemWrapper 这样的包装器,其中 T 是“BusinessObject”。对于看起来相当简单的数据绑定场景,它需要一些编码,但它肯定是一种解决方案。谢谢!
【解决方案2】:

您可以通过 TemplateBinding 将 ListBoxItem 的 isselected 属性绑定到复选框。

【讨论】:

  • 您好 Nakul,感谢您的回复!这不会实际更改 ListBox 选择以及选中状态(至少对于双向绑定)。我的目的是在 View-Model 中有一个 SelectedItem 属性来反映 ListBox 中的哪个(单个)项被选中。干杯!
【解决方案3】:

我想出了一个不求助于在代码隐藏中处理检查状态的解决方案,因此我将记录它以供后代使用。

第一个技巧是使用RadioButton 来代替GroupName。同一组中的单选按钮将自动确保在任何给定时间仅选中其中一个,从而避免了手动管理选中状态的问题。

第二个技巧是将CommandParameter 绑定到单选按钮DataContext

<RadioButton
    Name="rdbSelected"
    GroupName="itemsRadioGroup"
    Command="{Binding 
        RelativeSource={RelativeSource AncestorType=UserControl},
        Path=DataContext.SelectItemCommand}" 
    CommandParameter="{Binding 
        RelativeSource={RelativeSource Self}, 
        Path=DataContext}"/>

单选按钮命令会在每次单击时触发,不仅是在选中时,但我们不会这样做,因为与CheckBox不同的是,无法取消选中单选按钮,因此双击单选按钮将只确认SelecedItem

如果SelectedItem 属性触发PropertyChanged 事件,建议让命令处理程序检查它是否实际上设置了新值,或者只是重复前一个值:

RelayCommand<object> cmd = new RelayCommand<object>(
    (o) =>
    {
       if (this.SelectedItem != o)
           this.SelectedItem = o;
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    • 2013-02-22
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    • 2021-09-25
    相关资源
    最近更新 更多