WPF MvvM DataGrid Dynamic Columns

I’m going to preface this by saying it maybe isn’t the best solution to be doing and may not work in some situations but you can give it a try and see if it will work for what you want. I just whipped this up so it may have some bugs in it. Its still going to involve some code, but it keeps your model from knowing about you view.

What you need to do is create an extension property that will allow you to bind the Columns property on the DataGrid. Here is an example that I put together.

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;

public static class DataGridExtension
{
    public static ObservableCollection<DataGridColumn> GetColumns(DependencyObject obj)
    {
        return (ObservableCollection<DataGridColumn>)obj.GetValue(ColumnsProperty);
    }

    public static void SetColumns(DependencyObject obj, ObservableCollection<DataGridColumn> value)
    {
        obj.SetValue(ColumnsProperty, value);
    }

    public static readonly DependencyProperty ColumnsProperty =  
           DependencyProperty.RegisterAttached("Columns",
           typeof(ObservableCollection<DataGridColumn>),
           typeof(DataGridExtension),
           new UIPropertyMetadata (new ObservableCollection<DataGridColumn>(),
           OnDataGridColumnsPropertyChanged));


    private static void OnDataGridColumnsPropertyChanged(
           DependencyObject d,
           DependencyPropertyChangedEventArgs e)
    {
        if (d.GetType() == typeof(DataGrid))
        {
            DataGrid myGrid = d as DataGrid;

            ObservableCollection<DataGridColumn> Columns = 
                 (ObservableCollection<DataGridColumn>)e.NewValue;

            if(Columns != null)
            {
                myGrid.Columns.Clear();

                if (Columns != null && Columns.Count > 0)
                {
                    foreach (DataGridColumn dataGridColumn in Columns)
                    {
                        myGrid.Columns.Add(dataGridColumn);
                    }
                }


                Columns.CollectionChanged += delegate(object sender,
                                 NotifyCollectionChangedEventArgs args)
                     {
                       if(args.NewItems != null)
                       {
                         foreach (DataGridColumn column 
                              in args.NewItems.Cast<DataGridColumn>())
                         {
                             myGrid.Columns.Add(column);
                         }
                       }

                       if(args.OldItems != null)
                       {
                         foreach (DataGridColumn column 
                                 in args.OldItems.Cast<DataGridColumn>())
                         {
                           myGrid.Columns.Remove(column);
                         }
                       }
                    };
            }
        }
    }
}

Then you attach it to you DataGrid like this (Where columns is the an ObservableCollection property on your view model)

<Controls:DataGrid AutoGenerateColumns="False" 
            DataGridExtension.Columns="{Binding Columns}" />

I’m not sure how well it is going to respond if you start adding and remove columns, but it seems to work from my basic testing. Good Luck!

Leave a Comment