Home > Back-end >  WPF DataGrid horizontal scrollbar snpas right on column resize
WPF DataGrid horizontal scrollbar snpas right on column resize

Time:10-23

I have a problem with the default behavior of the resizing of columns:

If a DataGrid is too wide for its container, the horizontal scrollbar appears. If I drag the bar to the right and resize most right column, the scrollbar sticks to the right.

In my case I don't want that behavior. The scrollbar should either just not stick to the right, or, perfect would be, a resize preview like MS Excel.

Can someone tell me how to achieve that?

Edit1: This behavior is fine (not sticking to the right): Regular scrollbar behavior

What I don't like is this: Stick/snap to the right

If I could realize that easily, I would prefer: Excel behavior

/Edit1

I am using .Net 4.8 for a simple WPF application.

If an example is need, the following will display two grids and the left one can be used for that behavior:

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

CodePudding user response:

When I suggested to listen to the SizeChanged event I didn't mean the DataGrid to be the source.

Since you are interested in the columns, you must listen to the cell event of course:

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

This is an alternative version that shows how to gain more control over the scroll position using the ScrollViewer of the DataGrid:

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