WPF DataGrid 同步列宽
Posted
技术标签:
【中文标题】WPF DataGrid 同步列宽【英文标题】:WPF DataGrid Sync Column Widths 【发布时间】:2010-09-29 16:23:09 【问题描述】:我有两个 WPF 工具包DataGrids
,我希望当用户调整第一个网格中第一列的大小时,它会调整第二个网格中第一列的大小。我尝试将第二个网格中DataGridColumn
的宽度绑定到第一个网格中的相应列,但它不起作用。我更喜欢使用所有 xaml,但我也可以使用后面的代码。
<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>
我也尝试绑定到Width
而不是ActualWidth
,但都不起作用。
非常感谢任何帮助。
【问题讨论】:
【参考方案1】:好吧,我认为直接使用 XAML 是不可能的,但我仍然觉得应该这样做,因为 DataGridColumn
确实派生自 DependencyObject
。不过,我确实找到了一种以编程方式进行的方法。我对此并不感到兴奋,但它确实有效:
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);
【讨论】:
很抱歉,我没有收到您的代码。前两个定义是什么?方法?它们看起来不像方法,而且参数没有类型... @SoMoS - DataGridColumn.WidthProperty 是一个依赖属性。 AddValueChanged 是依赖属性的扩展方法,它需要的参数是源对象和事件处理程序。您在前两个定义中看到的委托是处理事件的匿名方法。【参考方案2】:您可以使用DataGrid
LayoutUpdated
方法来操作其他对象的列宽。
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;
【讨论】:
【参考方案3】:受上述艾哈迈德回答的启发,我使用附加行为快速解决了这个问题。
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>
第二个 DataGrids,标题和单元格宽度,现在与第一个网格标题宽度同步。
【讨论】:
【参考方案4】:我试过了:
<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>
但是,看起来由于DataGridColumn
s 不是从FrameworkElement
派生,而是从DependencyObject
派生,因此这种方式的绑定不可用。
【讨论】:
【参考方案5】:如果你想在 XAML 中绑定列 Width 属性,在 2 个DataGrid
中,你必须执行以下操作。
在第一个DataGrid
中命名DataGridTextColumn
:
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn x:Name="Col1"/>
</DataGrid.Columns>
</DataGrid>
在第二个DataGrid
中添加一个指向上述列的DiscreteObjectKeyFrame
作为资源,并在要“链接”的DataGridTextColumn
上使用以下Binding
到Width
属性:
<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>
【讨论】:
【参考方案6】:我找到了解决这个问题的方法,还有一个很酷的解决方案 :-) 您可以下载 WPF 工具包并获取 DataGrid 的代码。 获得代码后,您只需将 DataGridColumn 类更改为继承 FrameworkElement 而不是 DependencyObject。 一旦你这样做了 - 你只剩下一个问题,列的 DataContext 将不会被初始化,因为该列不是逻辑树的一部分,将它添加到逻辑树将解决这个问题。 你可以这样做: OnColumnInitialization 在哪里: 私有无效 OnColumnInitialization(对象发送者,EventArgs e) AddLogicalChild(发件人); 现在它是逻辑树的一部分,您拥有相同的数据上下文,您可以在 宽度属性。 如果所有都绑定到相同的宽度 - 你有你的列的宽度的完整同步。 这对我有用:-) 吉利
【讨论】:
修复框架代码中的错误对我来说似乎不是一个“超酷的解决方案”。这似乎更像是一种巨大的痛苦。以上是关于WPF DataGrid 同步列宽的主要内容,如果未能解决你的问题,请参考以下文章