WPF DataGrid:滚动时减小列的宽度以适合其内容

时间:2018-12-09 13:16:22

标签: c# wpf scroll datagrid width

当我滚动垂直滚动条时,如果新的可见行中的内容更大并且超过了先前的列宽,则DataGrid将自动扩展列宽。没关系。

但是,如果所有较大的行都滚动了,而新的可见行的内容宽度较小,则DataGrid不会减小列的宽度。有没有办法存档?

附加的行为实现会很棒。

代码行为:

 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>

2 个答案:

答案 0 :(得分:4)

LoadingRow属性添加到您的Datagrid:

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

然后添加此代码 在后面的代码中:

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

这绝对不是最干净的解决方案,但是它将调整滚动时查看的列的大小。

希望这会有所帮助。


  

您能把它包装成附加的行为吗?

1)第一种选择是使用附加属性

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

用法

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

2)或者,行为提供了一种更加封装的功能扩展方式(需要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;
        }
    }

用法

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

...

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

行为提供了一种干净的方法来释放事件处理程序。尽管在这种情况下,即使我们不取消订阅附加属性,也不会造成内存泄漏(参考Is it bad to not unregister event handlers?)。

答案 1 :(得分:2)

抱歉延迟回答您的问题。

我在这里的方法是捕获屏幕上的可见行并获取平均宽度并分配给列宽。

首先订阅ScrollViewer的ScrollChanged事件。

       <DataGrid
        ScrollViewer.ScrollChanged="DG_ScrollChanged"
        x:Name="DG"
        CanUserAddRows="False"
        CanUserDeleteRows="False"
        CanUserReorderColumns="False"
        CanUserResizeColumns="False"
        CanUserResizeRows="False"
        CanUserSortColumns="False" />
</Grid>

通过使用ScrollChangedFindVisualChildren,我可以获得垂直偏移。

我们可以从垂直偏移量获取行索引,最后一行的索引使用(int)scroll.VerticalOffset +(int)scroll.ViewportHeight-1

     ScrollViewer scroll = null;
    private void DG_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        // get the control once and then use its offset to get the row index
        if (scroll == null)
            scroll = MethodRepo.FindVisualChildren<ScrollViewer>((DependencyObject)sender).First(); 

        int firstRow = (int)scroll.VerticalOffset;
        int lastRow = (int)scroll.VerticalOffset + (int)scroll.ViewportHeight + 1;

        List<int> FirstColumnLength = new List<int>();
        List<int> SecondColumnLength = new List<int>();
        for (int i = firstRow; i < lastRow-1; i++)
        {
            FirstColumnLength.Add(Convert.ToString(persons[i].Name).Length);
            SecondColumnLength.Add(Convert.ToString(persons[i].Surname).Length);
        }

        DG.Columns[0].Width = FirstColumnLength.Max()*10;
        DG.Columns[1].Width = SecondColumnLength.Max() * 10;
    }

我还创建了一个静态类来获取Visual子级。

 public static class MethodRepo
{
    public static IEnumerable<T> FindVisualChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
    {
        if (parent == null)
            throw new ArgumentNullException(nameof(parent));

        var queue = new Queue<DependencyObject>(new[] { parent });

        while (queue.Any())
        {
            var reference = queue.Dequeue();
            var count = VisualTreeHelper.GetChildrenCount(reference);

            for (var i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(reference, i);
                if (child is T children)
                    yield return children;

                queue.Enqueue(child);
            }
        }
    }
}

enter image description here