locked
How to create DataGridTextColumn of DataGrid dynamically and bind it? RRS feed

  • Question

  • I have two properties:

    • public ObservableCollection<Object> HeaderOfDataGrid {get; set;}
    • public Observablecollection<Object> BodyOfDataGrid {get; set;}

    Is it possible to bind HeaderOfDataGrid property to DataGridTextColumn? Yeah, I know that it is possible by binding like that:

    <DataGrid.Columns>
       <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
       <DataGridTextColumn Header="Surname" Binding="{Binding Surname}"/>
       <DataGridTextColumn Header="Phone" Binding="{Binding Phone_Number}" />
    </DataGrid.Columns>

    However, HeaderOfDataGrid property can have 'Count' equals 700 and I need to show all 700 items of collection HeaderOfDataGrid in DataGridTextColumns of DataGrid. So, I need to create 700 DataGridTextColumns based on HeaderOfDataGrid property(collection). Also, if Count of HeaderOfDataGrid property is 700, then BodyOfDataGrid property also has the same Count(for example, 700). So using hard-coded xaml is not convenient to me.

    I know I can bind this way:

    <DataGrid ItemsSource="{Binding Path=Body, Mode=TwoWay}"/>

    But it is not what I want as I would like to renamed headers located in Header property.

    How to implement binding and creating without hard-coded xaml to DataGridTextColumn?



    Wednesday, November 25, 2015 1:26 PM

Answers

  • >>Is it possible to bind HeaderOfDataGrid property to DataGridTextColumn?

    Yes, you could bind the Header of the column to a value in the ObservableCollection by setting the Header property to a TextBlock like this:

                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Name}">
                        <DataGridTextColumn.Header>
                            <TextBlock Text="{Binding Path=DataContext.HeaderOfDataGrid[0],RelativeSource={RelativeSource AncestorType=Window}}"/>
                        </DataGridTextColumn.Header>
                    </DataGridTextColumn>
                </DataGrid.Columns>


    The following does not work though because a DataGridTextColumn is not part of the visual tree (but a TextBlock is):

    <DataGridTextColumn Header="{Binding Path=DataContext.HeaderOfDataGrid[0],RelativeSource={RelativeSource AncestorType=Window}}" Binding="{Binding}"/>


    >>So using hard-coded xaml is not convenient to me.

    If you don't want to hardcode the columns in your XAML markup, you could create the columns programmatically or auto-generate them and just set the Header property programmatically, e.g.:

    <DataGrid ItemsSource="{Binding Path=Body, Mode=TwoWay}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" />


            int index = 0;
            private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
            {
                e.Column.Header = (this.DataContext as ViewModel).HeaderOfDataGrid[index++];
            }

    Just make sure that the HeaderOfDataGrid collection contains a header value for each property/column that gets generated.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by NiceStepUp Wednesday, November 25, 2015 4:33 PM
    • Unmarked as answer by NiceStepUp Wednesday, November 25, 2015 4:42 PM
    • Marked as answer by NiceStepUp Wednesday, November 25, 2015 4:42 PM
    Wednesday, November 25, 2015 3:40 PM

All replies

  • Columns only has a getter, so you can't bind it.

    The way I do dynamic datagrids is to build xaml as a string and then xamlreader.parse it into actual objects.

    You can do that column by column, iterating through whatever collection or collections you like.

    This is an extremely powerful technique because you're dynamically building xaml like you would usually type into markup.

    You can manipulate this as xml and start with various templates you set attribute or add nodes to.

    The technique is explained generally in the links to this article:

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

    And an datagrid (specifically )  is built in this one:

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

    Having said that.

    If all you want to do is define a header then you could bind to an ObservableCollection<t> Where t is your type with these 300 columns.

    Then apply an attribute to use as the displayname. I think that might be automagically picked up by column generation. If not you could handle the Autogeneratingcolumn event

    <DataGrid AutoGeneratingColumn="dg_AutoGeneratingColumn">

    That has an eventargs:

    https://msdn.microsoft.com/en-us/library/system.windows.controls.datagridautogeneratingcolumneventargs(v=vs.110).aspx

    Which has a property descriptor which you could get an attribute ( or many attributes ) from.

    I think you can dynamically add attributes so you could maybe even use that to drive which properties you want to include.


    Hope that helps.

    Technet articles: WPF: MVVM Step 1; All my Technet Articles

    Wednesday, November 25, 2015 1:50 PM
  • >>Is it possible to bind HeaderOfDataGrid property to DataGridTextColumn?

    Yes, you could bind the Header of the column to a value in the ObservableCollection by setting the Header property to a TextBlock like this:

                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Name}">
                        <DataGridTextColumn.Header>
                            <TextBlock Text="{Binding Path=DataContext.HeaderOfDataGrid[0],RelativeSource={RelativeSource AncestorType=Window}}"/>
                        </DataGridTextColumn.Header>
                    </DataGridTextColumn>
                </DataGrid.Columns>


    The following does not work though because a DataGridTextColumn is not part of the visual tree (but a TextBlock is):

    <DataGridTextColumn Header="{Binding Path=DataContext.HeaderOfDataGrid[0],RelativeSource={RelativeSource AncestorType=Window}}" Binding="{Binding}"/>


    >>So using hard-coded xaml is not convenient to me.

    If you don't want to hardcode the columns in your XAML markup, you could create the columns programmatically or auto-generate them and just set the Header property programmatically, e.g.:

    <DataGrid ItemsSource="{Binding Path=Body, Mode=TwoWay}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" />


            int index = 0;
            private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
            {
                e.Column.Header = (this.DataContext as ViewModel).HeaderOfDataGrid[index++];
            }

    Just make sure that the HeaderOfDataGrid collection contains a header value for each property/column that gets generated.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by NiceStepUp Wednesday, November 25, 2015 4:33 PM
    • Unmarked as answer by NiceStepUp Wednesday, November 25, 2015 4:42 PM
    • Marked as answer by NiceStepUp Wednesday, November 25, 2015 4:42 PM
    Wednesday, November 25, 2015 3:40 PM