【问题标题】:How to Implement a ListBox of Checkboxes in WPF?如何在 WPF 中实现复选框列表框?
【发布时间】:2011-05-30 11:06:06
【问题描述】:

虽然在编写 Winforms 应用程序方面有些经验,但就最佳实践和设计模式而言,WPF 的“模糊性”仍然让我难以理解。

尽管在运行时填充了我的列表,但我的列表框显示为空。

我已按照this helpful article 的简单说明进行操作,但无济于事。我怀疑我错过了某种DataBind() 方法,我告诉列表框我已经完成了对基础列表的修改。

在我的 MainWindow.xaml 中,我有:

    <ListBox ItemsSource="{Binding TopicList}" Height="177" HorizontalAlignment="Left" Margin="15,173,0,0" Name="listTopics" VerticalAlignment="Top" Width="236" Background="#0B000000">
        <ListBox.ItemTemplate>
            <HierarchicalDataTemplate>
                <CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
            </HierarchicalDataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

在我的代码隐藏中,我有:

    private void InitializeTopicList( MyDataContext context )
    {
        List<Topic> topicList = ( from topic in context.Topics select topic ).ToList();

        foreach ( Topic topic in topicList )
        {
            CheckedListItem item = new CheckedListItem();
            item.Name = topic.DisplayName;
            item.ID = topic.ID;
            TopicList.Add( item );
        }
    }

通过追踪,我知道其中填充了四个项目。

编辑

我已将TopicList 更改为ObservableCollection。还是不行。

    public ObservableCollection<CheckedListItem> TopicList;

编辑#2

我做了两个有用的更改:

在 .xaml 文件中:

ListBox ItemsSource="{Binding}"

在我填充列表后的源代码中:

listTopics.DataContext = TopicList;

我得到一个列表,但是当我刷新复选框状态时,它不会自动更新复选框状态。我怀疑我的进一步阅读会解决这个问题。

【问题讨论】:

  • 投反对票的人:这个问题究竟如何不显示研究工作、不清楚或没有用处?

标签: wpf listbox checkbox


【解决方案1】:

假设TopicList 不是ObservableCollection&lt;T&gt;,因此当您添加没有INotifyCollection 更改的项目时,将触发绑定引擎更新值。

将您的 TopicList 更改为 ObservableCollection&lt;T&gt;,这将解决当前问题。您也可以提前填充List&lt;T&gt;,然后绑定将通过 OneWay 工作;但是ObservableCollection&lt;T&gt; 是一种更强大的方法。

编辑:

您的TopicList 必须是属性而不是成员变量;绑定需要属性。它确实不需要必须是DependencyProperty

编辑 2:

修改您的ItemTemplate,因为它不需要是HierarchicalDataTemplate

   <ListBox.ItemTemplate>
     <DataTemplate>
       <StackPanel>
         <CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
       </StackPanel>
     </DataTemplate>
   </ListBox.ItemTemplate>

【讨论】:

  • 只有在首次绑定完成或容器类实现 INotifyPropertyChanged 并在 TopicList.set 方法上调用 PropertyChanged(...)
  • 您应该绑定到ListBoxItem 上的IsSelected 属性:&lt;CheckBox IsChecked="{Binding IsSelected, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" 您可能还应该将IsHitTestVisibleIsFocusable 设置为false。
【解决方案2】:

使用ObservableCollection&lt;Topic&gt; 而不是List&lt;Topic&gt;

编辑

它实现了 INotifyCollectionChanged 接口,让 WPF 知道您何时添加/删除/修改项目

编辑 2

由于你在代码中设置了TopicList,它应该是一个依赖属性,而不是一个公共字段

    public ObservableCollection<CheckedListItem> TopicList {
        get { return (ObservableCollection<CheckedListItem>)GetValue(TopicListProperty); }
        set { SetValue(TopicListProperty, value); }
    }
    public static readonly DependencyProperty TopicListProperty =
        DependencyProperty.Register("TopicList", typeof(ObservableCollection<CheckedListItem>), typeof(MainWindow), new UIPropertyMetadata(null));

编辑 3

查看项目的变化

  1. CheckedListItem 中实现INotifyPropertyChanged 接口(每个setter 都应该调用PropertyChanged(this, new PropertyChangedEventArgs(&lt;property name as string&gt;)) 事件)
  2. 或从DependencyObject派生CheckedListItem,并将NameIDIsChecked转换为依赖属性
  3. 或完全更新它们 (topicList[0] = new CheckedListItem() { Name = ..., ID = ... })

【讨论】:

  • 感谢所有帮助我拼凑解决方案的人。正是您的 INotifyPropertyChanged 建议使我找到了这篇文章:msdn.microsoft.com/en-us/library/ms229614.aspx,它完成了解决方案。
  • ObservableCollection 将发现添加/删除的项目,但不会发现修改的项目
【解决方案3】:

首先,您不需要 HeirarchicalDataTemplate。只需 Aaron 给出的常规 DataTemplate 就足够了。 然后你需要在类的构造函数中的某个地方实例化 TopicList ObservableCollection。这使得 ObservableCollection 甚至在您向其中添加数据之前仍然存在并且绑定系统知道集合。然后,当您添加每个 Topic/CheckedListItem 时,它会自动显示在 UI 中。

TopicList = new ObservableCollection<CheckedListItem>(); //This should happen only once

private void InitializeTopicList( MyDataContext context )
{
    TopicList.Clear();

    foreach ( Topic topic in topicList )
    {
        CheckedListItem item = new CheckedListItem();
        item.Name = topic.DisplayName;
        item.ID = topic.ID;
        TopicList.Add( item );
    }
}

【讨论】:

  • 是的,这样你就不需要TopicList依赖属性
【解决方案4】:

将您的绑定更改为

 <ListBox ItemsSource="{Binding Path=TopicList}"

【讨论】:

    【解决方案5】:

    其他人已经提出了有用的建议(使用可观察的集合来获取列表更改通知,使集合成为属性而不是字段)。以下是他们没有的两个:

    1) 每当您遇到数据绑定问题时,请查看“输出”窗口以确保您没有遇到任何绑定错误。如果您不这样做,您可能会花费大量时间来尝试解决错误的问题。

    2) 了解更改通知在绑定中的作用。除非数据源实现更改通知,否则数据源中的更改不能也不会传播到 UI。对于普通属性有两种方法:使数据源派生自DependencyObject并使绑定属性成为依赖属性,或使数据源实现INotifyPropertyChanged并在属性值更改时引发PropertyChanged事件.将ItemsControl 绑定到集合时,请使用实现INotifyCollectionChanged 的集合类(如ObservableCollection&lt;T&gt;),以便对集合的内容和顺序的更改将传播到绑定控件。 (请注意,如果您希望对集合中的项目 的更改传播到绑定控件,这些项目也需要实现更改通知。)

    【讨论】:

      猜你喜欢
      • 2023-02-16
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 2014-08-01
      • 1970-01-01
      • 2011-09-12
      • 1970-01-01
      • 2011-09-25
      相关资源
      最近更新 更多