【问题标题】:WPF DataGrid Column Changed Event. How to Detect Column Resizing?WPF DataGrid 列更改事件。如何检测列调整大小?
【发布时间】:2021-12-08 04:49:56
【问题描述】:

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

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

就我而言,我不希望这种行为。 滚动条不应该粘在右边, 或者,完美的是,像 MS Excel 这样的调整大小预览。

谁能告诉我如何做到这一点?

编辑1: 这种行为很好(不坚持右边):

我不喜欢的是:

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

/编辑1

我将 .Net 4.8 用于一个简单的 WPF 应用程序。

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

<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>

【问题讨论】:

  • 如果你知道你有很多列,你应该明确启用水平滚动条来提高渲染性能。你想要的行为不清楚。如果您想在视图中保留调整大小列的左侧列,您可以轻松地监听 DataGrid 的 SizeChanged 事件。然后在事件处理程序中获取上一列并将其滚动到视图中。
  • 也许这样更清楚:滚动条的行为在到达右端后发生变化,我开始调整大小。滚动条然后粘在右边。因此,如果我调整最后一列的大小,我很容易因此而失去其他列的视图。也许我应该添加图片来说明我的意思。无论如何,我不希望这种行为改变。
  • @BionicCode 我试图监听该事件,但它只在打开窗口时触发一次,而不是在开始时或调整列大小期间或之后。

标签: c# .net wpf datagrid horizontal-scrolling


【解决方案1】:

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

既然对列感兴趣,当然要听单元格事件:

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();

这是一个替代版本,展示了如何使用ScrollViewerDataGrid 获得对滚动位置的更多控制:

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;
}

【讨论】:

  • 啊,好吧,我误会了。谢谢你的建议。我测试了你的它,不得不说它“只”改善了一点行为。对于第一列,如果调整大小的列不在视野范围内,它会停止滚动条。由于最后一列永远不会消失,所以它是一样的。我希望滚动条永远不会自行向右移动。
  • 无法理解输入“只能稍微改善行为” - 信息不足。此示例旨在向您展示如何对不断变化的列大小做出反应。这是将调整大小列保留在视图中的最简单方法。您可以微调行为,例如,如果滚动位置对您来说不是最佳的,例如通过调整滚动位置来补偿元素 Padding 和 Margin。您可以提取 ScrollViewer 以完全控制滚动行为并手动滚动到计算的位置,而不是调用 BringIntoView()。
  • @Luy 我添加了一个快速的第二个解决方案,它向您展示如何使用 ScrollViewer 来保持列/当前视图的位置。它比使用 BringIntoView() 更准确。它应该解决由 BringIntoView 方法的非常原始的定位行为引入的任何问题。
  • 好的,添加确实是我正在寻找的东西。只需将小类型-o“VerticalOffset”更改为“Horizo​​ntalOffset”。非常感谢,标记为答案。
  • 感谢您的反馈。我已经修好了。
猜你喜欢
  • 2011-04-30
  • 2011-10-20
  • 2011-11-20
  • 2016-01-22
  • 2011-08-20
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多