Answered by:
DataGrid and ObservableCollection

Question
-
I am trying to fill a DataGrid with a 2D Array of a property in my data object. When the follwoing is executed I only see a single column populated. Also, how do I display a column header that just has the header number similar to what is displaying in the row header.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Data; namespace DataGridBindingExample { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { this.DataContext = new MyViewModel(); InitializeComponent(); } private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) { e.Row.Header = (e.Row.GetIndex() + 1).ToString(); } } public class MyViewModel { ObservableCollection<ObservableCollection<MyObject>> _dataTable; public MyViewModel() { _dataTable = new ObservableCollection<ObservableCollection<MyObject>>(); FillTable(); } public void FillTable() { _dataTable.Clear(); for (int j = 0; j < 20; j++) { ObservableCollection<MyObject> row = new ObservableCollection<MyObject>(); for (int i = 0; i < 10; i++) { row.Add(new MyObject(1, j, i)); } _dataTable.Add(row); } } public ObservableCollection<ObservableCollection<MyObject>> DataTable { get { return _dataTable; } set { if (value != _dataTable) { _dataTable = value; } } } } public class MyObject : INotifyPropertyChanged { double _value; int _rowIndex; int _colIndex; public double Value { get { return _value; } set { if (value != _value) { _value = value; OnPropertyChanged("Value"); } } } public int RowIndex { get { return _rowIndex; } set { if (Value != _rowIndex) { _rowIndex = value; OnPropertyChanged("RowIndex"); } } } public int ColIndex { get { return _colIndex; } set { if (Value != _colIndex) { _colIndex = value; OnPropertyChanged("ColIndex"); } } } public MyObject(double value, int rowIndex, int colIndex) { Value = value; RowIndex = rowIndex; ColIndex = colIndex; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { this.VerifyPropertyName(propertyName); PropertyChangedEventHandler handler = this.PropertyChanged; if (handler != null) { var e = new PropertyChangedEventArgs(propertyName); handler(this, e); } } [Conditional("DEBUG")] [DebuggerStepThrough] public void VerifyPropertyName(string propertyName) { // Verify that the property name matches a real, // public, instance property on this object. if (TypeDescriptor.GetProperties(this)[propertyName] == null) { string msg = "Invalid property name: " + propertyName; if (this.ThrowOnInvalidPropertyName) throw new Exception(msg); else Debug.Fail(msg); } } protected virtual bool ThrowOnInvalidPropertyName { get; private set; } } }
<Window x:Class="DataGridBindingExample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <DataGrid AutoGenerateColumns="True" Height="Auto" Margin="6,6,6,6" VerticalAlignment="Top" HorizontalContentAlignment="left" VerticalContentAlignment="Top" ColumnHeaderHeight="25" RowHeaderWidth="30" ColumnWidth="50" ItemsSource="{Binding DataTable, Mode=TwoWay}" LoadingRow="DataGrid_LoadingRow" /> </Grid> </Window>
Sunday, April 1, 2012 5:29 PM
Answers
-
Your sample is rectangular at initialisation - but the whole idea of ObservableCollection is that you can add and delete items and have the bindings update.To use a DataGrid you would have to wrap the inner ObservableCollection as a seperate class (MyRow?) and then expose a property for each column you wish to display (not much point in basing it on a collection in that case).
Otherwise the nested ItemControls will give you access to each of your MyObject and will expand to adapt to any dimentions (even a jaggerd/raggerd array)
If you wish to be able to modify the Value property use a TextBox (with TwoWay binding) rather than TextBlock as the ItemTemplate. It dosn't take much to make it look like a DataGrid.
Good luck
John
- Edited by PrismPoint Tuesday, April 3, 2012 4:51 AM
- Marked as answer by jfras2009 Tuesday, April 3, 2012 4:08 PM
Tuesday, April 3, 2012 4:46 AM
All replies
-
Hi,
What exactly do you want to see appear in the datagrid?
Sunday, April 1, 2012 5:50 PM -
In this example, MyObject had three properties and therefore the observablecollection was 20 rows and 10 columns of MyObject. I only want to dispaly the Value property of MyObject in the datagrid. I expect the DataGrid to have ten columns and twenty rows with each cell having a MyObject.Value of 1.0. When displayed the datagrid had one coumn and 20 rows. Why didn't the other columns get populated? I quess I never even told it to display the MyObject.Value property so how would I do this?
Thanks
- Edited by jfras2009 Sunday, April 1, 2012 8:40 PM
Sunday, April 1, 2012 6:29 PM -
Hi,
I doubt you can ever get a DataGrid to display a ragged array - may be able to do something if you replace the inner ObservableCollection with something that has a fixed number of 'columns'.
Otherwise replacing the DataGrid with nested ItemControls like this will be a start (you would have to do your own thing for column and row headings)
<Grid> <ScrollViewer> <ItemsControl ItemsSource="{Binding DataTable}"> <ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding }"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Margin="8,0,0,0"> <Border Width="30" BorderThickness="1" CornerRadius="3" BorderBrush="Black" /> <TextBlock Text="{Binding Value}" HorizontalAlignment="Center" /> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </ScrollViewer> </Grid>
Cheers
John
- Edited by PrismPoint Monday, April 2, 2012 5:42 AM
- Proposed as answer by PrismPoint Monday, April 2, 2012 10:55 PM
Monday, April 2, 2012 5:37 AM -
The ObservableCollection<Observable<MyObject> is rectangular. I am using a DataGrid because I would like to get the selected items. Can I do this with a DataGrid?
Tuesday, April 3, 2012 1:11 AM -
Your sample is rectangular at initialisation - but the whole idea of ObservableCollection is that you can add and delete items and have the bindings update.To use a DataGrid you would have to wrap the inner ObservableCollection as a seperate class (MyRow?) and then expose a property for each column you wish to display (not much point in basing it on a collection in that case).
Otherwise the nested ItemControls will give you access to each of your MyObject and will expand to adapt to any dimentions (even a jaggerd/raggerd array)
If you wish to be able to modify the Value property use a TextBox (with TwoWay binding) rather than TextBlock as the ItemTemplate. It dosn't take much to make it look like a DataGrid.
Good luck
John
- Edited by PrismPoint Tuesday, April 3, 2012 4:51 AM
- Marked as answer by jfras2009 Tuesday, April 3, 2012 4:08 PM
Tuesday, April 3, 2012 4:46 AM -
ThanksTuesday, April 3, 2012 4:08 PM