0. はじめに
Freeradicalの中の人、yamarahです。
DataGridの列幅は、自動で拡張はすれど、縮小はしないです。列幅が*
であり、初回表示前に全てのデータが入っていれば適切にレイアウトされるんですが、後から行を追加すると「列2の幅は余裕があるんだから縮めて、列3の幅を広くしてくれよ」って感じになることが多々あります。
何が問題で、原因なのかは以下が詳しい。
幅が*
の列幅を一旦ゼロにして、UpdateLayout()
した後に列幅を*
に戻すと、良い感じに再レイアウトされます。
コードビハインドはいやなので、behaviorを作りました。
1. DataGridの列幅をリセットするbehavior
using Microsoft.Xaml.Behaviors;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Freeradical.Windows.Behaviors;
public class RefreshDataGridColumnWidthsBehavior : Behavior<DataGrid>
{
public static readonly DependencyProperty RefreshColumnWidthsCommandProperty = DependencyProperty.Register(
nameof(RefreshColumnWidthsCommand),
typeof(ICommand),
typeof(RefreshDataGridColumnWidthsBehavior),
new PropertyMetadata(null));
public ICommand RefreshColumnWidthsCommand
{
get => (ICommand)GetValue(RefreshColumnWidthsCommandProperty);
set => SetValue(RefreshColumnWidthsCommandProperty, value);
}
protected override void OnAttached()
{
base.OnAttached();
RefreshColumnWidthsCommand = new RefreshColumnWidths(AssociatedObject);
}
protected class RefreshColumnWidths(DataGrid dataGrid) : ICommand
{
public event EventHandler? CanExecuteChanged;
protected readonly DataGrid AssociatedObject = dataGrid;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
var starWidthColumns = AssociatedObject.Columns
.Select((x, index) => (index, x.Width))
.Where(pair => pair.Width.IsStar)
.Select(pair => (pair.index, pair.Width.Value))
.ToArray();
foreach ((var index, _) in starWidthColumns)
{
AssociatedObject.Columns[index].Width = 0;
}
AssociatedObject.UpdateLayout();
foreach ((var index, var value) in starWidthColumns)
{
AssociatedObject.Columns[index].Width = new DataGridLength(value, DataGridLengthUnitType.Star);
}
}
}
}
命名がダサいので、各自リファクタリングしてください。名前空間も。
使い方は、
<Window (中略)
xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
xmlns:behaviors="clr-namespace:Freeradical.Windows.Behaviors"
(中略)>
<DataGrid (中略)>
<i:Interaction.Behaviors>
<behaviors:RefreshDataGridColumnWidthsBehavior RefreshColumnWidthsCommand="{Binding RefreshDataGridColumnWidthsCommand, Mode=OneWayToSource}"/>
</i:Interaction.Behaviors>
(中略)
</DataGrid>
</Window>
みたいな感じで。
あとは、ViewModelで
public ICommand? RefreshDataGridColumnWidthsCommand { get; set; }
RefreshDataGridColumnWidthsCommand?.Execute(this);
とすれば、良い感じに再レイアウトされます。やったね!