Search code examples
c#wpfdata-bindingdatagrid

How to dynamically create a datagrid in WPF?


I have the following datagrid in XAML:

<DataGrid ItemsSource="{Binding View}" AutoGenerateColumns="False" IsReadOnly="True"
          GridLinesVisibility="None" CanUserAddRows="False" CanUserDeleteRows="False" 
          CanUserResizeColumns="False" CanUserResizeRows="False" 
          CanUserReorderColumns="False" >
    <DataGrid.ColumnHeaderStyle>
        <Style TargetType="{x:Type DataGridColumnHeader}">
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="FontSize" Value="12" />
        </Style>
    </DataGrid.ColumnHeaderStyle>
    <DataGrid.Columns>
        <DataGridTextColumn Header="Type" Width="200" FontSize="12" 
                            Binding="{Binding Path=Name}" />
        <DataGridTemplateColumn Header="Ingredients" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <DataGrid ItemsSource="{Binding Ingredients}" 
                            AutoGenerateColumns="False" IsReadOnly="True"
                            GridLinesVisibility="None" CanUserAddRows="False" 
                            CanUserDeleteRows="False" CanUserResizeColumns="False"
                            CanUserResizeRows="False" CanUserReorderColumns="False" >
                        <DataGrid.ColumnHeaderStyle>
                            <Style TargetType="{x:Type DataGridColumnHeader}">
                                <Setter Property="FontWeight" Value="Bold" />
                                <Setter Property="FontSize" Value="12" />
                            </Style>
                        </DataGrid.ColumnHeaderStyle>
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="Ingredients" 
                                            Width="*" FontSize="12"
                                            Binding="{Binding Path=IngredientName}"/>
                            <DataGridTextColumn Header="Quantite" Width="*" 
                                          FontSize="12" Binding="{Binding Path=Qty}"/>
                        </DataGrid.Columns>
                    </DataGrid>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

I am trying to find a way to create the datagrid dynamically (in-code) so that I can create multiple copies of it and bind it to different datasources at run-time.

Is this possible? Anyone know how I could go about it for a datagrid complicated like this?


Solution

  • First, move as much as possible of the different settings out into reusable Styles and DataTemplates, leaving very little in the DataGrid itself:

    <UserControl ... >
      <UserControl.Resources>
      <Style x:Key="GridHeaderStyle" 
             TargetType="{x:Type DataGridColumnHeader}">
         <Setter Property="FontWeight" Value="Bold" />
         <Setter Property="FontSize" Value="12" />
      </Style>
      <Style x:Key="ReadOnlyGridStyle" TargetType="{x:Type DataGrid}" >
          <Setter Property="AutoGenerateColumns" Value="False" />
          <Setter Property="IsReadOnly" Value="True" />
          <Setter Property="GridLinesVisibility" Value="None" />
          <Setter Property="CanUserAddRows" Value="False" />
          <Setter Property="CanUserDeleteRows" Value="False" />
          <Setter Property="CanUserResizeColumns" Value="False" />
          <Setter Property="CanUserResizeRows" Value="False" />
          <Setter Property="CanUserReorderColumns" Value="False" />
          <Setter Property="ColumnHeaderStyle" 
                  Value="{StaticResource GridHeaderStyle}" />
      </Style>
    
      <DataTemplate x:Key="IngredientsCellTemplate">
        <DataGrid ItemsSource="{Binding Ingredients}" 
                   Style="{StaticResource ReadOnlyGridStyle}">
         <DataGrid.Columns>
           <DataGridTextColumn Header="Ingredients" Width="*" 
                             FontSize="12"
                   Binding="{Binding Path=IngredientName}" />
           <DataGridTextColumn Header="Quantite" Width="*" 
                             FontSize="12"
                   Binding="{Binding Path=Qty}" />
         </DataGrid.Columns>
        </DataGrid>
      </DataTemplate>
    </UserControl.Resources>
    
     <!-- A DataGrid using our Styles: -->
     <DataGrid ItemsSource="{Binding View}" 
              Style="{StaticResource ReadOnlyGridStyle}" >
      <DataGrid.Columns>
         <DataGridTextColumn Header="Type" 
           Width="200" FontSize="12"
           Binding="{Binding Path=Name}" />
         <DataGridTemplateColumn Header="Ingredients" 
           Width="*"
           CellTemplate="{StaticResource IngredientsCellTemplate}" />
      </DataGrid.Columns>
     </DataGrid>
    </UserControl>
    

    Then it gets a lot easier to create new DataGrids in your code-behind, using the existing Styles:

    var datagrid = new DataGrid();
    datagrid.Style = FindResource("ReadOnlyGridStyle") as Style;
    
    datagrid.Columns.Add(new DataGridTextColumn()
    {
        Header = "Type",
        Width = new DataGridLength(200),
        FontSize = 12,
        Binding = new Binding("Name")
    });
    datagrid.Columns.Add(new DataGridTemplateColumn()
    {
        Header = "Ingredients",
        Width = new DataGridLength(1, DataGridLengthUnitType.Star),
        CellTemplate = FindResource("IngredientsCellTemplate") as DataTemplate
    });
    
    datagrid.ItemsSource = ...