locked
WPF: how to make dynamical adding columns sortable? RRS feed

  • Question

  • Our WPF application needs a Data sheet which requires dynamically adding columns (we know how many columns during run time) and able to sort column by clicking on column header.

    We found the link: http://elegantcode.com/2010/03/08/silverlight-datagrid-populate-dynamic-columns-from-a-child-collection/

    which provides an DataGrid example we are looking for. However, we found out one limitation with this implementation is that it cannot sort on column header.

    Does anyone know how to fix this limitation?

    or is there approach which will satisfied two conditions: dynamically add columns in run time and sorting columns? Thx!


    JaneC

    Friday, July 31, 2015 5:11 PM

Answers

  • Hi Jane,

    There's nothing about dynamically added columns which would stop them being sortable. Or any less sortable than their static equivalents defined in xaml.

    The simplest way to do a dynamic datagrid is using a datatable, build one with the columns you want and use the default autogenerate to create them. Bind the defaultview of the datatable to the itemssource of the datagrid.

    I threw together a simple example for another thread recently:

    Markup is very simple:

        <Grid>
            <DataGrid Name="dg" AutoGenerateColumns="False"/>
        </Grid>

    Code behind

            public MainWindow()
            {
                InitializeComponent();
                DataTable table = new DataTable();
                table.Columns.Add("TrueFalse", typeof(bool));
                table.Columns.Add("Description", typeof(string));
    
    
                table.Rows.Add(true, "Apple");
                table.Rows.Add(false, "Banana");
                table.Rows.Add(false, "Pear");
                foreach (DataColumn col in table.Columns )
                {
                    DataGridBoundColumn _xtemp = null;
                    if (col.DataType == typeof(Boolean) )
                    {
                        _xtemp = new DataGridCheckBoxColumn();
                    }
                    else
                    {
                        _xtemp = new DataGridTextColumn();
                    }
    
                    _xtemp.Header = col.ColumnName;
                    _xtemp.Binding = new Binding(col.ColumnName);
                    dg.Columns.Add(_xtemp);
                }
    
                dg.ItemsSource = table.DefaultView;
            }
    When I run that, both those columns are sorted if you click their header.

    .

    I use that xamlreader approach from your link a fair bit. I've worked on couple of Silverlight apps where large parts of the UI were dynamic and the user gets the xaml for them as a string which is then converted to UI using xamlreader.load.

    With wpf you have more options than Silverlight because you can use xamlreader.parse to work with a string.

    I use templates and manipulate them like:

    http://social.technet.microsoft.com/wiki/contents/articles/28797.wpf-dynamic-xaml.aspx#Awkward_Bindings_Data

    If you're binding to an observablecollection ( or the default view of a datatable ) then the columns will be text or bool rather than template.

    These are all sortable by default.

    Template columns aren't, unless you set SortMemberPath. I guess this might be the cause of your issue.

    <DataGridTemplateColumn SortMemberPath="SomeColumn"


    Please provide more information if none of this addresses your issue.


    • Proposed as answer by Xavier Xie-MSFT Monday, August 3, 2015 6:41 AM
    • Marked as answer by JJChen Monday, August 3, 2015 6:26 PM
    Friday, July 31, 2015 5:53 PM
  • >>If using "DataGridTemplateColumn", the column header is not sortable.

    When adding a DataGridTemplateColumn, you must set the SortMemberPath to the name of the property of the source object that you want to sort by:

    private DataGridTemplateColumn CreateTemplateColumn(int i, string propName)
    {
        DataGridTemplateColumn column = new DataGridTemplateColumn();
        column.Header = String.Format("Period#{0}.{1}", i, propName);
        column.CellTemplate = (DataTemplate)XamlReader.Load(CreateColumnTemplate(i, propName)); //display template
        column.CellEditingTemplate = (DataTemplate)XamlReader.Load(CreateColumnEditTemplate(i, propName)); //edit template
        column.SortMemberPath = propName;
        return column;
    }

    Hope that helps.

    Please remember to mark all helpful posts as answer.

    • Marked as answer by JJChen Tuesday, August 4, 2015 10:03 PM
    Monday, August 3, 2015 8:08 PM

All replies

  • Hi Jane,

    There's nothing about dynamically added columns which would stop them being sortable. Or any less sortable than their static equivalents defined in xaml.

    The simplest way to do a dynamic datagrid is using a datatable, build one with the columns you want and use the default autogenerate to create them. Bind the defaultview of the datatable to the itemssource of the datagrid.

    I threw together a simple example for another thread recently:

    Markup is very simple:

        <Grid>
            <DataGrid Name="dg" AutoGenerateColumns="False"/>
        </Grid>

    Code behind

            public MainWindow()
            {
                InitializeComponent();
                DataTable table = new DataTable();
                table.Columns.Add("TrueFalse", typeof(bool));
                table.Columns.Add("Description", typeof(string));
    
    
                table.Rows.Add(true, "Apple");
                table.Rows.Add(false, "Banana");
                table.Rows.Add(false, "Pear");
                foreach (DataColumn col in table.Columns )
                {
                    DataGridBoundColumn _xtemp = null;
                    if (col.DataType == typeof(Boolean) )
                    {
                        _xtemp = new DataGridCheckBoxColumn();
                    }
                    else
                    {
                        _xtemp = new DataGridTextColumn();
                    }
    
                    _xtemp.Header = col.ColumnName;
                    _xtemp.Binding = new Binding(col.ColumnName);
                    dg.Columns.Add(_xtemp);
                }
    
                dg.ItemsSource = table.DefaultView;
            }
    When I run that, both those columns are sorted if you click their header.

    .

    I use that xamlreader approach from your link a fair bit. I've worked on couple of Silverlight apps where large parts of the UI were dynamic and the user gets the xaml for them as a string which is then converted to UI using xamlreader.load.

    With wpf you have more options than Silverlight because you can use xamlreader.parse to work with a string.

    I use templates and manipulate them like:

    http://social.technet.microsoft.com/wiki/contents/articles/28797.wpf-dynamic-xaml.aspx#Awkward_Bindings_Data

    If you're binding to an observablecollection ( or the default view of a datatable ) then the columns will be text or bool rather than template.

    These are all sortable by default.

    Template columns aren't, unless you set SortMemberPath. I guess this might be the cause of your issue.

    <DataGridTemplateColumn SortMemberPath="SomeColumn"


    Please provide more information if none of this addresses your issue.


    • Proposed as answer by Xavier Xie-MSFT Monday, August 3, 2015 6:41 AM
    • Marked as answer by JJChen Monday, August 3, 2015 6:26 PM
    Friday, July 31, 2015 5:53 PM
  • Hi Andy,

    thanks for replying our question and points out "dynamically added columns which would stop them being sortable."

    We have followed the link to implement the solution.

    What we found out, if using "DataGridTextColumn", the column header is sortable.

    If using "DataGridTemplateColumn", the column header is not sortable.

    private string CreateColumnTemplate(int index, string propertyName)
            {
                StringBuilder CellTemp = new StringBuilder();
                CellTemp.Append("<DataTemplate ");
                CellTemp.Append("xmlns='http://schemas.microsoft.com/winfx/");
                CellTemp.Append("2006/xaml/presentation' ");
                CellTemp.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>");
                CellTemp.Append(String.Format("<TextBlock Text='{{Binding BMBMFIs[{0}].{1}}}'/>", index, propertyName));
                CellTemp.Append("</DataTemplate>");
                return CellTemp.ToString();
            }

    private DataGridTemplateColumn CreateTemplateColumn(int i, string propName)
    {
        DataGridTemplateColumn column = new DataGridTemplateColumn();
        column.Header = String.Format("Period#{0}.{1}", i, propName);
        column.CellTemplate = (DataTemplate)XamlReader.Load(CreateColumnTemplate(i, propName)); //display template
        column.CellEditingTemplate = (DataTemplate)XamlReader.Load(CreateColumnEditTemplate(i, propName)); //edit template
        return column;
    }

    So we add dynamically column by using "DataGridTextColumn", all dynamical columns are sortable now.

    DataGridTextColumn column = new DataGridTextColumn();
                column.Width = width;
                column.Header = description;
                column.CanUserSort = true;
                column.Binding = new System.Windows.Data.Binding(string.Format("dynamicalCollection[{0}].{1}", i, propName));
                return column;

    cheers!



    JaneC



    • Edited by JJChen Monday, August 3, 2015 6:28 PM
    Monday, August 3, 2015 6:26 PM
  • >>If using "DataGridTemplateColumn", the column header is not sortable.

    When adding a DataGridTemplateColumn, you must set the SortMemberPath to the name of the property of the source object that you want to sort by:

    private DataGridTemplateColumn CreateTemplateColumn(int i, string propName)
    {
        DataGridTemplateColumn column = new DataGridTemplateColumn();
        column.Header = String.Format("Period#{0}.{1}", i, propName);
        column.CellTemplate = (DataTemplate)XamlReader.Load(CreateColumnTemplate(i, propName)); //display template
        column.CellEditingTemplate = (DataTemplate)XamlReader.Load(CreateColumnEditTemplate(i, propName)); //edit template
        column.SortMemberPath = propName;
        return column;
    }

    Hope that helps.

    Please remember to mark all helpful posts as answer.

    • Marked as answer by JJChen Tuesday, August 4, 2015 10:03 PM
    Monday, August 3, 2015 8:08 PM