代码之家  ›  专栏  ›  技术社区  ›  jjrdk

当列宽收缩时,WPF DataGrid不会收缩

  •  5
  • jjrdk  · 技术社区  · 14 年前

    我在WPF中使用一个DataGrid,希望它缩小到只适合其列的宽度。对于最初的渲染,它做得很好。当我调整列的大小以使其更宽时,网格也会随之增长。但是,如果我调整列的大小,使其再次变窄,我会在列的右侧得到空白(我可以看到列标题灰色区域扩展到列之外)。

    我想让数据网格用列缩小它的宽度,这样就不会得到右边的空白。我试过调试代码,据我所见,问题出在DataGridCellsPanel中,但我看不到任何地方可以修复宽度测量。

    任何帮助都将不胜感激。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Fredrik Hedblad    14 年前

    我有一段时间以前遇到过这个问题,我被它惹恼了,所以我做了一个丑陋的修复。虽然不漂亮,但能完成任务。首先,当水平滚动条不可见时这只是一个问题,所以我们需要一个对它的引用。一旦加载了所有DataGridColumns(在我的例子中,都是在Xaml中,所以是加载的事件),就必须运行这段代码,并且不考虑添加/删除DataGridColumns,但这是一个简单的修复方法。

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

    然后在加载的EventHandler中,我们获取DataGrid ScrollViewer,并添加一个侦听器,用于更改DataGrid中每个DataGrid列的actualwidth属性。

    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;
    }
    
        2
  •  2
  •   jjrdk    14 年前

    这是一个很好的解决方案。我对它进行了一些微调,以便它改为设置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();
            }
        }
    

    关于两个网格的宽度协调,我还有一些事情要解决,但它看起来只适用于一个网格。

        3
  •  0
  •   Leon    13 年前

    两种方法似乎都有点问题。当我将最左边的列拖到右边时,整个网格将被调整大小/卷到里面(不幸的是,我没有足够的声誉来发布图像)。

    所以我修改了jjrdk ResizeGrid函数,它计算最后一个列宽,并将它一直扩展到左边。网格水平对齐和水平内容对齐必须设置为 水平对齐。拉伸。

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

    我唯一的问题是滚动条总是在那里,即使所有的列都是合适的。

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

    有什么可以做的,才能真正摆脱这片空白?

    里昂