Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataGrid: reduce column width to fit its content while scrolling

When I scroll vertical scrollbar, DataGrid automatically expands column width if content in new visible rows is bigger and exceeds previous column width. It's OK.

But if all bigger rows are scrolled over and new visible ones have small content width, DataGrid does not decrease column width. Is there a way to archieve this?

Attached behaviour implementation will be great.

Code behing:

 public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            var persons = new List<Person>();
            for (var i = 0; i < 20; i++)
                persons.Add(new Person() {Name = "Coooooooooooooool", Surname = "Super"});
            for (var i = 0; i < 20; i++)
                persons.Add(new Person() {Name = "Cool", Surname = "Suuuuuuuuuuuuuuper"});
            for (var i = 0; i < 20; i++)
                persons.Add(new Person() {Name = "Coooooooooooooool", Surname = "Super"});
            DG.ItemsSource = persons;
        }

        public class Person
        {
            public string Name { get; set; }
            public string Surname { get; set; }
        }
    }

XAML:

<Window
    x:Class="WpfApp4.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"
    Title="MainWindow"
    Width="400"
    Height="200"
    mc:Ignorable="d">
    <Grid>
        <DataGrid
            x:Name="DG"
            CanUserAddRows="False"
            CanUserDeleteRows="False"
            CanUserReorderColumns="False"
            CanUserResizeColumns="False"
            CanUserResizeRows="False"
            CanUserSortColumns="False" />
    </Grid>
</Window>
like image 649
AsValeO Avatar asked Dec 09 '18 13:12

AsValeO


1 Answers

Add the LoadingRow property to you Datagrid:

   <DataGrid x:Name="DG"
        CanUserAddRows="False"
        CanUserDeleteRows="False"
        CanUserReorderColumns="False"
        CanUserResizeColumns="False"
        CanUserResizeRows="False"
        CanUserSortColumns="False" LoadingRow="DG_LoadingRow">
    </DataGrid>

And then add this code in the code behind:

private void DG_LoadingRow(object sender, DataGridRowEventArgs e)
    {
        foreach (DataGridColumn c in DG.Columns)
            c.Width = 0;

        DG.UpdateLayout();

        foreach (DataGridColumn c in DG.Columns)
            c.Width = DataGridLength.Auto;
    }

It's definitely not the cleanest solution but it will resize the columns that are in view while scrolling.

Hope this helps.


Can you please wrap this into attached behaviour?

1) First option is to use an Attached Property.

public class DataGridHelper : DependencyObject
{
    public static readonly DependencyProperty SyncedColumnWidthsProperty =
        DependencyProperty.RegisterAttached(
          "SyncedColumnWidths",
          typeof(Boolean),
          typeof(DataGridHelper),
          new FrameworkPropertyMetadata(false,
              FrameworkPropertyMetadataOptions.AffectsRender,
              new PropertyChangedCallback(OnSyncColumnsChanged)
          ));

    private static void OnSyncColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is DataGrid dataGrid)
        {
            dataGrid.LoadingRow += SyncColumnWidths;
        }
    }

    private static void SyncColumnWidths(object sender, DataGridRowEventArgs e)
    {
        var dataGrid = (DataGrid)sender;

        foreach (DataGridColumn c in dataGrid.Columns)
            c.Width = 0;

        e.Row.UpdateLayout();

        foreach (DataGridColumn c in dataGrid.Columns)
            c.Width = DataGridLength.Auto;
    }

    public static void SetSyncedColumnWidths(UIElement element, Boolean value)
    {
        element.SetValue(SyncedColumnWidthsProperty, value);
    }
}

Usage

<DataGrid
    ext:DataGridHelper.SyncedColumnWidths="True"
    ... />

2) Alternatively, Behaviors provide a more encapsulated way to extend functionality (requires System.Windows.Interactivity).

using System.Windows.Interactivity;

...

    public class SyncedColumnWidthsBehavior : Behavior<DataGrid>
    {
        protected override void OnAttached()
        {
            this.AssociatedObject.LoadingRow += this.SyncColumnWidths;
        }

        protected override void OnDetaching()
        {
            this.AssociatedObject.LoadingRow -= this.SyncColumnWidths;
        }

        private void SyncColumnWidths(object sender, DataGridRowEventArgs e)
        {
            var dataGrid = this.AssociatedObject;

            foreach (DataGridColumn c in dataGrid.Columns)
                c.Width = 0;

            e.Row.UpdateLayout();

            foreach (DataGridColumn c in dataGrid.Columns)
                c.Width = DataGridLength.Auto;
        }
    }

Usage

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

...

    <DataGrid
        ... >
        <i:Interaction.Behaviors>
            <ext:SyncedColumnWidthsBehavior />
        </i:Interaction.Behaviors>
    </DataGrid>

Behaviors offer a clean way to release event handlers. Although, in this case, even when we don't unsubscribe with the attached property, we wouldn't create memory leaks (ref Is it bad to not unregister event handlers?).

like image 174
TimvZon Avatar answered Sep 19 '22 12:09

TimvZon