views:

58

answers:

2

UPDATE: See the bottom of this question for what I did to solve the problem.

I'm trying to understand how the ItemsSource and DataContext properties work in a Silverlight Toolkit DataGrid. I'm currently working with dummy data and trying to get the data in the DataGrid to update when the value of a combo box changes.

My MainPage.xaml.vb file currently looks like this:

Partial Public Class MainPage
    Inherits UserControl

Private IssueSummaryList As List(Of IssueSummary)    

Public Sub New()
    GetDummyIssueSummary("Day")
    InitializeComponent()
    dgIssueSummary.ItemsSource = IssueSummaryList
    'dgIssueSummary.DataContext = IssueSummaryList        '
End Sub

Private Sub GetDummyIssueSummary(ByVal timeInterval As String)
    Dim lst As New List(Of IssueSummary)()
    'Generate dummy data for lst '
    IssueSummaryList = lst
End Sub

Private Sub ComboBox_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
    Dim cboBox As ComboBox = CType(sender, ComboBox)
    Dim cboBoxItem As ComboBoxItem = CType(cboBox.SelectedValue, ComboBoxItem)
    GetDummyIssueSummary(cboBoxItem.Content.ToString())
End Sub

End Class

My XAML currently looks this for the DataGrid:

<sdk:DataGrid x:Name="dgIssueSummary" AutoGenerateColumns="False" >
    <sdk:DataGrid.Columns>
        <sdk:DataGridTextColumn Binding="{Binding ProblemType}" Header="Problem Type"/>
        <sdk:DataGridTextColumn Binding="{Binding Count}" Header="Count"/>
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

The problem is that if I set the value of the ItemsSource property of the data grid equal to IssueSummaryList, it will display the data when it loads, but it won't update when the underlying IssueSummaryList collection changes. If I set the DataContext of the grid to be IssueSummaryList, no data will be displayed when it renders.

I must not understand how ItemsSource and DataContext are supposed to function, because I expect one of those properties to "just work" when I assign a List object to them. What do I need to understand and change in my code so that as data changes in the List, the data in the grid is updated?


Here's what I did to solve the problem:

  • First, I changed IssueSummaryList to be of type System.Collections.ObjectModel.ObservableCollection. The ObservableCollection object raises events when items are added or removed from the collection, telling the datagrid to refresh its view.
  • Next, I changed the GetDummyIssueSummary() method to modify IssueSummaryList directly instead of setting it to a new instance of a List or ObservableCollection.

These two changes allowed the data grid to refresh when the combo box changed.

A: 

ItemsSource is where the items come from, DataContext is where the bindings are relative to.

E.g.

If ItemsSource is set via code, then the DataContext is irrelevant to ItemsSource.

If ItemsSource="{Binding MyList}", then ItemsSource is connected to where ever data context is set to + ".MyList".

If ItemsSource="{Binding}", then ItemsSource is connected to where ever data context is set to.

Danny Varod
+1  A: 

DataContext is used for binding dependency property values to another source. ItemsSource is used for an ItemsControl to provide a list of objects to display.

If all you want to do is to be able to set this list in your code and not in XAML or through a data binding expression, then setting DataContext is not required (nor will it do anything for you if you do set it). It's also worth noting that the data context used for binding will check the control parent recursively until it finds a non-null value. In other words, if your DataContext is null, then it will check the control's parent, then its parent, and so on until it finds a non-null value to use (or it reaches the end of the chain).

When you say that it won't update when the underlying list changes, what sort of change are you referring to? If you're speaking of adding and removing items, then what type of collection are you using? In order for this to work, the type needs to implement INotifyCollectionChanged or IBindingList. ObservableCollection<T> implements INotifyCollectionChanged as is a common candidate for this sort of thing.

If you're saying that changes to the contents of the list (meaning property changes to items that are in the list, not changes to the list itself) are not reflected, then you'll need to ensure that your objects implement INotifyPropertyChanged.

Adam Robinson
Clarification, use INotifyPropertyChanged if the changes to the contents of already displayed items in the list are not reflected.
Danny Varod
@Danny: That is what I meant, though I'll admit that it wasn't entirely clear. I was trying to differentiate between changes *to the list* (adding, removing, reordering, etc.) and changes *to its items* (property changes). I'll try to edit to make this clearer.
Adam Robinson
@Adam Thanks for your response. I changed `IssueSummaryList` to be an `ObservableCollection` and modified `GetDummyIssueSummary()` accordingly, but the grid still isn't updating when the `IssueSummaryList` gets reset. I have `dgIssueSummary.ItemsSource` set it my constructor still, but it doesn't seem to "stick."
Ben McCormack
@Ben: `IssueSummaryList` is an instance variable. While you're setting `ItemsSource = IssueSummaryList` in the constructor, subsequent changes to `IssueSummaryList` won't be reflected automatically in the grid (just like any other variable). You'll need to set the `ItemsSource` property again.
Adam Robinson
@Adam That makes sense. I modified my `GetDummyIssueSummary()` method to modify `IssueSummaryList` directly instead of setting it to a new instance of `ObservableCollection`, and that made it work correctly. Thanks again for your help.
Ben McCormack