WPF数据表格在列宽缩小时不会缩小。

6
我在WPF中使用DataGrid,并希望它缩小以适应其列的宽度。对于初始渲染,它可以很好地实现这一点。当我调整列的宽度以使其更宽时,网格也会增长。但是,如果我再次调整列的宽度使其变窄,我的列右侧就会出现空白区域(并且我可以看到列标题灰色区域延伸到了列之外)。
我希望数据网格随着列的宽度缩小,这样我就不会在右边得到白色空间。我已经尝试调试代码,据我所见问题出现在DataGridCellsPanel中,但我找不到任何地方修复宽度测量。
任何帮助都将不胜感激。
3个回答

3

我之前也遇到过这个问题,感到非常烦恼,于是我做了一个丑陋的解决方案。虽然不太美观,但它可以完成工作。首先,只有在水平滚动条不可见时才会出现这个问题,所以我们需要引用它。这段代码必须在所有DataGridColumns加载完毕后运行(在我的情况下,全部在Xaml中,因此使用Loaded事件),它没有考虑添加/删除DataGridColumns,但这很容易修复。

<DataGrid Name="c_dataGrid"
          Loaded="c_dataGrid_Loaded"
          ...>
    <DataGrid.Columns>
        <DataGridTextColumn ..."/>
        <DataGridTextColumn ..."/>
        <!-- ... -->

然后在Loaded事件处理程序中,我们获取DataGrid ScrollViewer,并为DataGrid中每个DataGridColumn的ActualWidthProperty添加监听器以便于检测其变化。

private ScrollViewer m_dataGridScrollViewer = null;
private void c_dataGrid_Loaded(object sender, RoutedEventArgs e)
{
    m_dataGridScrollViewer = GetVisualChild<ScrollViewer>(c_dataGrid);
    DependencyPropertyDescriptor dependencyPropertyDescriptor =
        DependencyPropertyDescriptor.FromProperty(DataGridColumn.ActualWidthProperty, typeof(DataGridColumn));
    if (dependencyPropertyDescriptor != null)
    {
        foreach (DataGridColumn column in c_dataGrid.Columns)
        {
            dependencyPropertyDescriptor.AddValueChanged(column, DataGridColumn_ActualWidthChanged);
        }
    }
}

然后我们通过所有DataGridColumns的大小计算DataGrid的大小,并添加一个常量8.0(这通常是差异)。

private void DataGridColumn_ActualWidthChanged(object sender, EventArgs e)
{
    if (m_dataGridScrollViewer != null)
    {
        if (m_dataGridScrollViewer.ComputedHorizontalScrollBarVisibility != Visibility.Visible)
        {
            double dataGridWidth = 8.0;
            foreach (DataGridColumn column in c_dataGrid.Columns)
            {
                dataGridWidth += column.ActualWidth;
            }
            c_dataGrid.Width = dataGridWidth;
        }
        else
        {
            c_dataGrid.Width = double.NaN;
        }
    }
}

如果你有更好的做法,请告诉我 :)
public static T GetVisualChild<T>(object parent) where T : Visual
{
    DependencyObject dependencyObject = parent as DependencyObject;
    return InternalGetVisualChild<T>(dependencyObject);
}
private static T InternalGetVisualChild<T>(DependencyObject parent) where T : Visual
{
    T child = default(T);

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++)
    {
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
            child = GetVisualChild<T>(v);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}

这是一个不错的解决方案。我稍微调整了一下,使其设置MaxWidth属性。这解决了网格超出视觉父级约束的问题。我还将其转换为行为,以更好地封装它。 - jjrdk
此外,如果 DataGridColumn_ActualWidthChanged 没有被调用,请确保在获取 DependencyPropertyDescriptor 时使用 Microsoft.Windows.Controls.DataGridColumn 而不是 System.Windows.Controls.DataGridColumn。 - Monsignor

2

这是一个不错的解决方案。我稍微调整了一下,使其设置MaxWidth属性。这解决了网格扩展超出可视父级的问题。我还将其转换为行为以更好地封装它。

这就是最终结果。

public class UpdateWidthOnColumnResizedBehavior : Behavior<DataGrid>
{
        private static readonly DependencyPropertyDescriptor Descriptor;

        static UpdateWidthOnColumnResizedBehavior()
        {
            Descriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.ActualWidthProperty, typeof(DataGridColumn));
        }

        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.Columns.CollectionChanged += OnColumnsCollectionChanged;

            foreach (var column in AssociatedObject.Columns)
            {
                AddListener(column);
            }
        }

        void OnColumnsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var column in e.NewItems.OfType<DataGridColumn>())
                    {
                        AddListener(column);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var column in e.OldItems.OfType<DataGridColumn>())
                    {
                        RemoveListener(column);
                    }
                    break;
                case  NotifyCollectionChangedAction.Replace:
                    foreach (var column in e.NewItems.OfType<DataGridColumn>())
                    {
                        AddListener(column);
                    }
                    foreach (var column in e.OldItems.OfType<DataGridColumn>())
                    {
                        RemoveListener(column);
                    }
                    break;
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            foreach (var column in AssociatedObject.Columns)
            {
                RemoveListener(column);
            }
        }

        private void AddListener(DataGridColumn column)
        {
            Descriptor.AddValueChanged(column, ResizeGrid);
        }

        private void RemoveListener(DataGridColumn column)
        {
            Descriptor.RemoveValueChanged(column, ResizeGrid);
        }

        private void ResizeGrid(object sender, EventArgs e)
        {
            var columnsWidth = AssociatedObject.Columns.Sum(c => c.ActualWidth);
            AssociatedObject.MaxWidth = columnsWidth + 2;
            AssociatedObject.InvalidateMeasure();
        }
    }

我还需要解决两个网格宽度协调方面的一些问题,但对于一个网格来说,看起来是有效的。


看起来很不错,下次需要的时候我会试试! - Fredrik Hedblad

0

你们两种方法似乎都存在一些小问题。当我将最左边的列向右拖动时,整个网格会被重新调整大小/卷入(不幸的是,我没有足够的声望来发布图片)。

因此,我修改了jjrdk的ResizeGrid函数,使其计算出最后一列的宽度,并将其扩展到最左边。网格的HorizontalAlignment和HorizontalContentAlignment必须设置为HorizontalAlignment.Stretch。

void ResizeGrid(object sender, EventArgs e) 
    {
         var scroll = ExTreeHelper.FindVisualChild<ScrollViewer>(AssociatedObject);

        if (scroll != null && null != AssociatedObject.Columns && AssociatedObject.Columns.Count > 0)
        {
            var lastColumn = AssociatedObject.Columns.Last();

            double dataGridWidth = AssociatedObject.Columns.Sum(c => c.ActualWidth) + 2.0;

            if (scroll.ComputedHorizontalScrollBarVisibility != Visibility.Visible)
            {
                RemoveListener(lastColumn);

                AssociatedObject.Columns.Last().Width =
                    AssociatedObject.Columns.Last().Width.DisplayValue + scroll.ViewportWidth - dataGridWidth;

                AssociatedObject.Width = dataGridWidth + scroll.ViewportWidth - dataGridWidth;

                AddListener(lastColumn);
            }
            else
            {
                AssociatedObject.HorizontalAlignment = HorizontalAlignment.Stretch;
                AssociatedObject.HorizontalContentAlignment = HorizontalAlignment.Stretch;

                AssociatedObject.Width = double.NaN;
            }
        }         }  

我唯一的问题是,即使所有列都适应了,滚动条仍然会存在。

还有另一个问题,当所有列都折叠到左侧时,它开始闪烁。

有没有办法真正消除这个空白区域呢?

Leon


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接