Search code examples
wpf

Can I share DataGrid.Columns amongst DataGrid tables


I have 3 datagrids that share the same data type. I'd like to configure the column binding once and have the 3 datagrids share the resource.

e.g.

<DataGrid Grid.Row="1" x:Name="primaryDG" ItemsSource="{Binding Path=dgSource AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Width="Auto" Header="Column 1" Binding="{Binding Path=Col1}"/>
        <DataGridTextColumn Width="Auto" Header="Column 2" Binding="{Binding Path=Col2}"/>
        <DataGridTextColumn Width="Auto" Header="Column 3" Binding="{Binding Path=Col3}"/>
        <DataGridTextColumn Width="Auto" Header="Column 4" Binding="{Binding Path=Col4}"/>
    </DataGrid.Columns>
</DataGrid>

Is there a way to set the ItemsSource for each DataGrid, then use a datatemplate or controltemplate to get the columns?


Solution

  • Yes... two ways. One you can simply add a style for DataGrid that sets the columns like this...

    <Style x:Key="MyColumnDefsStyle" x:Shared="True" TargetType="DataGrid">
        <Setter Property="Columns">
            <Setter.Value>
                 <DataGridTextColumn Width="Auto" Header="Column 1" Binding="{Binding Path=Col1}"/>
                 <DataGridTextColumn Width="Auto" Header="Column 2" Binding="{Binding Path=Col2}"/>
                 <DataGridTextColumn Width="Auto" Header="Column 3" Binding="{Binding Path=Col3}"/>
                 <DataGridTextColumn Width="Auto" Header="Column 4" Binding="{Binding Path=Col4}"/>
            </Setter.Value>
        </Setter>
    </Style>
    
    <DataGrid Style="{StaticResource MyColumnDefsStyle}" ItemsSource="{Binding Foo1}" />
    <DataGrid Style="{StaticResource MyColumnDefsStyle}" ItemsSource="{Binding Foo2}" />
    <DataGrid Style="{StaticResource MyColumnDefsStyle}" ItemsSource="{Binding Foo3}" />
    

    That works but represents a problem if you are applying it to multiple grids that themselves may already be using a style.

    In that case, the other, more flexible way works better. This however requires creating a XAML-friendly classes to represent an ObservableCollection<DataGridColumn> (although you technically only said columns, I like to be complete myself so I'd also do one for the rows) Then add them in a place you can reference in the XAML namespaces. (I call mine xmlns:dge for 'DataGridEnhancements') You then use it like this:

    In the code somwhere (I'd make it accessible app-wide)...

    public class DataGridRowsCollection : ObservableCollection<DataGridRow>{}
    public class DataGridColumnsCollection : ObservableCollection<DataGridColumn>{}
    

    Then in the resources...

    <dge:DataGridColumnsCollection x:Key="MyColumnDefs" x:Shared="True">
        <DataGridTextColumn Width="Auto" Header="Column 1" Binding="{Binding Path=Col1}"/>
        <DataGridTextColumn Width="Auto" Header="Column 2" Binding="{Binding Path=Col2}"/>
        <DataGridTextColumn Width="Auto" Header="Column 3" Binding="{Binding Path=Col3}"/>
        <DataGridTextColumn Width="Auto" Header="Column 4" Binding="{Binding Path=Col4}"/>
    </dge:DataGridColumnsCollection>
    

    And finally in the XAML...

    <DataGrid Columns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo1}" />
    <DataGrid Columns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo2}" />
    <DataGrid Columns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo3}" />
    

    HTH,

    Mark

    EDIT: Since you cannot set the DataGrid.Columns property, you need to enhance your DataGridView (as mentioned in the comments). Here is the code for an EnhancedDataGrid:

    public class EnhancedDataGrid : DataGrid
        {
            //the dependency property for 'setting' our columns
            public static DependencyProperty SetColumnsProperty = DependencyProperty.Register(
                "SetColumns",
                typeof (ObservableCollection<DataGridColumn>),
                typeof (EnhancedDataGrid),
                new FrameworkPropertyMetadata
                {
                    DefaultValue = new ObservableCollection<DataGridColumn>(),
                    PropertyChangedCallback = EnhancedDataGrid.SetColumnsChanged,
                    AffectsRender = true,
                    AffectsMeasure = true,
                    AffectsParentMeasure = true,
                    IsAnimationProhibited = true,
                    DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                });
    
            //callback to reset the columns when our dependency property changes
            private static void SetColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var datagrid = (DataGrid) d;
    
                datagrid.Columns.Clear();
                foreach (var column in (ObservableCollection<DataGridColumn>)e.NewValue)
                {
                    datagrid.Columns.Add(column);
                }
            }
    
            //The dependency property wrapper (so that you can consume it inside your xaml)
            public ObservableCollection<DataGridColumn> SetColumns
            {
                get { return (ObservableCollection<DataGridColumn>) this.GetValue(EnhancedDataGrid.SetColumnsProperty); }
                set { this.SetValue(EnhancedDataGrid.SetColumnsProperty, value); }
            } 
        }
    

    Now you could set the columns with the SetColumns dependency property created in your CustomControl:

    <custom:EnhancedDataGrid SetColumns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo1}" />
    <custom:EnhancedDataGrid SetColumns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo2}" />
    <custom:EnhancedDataGrid SetColumns="{StaticResource MyColumnDefs}" ItemsSource="{Binding Foo3}" />