WPF DataGrid 列更改事件。如何检测列大小调整?

WPF DataGrid Column Changed Event. How to Detect Column Resizing?

我对调整列大小的默认行为有疑问:

如果 DataGrid 对其容器而言太宽,则会出现水平滚动条。 如果我将栏向右拖动并调整最右侧列的大小,滚动条会保持在右侧。

就我而言,我不希望出现这种行为。 滚动条要么不粘在右边, 或者,完美的是,像 MS Excel.

这样的调整大小预览

有人能告诉我如何实现吗?

编辑1: 这种行为很好(不粘在右边):

我不喜欢的是:

如果我能很容易地意识到这一点,我宁愿:

/编辑1

我正在为一个简单的 WPF 应用程序使用 .Net 4.8。

如果需要示例,下面将显示两个网格,左侧的网格可用于该行为:

<Window x:Class="DataGridTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataGridTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MasterViewModel/>
    </Window.DataContext>

    <DockPanel>
        <Button DockPanel.Dock="Bottom" Command="{Binding DisplaySelectionCountCommand}">Display Selection Count</Button>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            
            <DataGrid Grid.Column="0" ItemsSource="{Binding Items}" AutoGenerateColumns="False"
                 SelectionMode="Extended" local:MultiSelect.IsEnabled="True" HorizontalScrollBarVisibility="Auto">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100"/>
                </DataGrid.Columns>
            </DataGrid>
            <DataGrid Grid.Column="1" ItemsSource="{Binding Items}"
                  SelectionMode="Extended" local:MultiSelect.IsEnabled="True"/>
        </Grid>
    </DockPanel>
</Window>

当我建议收听 SizeChanged 事件时,我并不是说 DataGrid 是来源。

既然对栏目感兴趣,那当然要监听cell事件了:

MainWindow.xaml

<DataGrid>
  <DataGrid.CellStyle>
    <Style TargetType="DataGridCell">
      <EventSetter Event="SizeChanged" Handler="DataGridCell_SizeChanged" />
    </Style>
  </DataGrid.CellStyle>
</DataGrid>

MainWindow.xaml.cs

private void DataGridCell_SizeChanged(object sender, SizeChangedEventArgs e)
  => (sender as DataGridCell).BringIntoView();

这是一个替代版本,展示了如何使用 DataGridScrollViewer 来更好地控制滚动位置:

MainWindow.xaml

<DataGrid x:Name="DataGrid" 
          ScrollViewer.ScrollChanged="DataGrid_ScrollChanged">
  <DataGrid.CellStyle>
    <Style TargetType="DataGridCell">
      <EventSetter Event="SizeChanged" Handler="DataGridCell_SizeChanged" />
    </Style>
  </DataGrid.CellStyle>
</DataGrid>

MainWindow.xaml.cs

private double OriginalScrollPosition { get; set; }
private bool IsResizingColumn { get; set; }

private void DataGrid_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
  var dataGrid = sender as DataGrid;
  if (this.IsResizingColumn 
    && TryFindVisualChildElement(dataGrid, out ScrollViewer scrollViewer))
  {
    this.Dispatcher.InvokeAsync(() =>
    {
      scrollViewer.ScrollToHorizontalOffset(this.OriginalScrollPosition);
      this.IsResizingColumn = false;
    }, DispatcherPriority.Background);
  }
}

private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
  var dataGridCell = sender as DataGridCell;
  if (TryFindVisualChildElement(this.DataGrid, out ScrollViewer scrollViewer))
  {
    this.OriginalScrollPosition = scrollViewer.HorizontalOffset;
    this.IsResizingColumn = true;
  }
}

private bool TryFindVisualChildElement<TChild>(DependencyObject parent, out TChild resultElement)
  where TChild : DependencyObject
{
  resultElement = null;

  if (parent is Popup popup)
  {
    parent = popup.Child;
    if (parent == null)
    {
      return false;
    }
  }

  for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
  {
    DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);

    if (childElement is TChild child)
    {
      resultElement = child;
      return true;
    }

    if (TryFindVisualChildElement(childElement, out resultElement))
    {
      return true;
    }
  }

  return false;
}