WPF DataBinding ObservableCollection <T> for DataGrid
I am trying to create a DataGrid in a separate UserControl whose DataContext is a list T.
In the code behind, I create a list, populate the list, and then submit it to the constructor for UserControl, on which I have a DataGrid that I am trying to populate.
The UserControl class is as follows.
public partial class QuotePreview : UserControl { private static SelectionList previewList = new SelectionList(); public SelectionList PreviewList { get { return previewList; } } public QuotePreview() { InitializeComponent(); } public QuotePreview(SelectionList selectedOptions) { InitializeComponent(); previewList = selectedOptions; QuotePreviewDataGrid.DataContext = previewList; } } And Xaml looks like this:
<DataGrid Name="QuotePreviewDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding}"> <DataGrid.Columns> <DataGridTextColumn Header="Model Number" Binding="{Binding ModelNumber}"/> <DataGridTextColumn Header="Description" Binding="{Binding Description}"/> <DataGridTextColumn Header="List Price per Unit" Binding="{Binding Price}"/> </DataGrid.Columns> </DataGrid> I also tried installing ItemSource using
QuotePreviewDataGrid.ItemsSource = PreviewList; I also tried to set both the data context and the data source, and also update:
QuotePreviewDataGrid.Items.Refresh(); Linking the data I set to the list in the rest of the application works fine. In the list boxes, I have the items source set to {Binding} and the ListItems binding set to {Binding Property}. Datacontext for lists set in code.
My datagrid is configured in the same way here, but for some reason nothing is displayed inside the grid.
When I look at the debugger and observe the flow of information, I see that a List of T, SelectionsList is created and passed to the constructor for the user control where the data grid is located. I see that the DataContext is really set and shows the elements in the list, but when I return to my topic and try to view the data grid, it is empty.
Any help would be greatly appreciated. I have been trying to ponder this problem for the past one and a half days. Thanks!
UPDATE
The SelectionList element is configured as follows:
public class SelectionList : List<Selection> { public List<Selection> availableSelections = new List<Selection>(); public List<Selection> AvailableSelections { get { return availableSelections; } } } and then the selection is determined as follows:
public class Selection : DependencyObject { public bool IsChecked { get; set; } public string ModelNumber { get; set; } public string Description { get; set; } public string Price { get; set; } } When the application starts, I create a catalog of existing products (Selections). On different tabs, one for each product family, the datacontext for the product list window is initialized with the available products that it extracts from the catalog. Then, waiting for which product the user selects, the available options or children associated with that product are populated with the appropriate lists, accessories, and warranties.
As soon as the user selects the parameters they need, a button is pressed to view the selected elements that should fill the data grid described above.
I can create a list of the selected options, however, when I try to set the data context of the data grid, nothing appears. Lists for the available selections are created and set in the appropriate data context, just as I am trying to do this for a data grid, however the data grid does not want to display my information.
UPDATE
So, after some additional debugging, I narrowed down the problem a bit. Data binding works as it should. I have no real problems, I do not think. However, the problem I'm currently facing is what I consider to be two different instances of my User Control, but only the original is displayed, not the updated copy.
Here's a copy of the class with about two lines that I added to help debug the problem.
public partial class QuotePreview : UserControl { private SelectionList _selectionList; private SelectionList temp; public QuotePreview() { InitializeComponent(); _selectionList = (SelectionList)this.DataContext; } private void QuotePreview_Loaded(object sender, RoutedEventArgs e) { _selectionList.SelectedOptions.Add( new Selection { ModelNumber = "this", Description = "really", Price = "sucks" }); } public QuotePreview(SelectionList selectedOptions) { InitializeComponent(); _selectionList = (SelectionList)this.DataContext; temp = selectedOptions; _selectionList.AddRange(selectedOptions); QuotePreview_Loaded(); } private void QuotePreview_Loaded() { foreach (var options in temp.SelectedOptions) { _selectionList.SelectedOptions.Add(options); } QuotePreviewDataGrid.ItemsSource = _selectionList.SelectedOptions; } } The default constructor implementation is called each time a user control / tab is clicked. When this happens, the _selectionList is set to the data context of the user control, and then a loaded event that adds a row to my data grid.
In another user control, where I select the parameters that I want to add to the user data network control, I click a button that creates a list of parameters that I want to add and calls the custom constructor that I wrote. As soon as the constructor finishes, it calls the custom Loaded Event method that I created for shits and higgles, which adds the selected parameters to my _selectionList.
Now, as soon as I click on the data network control again, it will go through the whole process by default and add another line by default.
If I go back to the tab and say that I want these parameters again and go back to the data grid, it will go through the default process again and add another row by default.
Which is the most intriguing, although I see that both selectionLists are being built, since I am not clearing between processes. I see a list containing the options I want to display, and an assembly of the list of default options built ...
Oh, also, a SelectionList implements an ObservableCollection.
Finally, I came up with a solution to the problem.
public static class QuotePreview { public static ObservableCollection<PurchasableItem> LineItems { get; private set; } static QuotePreview() { LineItems = new ObservableCollection<PurchasableItem>(); } public static void Add(List<PurchasableItems> selections) { foreach (var selection in selections) { LineItems.Add(selection); } } public static void Clear() { LineItems.Clear(); } } public class QuoteTab : TabItem { public ObservableCollection<PurchasableItem> PreviewItems { get; private set; } public QuoteTab() { Initialize() PreviewItems = QuotePreview.LineItems; DataGrid.ItemSource = PreviewItems } } Try changing:
QuotePreviewDataGrid.DataContext = previewList; to
this.DataContext = previewList; My suspicion is that ItemsSource="{Binding}" in your xaml is overriding the DataContext code in your constructor.
By changing the preliminary list as the DataContext of the entire UserControl, you can correctly evaluate the binding of the DataGrid ItemsSource .
On the other hand, I would begin to study the use of ObservableCollection<T> and the MVVM design pattern. The problem that may arise as a result is that your DataGrid does not update when the base list changes, using ObservableCollection<T> will fix this.
Using the MVVM design pattern will give you a good separation of your logic and data (in this case, your list and its loading) from the physical display (DataGrid)