WPF DataGrid Sync Column Widths
Asked Answered
E

6

13

I've got two WPF Toolkit DataGrids, I'd like so that when the user resizes the first column in the first grid, it resizes the first column in the second grid. I've tried binding the width of the DataGridColumn in the second grid to the appropriate column in the first grid, but it doesn't work. I'd prefer to use all xaml, but I'm fine with using code behind as well.

<tk:DataGrid Width="100" Height="100">
    <tk:DataGrid.Columns>
        <tk:DataGridTextColumn x:Name="Column1" Width="50"/>
    </tk:DataGrid.Columns>
</tk:DataGrid>
<tk:DataGrid Width="100" Height="100">
    <tk:DataGrid.Columns>
        <tk:DataGridTextColumn x:Name="Column1Copy" Width="{Binding Path=ActualWidth, ElementName=Column1}"/>
    </tk:DataGrid.Columns>
</tk:DataGrid>

I also tried binding to Width instead of ActualWidth, but neither works.

Any help is greatly appreciated.

Ebonieebonite answered 5/1, 2009 at 20:0 Comment(0)
E
14

Well, I don't think that it is possible using straight XAML, but I still feel like it should because DataGridColumn does derive from DependencyObject. I did find a way to do it programatically though. I'm not thrilled about it, but it works:

DataGridColumn.WidthProperty.AddValueChanged(upperCol, delegate
{
    if (changing) return;
    changing = true;
    mainCol.Width = upperCol.Width;
    changing = false;
});
DataGridColumn.WidthProperty.AddValueChanged(mainCol, delegate 
{ 
    if (changing) return;
    changing = true;
    upperCol.Width = mainCol.Width; 
    changing = false; 
});

public static void AddValueChanged(this DependencyProperty property, object sourceObject, EventHandler handler)
{
    DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(property, property.OwnerType);
    dpd.AddValueChanged(sourceObject, handler);
}
Ebonieebonite answered 12/1, 2009 at 16:58 Comment(2)
I'm sorry but I don't get your code. What are the two first definitions? Methods? They don't look like a method and the parameters have no type ...Sharitasharity
@SoMoS - DataGridColumn.WidthProperty is a dependency property. AddValueChanged is an extension method on a dependency property, the arguments it needs is a source object and an event handler. The delegate you see in the first two definitions is an anonymous method that handles an event.Ebonieebonite
S
6

You can use the DataGrid LayoutUpdated method to manipulate other objects regarding the column widths.

private void dataGrid1_LayoutUpdated(object sender, EventArgs e)
{
    for(int i = 0 ; i < dataGrid1.Columns.Count && i < dataGrid2.Columns.Count ; ++i)
        dataGrid2.Columns[i].Width = dataGrid1.Columns[i].ActualWidth;
}
Sello answered 27/11, 2011 at 4:10 Comment(0)
D
2

I did a quick solution to this to using a attached behavior, Inspired by Ahmed answer above.

public class DataGridWidthSyncronizerBehavior
{
    public static readonly DependencyProperty SyncronizeWidthWithProperty =
        DependencyProperty.RegisterAttached("SyncronizeWidthWith",
            typeof(DataGrid),
            typeof(DataGridWidthSyncronizerBehavior),
            new UIPropertyMetadata(null, SyncronizeWidthWithChanged));

    public static void SetSyncronizeWidthWith(DependencyObject target, DataGrid value)
    {
        target.SetValue(SyncronizeWidthWithProperty, value);
    }

    public static DataGrid GetSyncronizeWidthWith(DependencyObject target)
    {
        return (DataGrid)target.GetValue(SyncronizeWidthWithProperty);
    }

    private static void SyncronizeWidthWithChanged(DependencyObject obj, DependencyPropertyChangedEventArgs dpargs)
    {
        if (!(obj is DataGrid sourceDataGrid))
            return;

        if (!(sourceDataGrid.GetValue(SyncronizeWidthWithProperty) is DataGrid targetDataGrid))
            return;

        void Handler(object sender, EventArgs e)
        {
            for (var i = 0; i < sourceDataGrid.Columns.Count && i < targetDataGrid.Columns.Count; ++i)
                targetDataGrid.Columns[i].Width = sourceDataGrid.Columns[i].ActualWidth;
        }

        sourceDataGrid.LayoutUpdated -= Handler;
        sourceDataGrid.LayoutUpdated += Handler;
    }
}

XAML:

<DataGrid local:DataGridWidthSyncronizerBehavior.SyncronizeWidthWith="{Binding ElementName=SyncronizedHeaderGrid}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Header 1" 
                            Binding="{Binding Item1}" />
        <DataGridTextColumn Header="Header 2" 
                            Binding="{Binding Item2}"/>
        <DataGridTextColumn Header="Header 3" 
                            Binding="{Binding Item3}"/>
    </DataGrid.Columns>
</DataGrid>

<DataGrid x:Name="SyncronizedHeaderGrid">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Header 1" 
                            Binding="{Binding Item1}" />
        <DataGridTextColumn Header="Header 2" 
                            Binding="{Binding Item2}"/>
        <DataGridTextColumn Header="Header 3" 
                            Binding="{Binding Item3}"/>
    </DataGrid.Columns>
</DataGrid>

The Second DataGrids, header and cell width, is now syncronized with the first grid header width.

Disrupt answered 8/8, 2018 at 10:42 Comment(0)
R
1

I tried this:

<tk:DataGrid Width="100" Height="100" x:Name="Grid1" Grid.Column="0">
   <tk:DataGrid.Columns>
      <tk:DataGridTextColumn x:Name="Column1" Width="50"/>
   </tk:DataGrid.Columns>
</tk:DataGrid>
<tk:DataGrid Width="100" Height="100" x:Name="Grid2" Grid.Column="1">
   <tk:DataGrid.Columns>
     <tk:DataGridTextColumn x:Name="Column1Copy" Width="{Binding Mode=TwoWay, Path=Columns[0].ActualWidth, ElementName=Grid1}"/>
     </tk:DataGrid.Columns>
</tk:DataGrid>

However, It looks like since DataGridColumns do not derive from FrameworkElement but instead derive from DependencyObject, binding in this manner is not available.

Rinker answered 5/1, 2009 at 23:19 Comment(0)
M
1

If you want to bind column Width property in XAML, in 2 DataGrid's than you have to do the following.

In the first DataGrid name the DataGridTextColumn:

<DataGrid>
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Col1"/>
    </DataGrid.Columns>
</DataGrid>

In the second DataGrid add a DiscreteObjectKeyFrame pointing to the above mentioned column as a resource, and use the following Binding to Width property on the DataGridTextColumn you want to "link":

<DataGrid>
    <DataGrid.Resources>
        <DiscreteObjectKeyFrame x:Key="proxyCol1" Value="{Binding ElementName=Col1}"/>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn  Width="{Binding Path=Value.Width, Mode=TwoWay, Source={StaticResource proxyCol1}}"/>
    </DataGrid.Columns>
</DataGrid>
Maisey answered 21/12, 2017 at 8:55 Comment(0)
D
-2

I found a solution to this problem, and an extra cool solution :-) You can download the WPF toolkit and get the code of the DataGrid. Once you have the code all you have to do is change the DataGridColumn class to inherit FrameworkElement instead of DependencyObject. Once you do so - you are left with only one problem, the DataContext of the column would not be initialized since the column is not part of the logical tree, adding it to the logical tree would solve this. You can do it like this: Where the OnColumnInitialization is: private void OnColumnInitialization(object sender, EventArgs e) {
AddLogicalChild(sender); } Now that it is part of the logical tree you have the same data context and you can use binding on the Width property. If all are bound to the same Width - you have a complete sync of the Width of your columns. This worked for me :-) Gili

Dhiman answered 16/6, 2011 at 23:12 Comment(1)
Fixing a bug in framework code doesn't seem like an "extra cool solution" to me. It seems more like a huge pain.Dissension

© 2022 - 2024 — McMap. All rights reserved.