如何在 WPF 中实现复选框列表框?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4527286/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Implement a ListBox of Checkboxes in WPF?
提问by Bob Kaufman
Although somewhat experienced with writing Winforms applications, the... "vagueness" of WPF still eludes me in terms of best practices and design patterns.
尽管在编写 Winforms 应用程序方面有些经验,但在最佳实践和设计模式方面,WPF 的“模糊性”仍然让我望而却步。
Despite populating my list at runtime, my listbox appears empty.
尽管在运行时填充我的列表,但我的列表框显示为空。
I have followed the simple instructions from this helpful articleto no avail. I suspect that I'm missing some sort of DataBind()
method where I tell the listbox that I'm done modifying the underlying list.
我已按照这篇有用文章中的简单说明进行操作,但无济于事。我怀疑我错过了某种DataBind()
方法,我告诉列表框我已经修改了底层列表。
In my MainWindow.xaml, I have:
在我的 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>
In my code-behind, I have:
在我的代码隐藏中,我有:
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 );
}
}
Which, by tracing through, I know is being populated with four items.
通过跟踪,我知道其中填充了四个项目。
EDIT
编辑
I have changed TopicList
to an ObservableCollection
. It still doesn't work.
我已更改TopicList
为ObservableCollection
. 它仍然不起作用。
public ObservableCollection<CheckedListItem> TopicList;
EDIT #2
编辑#2
I have made two changes that help:
我做了两个有帮助的更改:
In the .xaml file:
在 .xaml 文件中:
ListBox ItemsSource="{Binding}"
In the source code after I populate the list:
在我填充列表后的源代码中:
listTopics.DataContext = TopicList;
I'm getting a list, but it's not automagically updating the checkbox states when I refresh those. I suspect a little further reading on my part will resolve this.
我得到了一个列表,但是当我刷新它们时它不会自动更新复选框状态。我怀疑我的进一步阅读会解决这个问题。
采纳答案by Mykola Bogdiuk
Use ObservableCollection<Topic>
instead of List<Topic>
使用ObservableCollection<Topic>
代替List<Topic>
Edit
编辑
it implements INotifyCollectionChanged interface to let WPF know when you add/remove/modify items
它实现了 INotifyCollectionChanged 接口,以便在您添加/删除/修改项目时让 WPF 知道
Edit 2
编辑 2
Since you set TopicList
in code, it should be a Dependency Property, not a common field
既然是TopicList
在代码中设置的,应该是Dependency Property,而不是普通字段
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));
Edit 3
编辑 3
To see changes in items
查看项目的变化
- implement
INotifyPropertyChanged
interface inCheckedListItem
(each setter should callPropertyChanged(this, new PropertyChangedEventArgs(<property name as string>))
event) - or derive
CheckedListItem
fromDependencyObject
, and convertName
,ID
,IsChecked
to dependency properties - or update them totally (
topicList[0] = new CheckedListItem() { Name = ..., ID = ... }
)
- 实现
INotifyPropertyChanged
接口CheckedListItem
(每个setter应该调用PropertyChanged(this, new PropertyChangedEventArgs(<property name as string>))
事件) - 或派生
CheckedListItem
自DependencyObject
, 并将Name
,ID
,转换IsChecked
为依赖属性 - 或完全更新它们 (
topicList[0] = new CheckedListItem() { Name = ..., ID = ... }
)
回答by Aaron McIver
Assuming TopicList
is not an ObservableCollection<T>
therefore when you add items no INotifyCollection
changed is being fired to tell the binding engine to update the value.
假设TopicList
不是,ObservableCollection<T>
因此当您添加项目时,不会INotifyCollection
触发任何更改以告诉绑定引擎更新值。
Change your TopicList
to an ObservableCollection<T>
which will resolve the current issue. You could also populate the List<T>
ahead of time and then the binding will work via OneWay; however ObservableCollection<T>
is a more robust approach.
将您的更改TopicList
为ObservableCollection<T>
将解决当前问题。您也可以List<T>
提前填充,然后绑定将通过 OneWay 工作;然而,这ObservableCollection<T>
是一种更强大的方法。
EDIT:
编辑:
Your TopicList
needs to be a property not a member variable; bindings require properties. It does notneed to be a DependencyProperty
.
您TopicList
需要是属性而不是成员变量;绑定需要属性。它并不需要是一个DependencyProperty
。
EDIT 2:
编辑2:
Modify your ItemTemplate
as it does not need to be a HierarchicalDataTemplate
修改您的,ItemTemplate
因为它不需要是HierarchicalDataTemplate
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
回答by Jobi Joy
First you dont need a HeirarchicalDataTemplate for this. Just regular DataTemplate as Aaron has given is enough. Then you need to instantiate the TopicList ObservableCollection somewhere inside the constructor of the class. which makes the ObservableCollection alive even before you add data in to it And binding system knows the collection. Then when you add each and every Topic/CheckedListItem it will automatically shows up in the UI.
首先,您不需要 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 );
}
}
回答by Robert Rossney
Others have already made useful suggestions (use an observable collection to get list-change notification, make the collection a property rather than a field). Here are two they haven't:
其他人已经提出了有用的建议(使用可观察的集合来获取列表更改通知,使集合成为属性而不是字段)。这里有两个他们没有:
1) Whenever you're having a problem with data binding, look in the Output window to make sure that you're not getting any binding errors. You can spend a lot of time trying to fix the wrong problem if you don't do this.
1) 每当您遇到数据绑定问题时,请查看“输出”窗口以确保您没有收到任何绑定错误。如果您不这样做,您可能会花费大量时间来尝试解决错误的问题。
2) Understand the role change notification plays in binding. Changes in your data source can't and won't get propagated to the UI unless the data source implements change notification. There are two ways to do this for normal properties: make the data source derive from DependencyObject
and make the bound property a dependency property, or make the data source implement INotifyPropertyChanged
and raise the PropertyChanged
event when the property's value changes. When binding an ItemsControl
to a collection, use a collection class that implements INotifyCollectionChanged
(like ObservableCollection<T>
), so that changes to the contents and order of the collection will get propagated to the bound control. (Note that if you want changes to the items inthe collection to get propagated to the bound controls, those items need to implement change notification too.)
2) 了解变更通知在绑定中的作用。除非数据源实现更改通知,否则数据源中的更改不能也不会传播到 UI。对于普通属性,有两种方法可以做到这一点:使数据源派生自DependencyObject
并将绑定的属性设为依赖属性,或者使数据源实现INotifyPropertyChanged
并PropertyChanged
在属性值更改时引发事件。将 an 绑定ItemsControl
到集合时,请使用实现INotifyCollectionChanged
(如ObservableCollection<T>
)的集合类,以便对集合的内容和顺序的更改将传播到绑定控件。(请注意,如果你想改变的项目中要传播到绑定控件的集合,这些项目也需要实现更改通知。)
回答by Binil
change your binding to
将您的绑定更改为
<ListBox ItemsSource="{Binding Path=TopicList}"