locked
DataGrid and ObservableCollection RRS feed

  • 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
  • Thanks
    Tuesday, April 3, 2012 4:08 PM