Although somewhat experienced with writing Winforms applications, the... "vagueness" of WPF still eludes me in terms of best practices and design patterns.
Despite populating my list at runtime, my listbox appears empty.
I have followed the simple instructions from this helpful article to 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.
In my MainWindow.xaml, I have:
<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.
public ObservableCollection<CheckedListItem> TopicList;
EDIT #2
I have made two changes that help:
In the .xaml file:
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.
Use
ObservableCollection<Topic>
instead ofList<Topic>
Edit
it implements INotifyCollectionChanged interface to let WPF know when you add/remove/modify items
Edit 2
Since you set
TopicList
in code, it should be a Dependency Property, not a common fieldEdit 3
To see changes in items
INotifyPropertyChanged
interface inCheckedListItem
(each setter should callPropertyChanged(this, new PropertyChangedEventArgs(<property name as string>))
event)CheckedListItem
fromDependencyObject
, and convertName
,ID
,IsChecked
to dependency propertiestopicList[0] = new CheckedListItem() { Name = ..., ID = ... }
)Assuming
TopicList
is not anObservableCollection<T>
therefore when you add items noINotifyCollection
changed is being fired to tell the binding engine to update the value.Change your
TopicList
to anObservableCollection<T>
which will resolve the current issue. You could also populate theList<T>
ahead of time and then the binding will work via OneWay; howeverObservableCollection<T>
is a more robust approach.EDIT:
Your
TopicList
needs to be a property not a member variable; bindings require properties. It does not need to be aDependencyProperty
.EDIT 2:
Modify your
ItemTemplate
as it does not need to be aHierarchicalDataTemplate
change your binding to
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.
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.
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 implementINotifyPropertyChanged
and raise thePropertyChanged
event when the property's value changes. When binding anItemsControl
to a collection, use a collection class that implementsINotifyCollectionChanged
(likeObservableCollection<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 in the collection to get propagated to the bound controls, those items need to implement change notification too.)