none
[MVVM] Dynamicly add Columns in Datagrid

    Question

  • Hello,

    is there any Possibility to add Collumns from my Viemodel ? I need to add ComboBoxcolumns an TextColumns.

    Hope there is anybody who can help me.

    I strongly need to do this in my Viewmodel and not in Codebehin.

    Thursday, March 01, 2012 7:06 AM

Answers

  • Here's what I did ;

    On my ViewModel, observable collection that contains the columns ;

            private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>();
            public ObservableCollection<DataGridColumn> ColumnCollection 
            {
                get
                {
                    return this._columnCollection;
                }
                set
                {
                    _columnCollection = value;
                    base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection);
                }
            }

    I then create a behavior class for ;

    public class DataGridColumnsBehavior
        {
            public static readonly DependencyProperty BindableColumnsProperty =
                DependencyProperty.RegisterAttached("BindableColumns",
                                                    typeof(ObservableCollection<DataGridColumn>),
                                                    typeof(DataGridColumnsBehavior),
                                                    new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
            private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
            {
                DataGrid dataGrid = source as DataGrid;
                ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
                dataGrid.Columns.Clear();
                if (columns == null)
                {
                    return;
                }
                foreach (DataGridColumn column in columns)
                {
                    dataGrid.Columns.Add(column);
                }
                columns.CollectionChanged += (sender, e2) =>
                {
                    NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
                    if (ne.Action == NotifyCollectionChangedAction.Reset)
                    {
                        dataGrid.Columns.Clear();
                        if (ne.NewItems != null)
                        {
                            foreach (DataGridColumn column in ne.NewItems)
                            {
                                dataGrid.Columns.Add(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Add)
                    {
                        if (ne.NewItems != null)
                        {
                            foreach (DataGridColumn column in ne.NewItems)
                            {
                                dataGrid.Columns.Add(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Move)
                    {
                        dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Remove)
                    {
                        if (ne.OldItems != null)
                        {
                            foreach (DataGridColumn column in ne.OldItems)
                            {
                                dataGrid.Columns.Remove(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Replace)
                    {
                        dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
                    }
                };
            }
            public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
            {
                element.SetValue(BindableColumnsProperty, value);
            }
            public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
            {
                return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
            }
        }

    and then in my view , i add the following line to the datagrid.

    <DataGrid
    Grid.Row="0" 
    Grid.Column="1" 
    ItemsSource="{Binding myDataCollectionOnMyViewModel}" 
    bh:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}" 

    I populate my column collection in the view model by

    this.ColumnCollection.Add(New DataGridTextColumn(......

    and the binding makes them appears on the datagrid.

    Hope this helps,


    Tuesday, March 20, 2012 4:16 PM
  • Hi liberado,

    I happen to see this thread. And I think we can generate the column automatically by setting the property AutoGenerateColumns to True. So if we could dynamically add the Column into the bound DataTable, the DataGrid should show the changes by refresh its DataBinding.

    As below link:http://code.msdn.microsoft.com/How-to-add-the-Column-into-2ad31c47

    In this MVVM case, you need to refresh the ViewModel to update DataGrid View.

    Hope it helps.

    Have a nice day.


    Annabella Luo[MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, March 20, 2012 9:22 AM

All replies

  • I'm sorry but this is not MVVM.

    Thursday, March 01, 2012 12:50 PM
  • Can you add the colums at startup, and the set visibility as hidden?

    Kenneth

    Thursday, March 01, 2012 1:54 PM
  • I haven't used the WPF datagrid (we use the Infragistics XamDataGrid), but I don't think there's a way to do this without any code-behind.  I would look into expanding the datagrid by creating a custom control based off it.  Maybe use that Silverlight article as a starting point for implementing that functionality in your custom control: have it create columns based on some new collection property that you implement in that derived class, and create the columns based on whatever comes from that collection.  Not necessarily straightforward though, though the advantage of a custom control (as opposed to code-behind) is that it would be reusable all over the place.

    We've had to expand a few controls such as the XamDataTree to make them more MVVM-friendly, but it was to perform easier tasks than this.

    Thursday, March 01, 2012 2:26 PM
  • What I've done is use auto-generated columns and then bind the DataGrid to an IEnumerable<dynamic> where I generate a set of ExpandoObject elements in my ViewModel.  You can cast the ExpandoObject to an IDictionary<string,object> and then populate the columns at runtime by simply adding dictionary keys.   I've used this approach a couple of times without a problem.  If you need more control over the column generation, hook into the AutoGeneratedColumns event on the DataGrid and then create the columns in code behind (using the distinct keys from the dictionary bound to the ItemsSource).
    Thursday, March 01, 2012 9:38 PM
  • Hi liberado,

    I happen to see this thread. And I think we can generate the column automatically by setting the property AutoGenerateColumns to True. So if we could dynamically add the Column into the bound DataTable, the DataGrid should show the changes by refresh its DataBinding.

    As below link:http://code.msdn.microsoft.com/How-to-add-the-Column-into-2ad31c47

    In this MVVM case, you need to refresh the ViewModel to update DataGrid View.

    Hope it helps.

    Have a nice day.


    Annabella Luo[MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, March 20, 2012 9:22 AM
  • Here's what I did ;

    On my ViewModel, observable collection that contains the columns ;

            private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>();
            public ObservableCollection<DataGridColumn> ColumnCollection 
            {
                get
                {
                    return this._columnCollection;
                }
                set
                {
                    _columnCollection = value;
                    base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection);
                }
            }

    I then create a behavior class for ;

    public class DataGridColumnsBehavior
        {
            public static readonly DependencyProperty BindableColumnsProperty =
                DependencyProperty.RegisterAttached("BindableColumns",
                                                    typeof(ObservableCollection<DataGridColumn>),
                                                    typeof(DataGridColumnsBehavior),
                                                    new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
            private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
            {
                DataGrid dataGrid = source as DataGrid;
                ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
                dataGrid.Columns.Clear();
                if (columns == null)
                {
                    return;
                }
                foreach (DataGridColumn column in columns)
                {
                    dataGrid.Columns.Add(column);
                }
                columns.CollectionChanged += (sender, e2) =>
                {
                    NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
                    if (ne.Action == NotifyCollectionChangedAction.Reset)
                    {
                        dataGrid.Columns.Clear();
                        if (ne.NewItems != null)
                        {
                            foreach (DataGridColumn column in ne.NewItems)
                            {
                                dataGrid.Columns.Add(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Add)
                    {
                        if (ne.NewItems != null)
                        {
                            foreach (DataGridColumn column in ne.NewItems)
                            {
                                dataGrid.Columns.Add(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Move)
                    {
                        dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Remove)
                    {
                        if (ne.OldItems != null)
                        {
                            foreach (DataGridColumn column in ne.OldItems)
                            {
                                dataGrid.Columns.Remove(column);
                            }
                        }
                    }
                    else if (ne.Action == NotifyCollectionChangedAction.Replace)
                    {
                        dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
                    }
                };
            }
            public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
            {
                element.SetValue(BindableColumnsProperty, value);
            }
            public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
            {
                return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
            }
        }

    and then in my view , i add the following line to the datagrid.

    <DataGrid
    Grid.Row="0" 
    Grid.Column="1" 
    ItemsSource="{Binding myDataCollectionOnMyViewModel}" 
    bh:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}" 

    I populate my column collection in the view model by

    this.ColumnCollection.Add(New DataGridTextColumn(......

    and the binding makes them appears on the datagrid.

    Hope this helps,


    Tuesday, March 20, 2012 4:16 PM