9
votes

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?

3
similar question, useful answers: stackoverflow.com/questions/5716123/…surfen

3 Answers

5
votes

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}" />
2
votes

This answer is based on MarquelV's solution. In his answer (and in the comments) he mentions a Custom Control named EnhancedDataGrid, where he provides logic to set the DataGrid.Columns property. 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}" />
1
votes

You can create a custom control just to wrap the data grid and pass data to it. The control will set the data on a grid.