调整一个行高时如何设置wpf数据网格的所有行高

Posted

技术标签:

【中文标题】调整一个行高时如何设置wpf数据网格的所有行高【英文标题】:how to set all row heights of the wpf datagrid when one row height is adjusted 【发布时间】:2009-08-19 10:41:58 【问题描述】:

我正在使用 wpf 数据网格,并且正在寻找一种方法来在用户调整其中一个时设置所有行的高度。我知道数据网格有一个 RowHeight 属性,可以一次设置所有的行高,但是如何捕捉单个行高的变化让我无法理解

【问题讨论】:

【参考方案1】:

没有任何事件可以直接用于此目的。您可以做的是使用在调整行大小和其他内容时触发的另一个事件。我现在想到的事件是PreviewMouseUp,当你在数据网格的任何地方释放鼠标按钮时释放。

您可以做的是,当事件被触发时,您可以检查所有行的行高并找到不同的行,然后用它更新所有行。

【讨论】:

【参考方案2】:

我通过反复试验得出了这个结论,只要您使用的是 ItemsSource 数据源,它应该可以正常工作。 它应该与虚拟行一起工作,并且只会导致短暂的视觉暂停并切换(这似乎主要归结为列自动生成,因此可以避免)。

随着 hacks 的发展,它具有简单性和使用预计不会改变的机制的优点。

用户触发操作的启发式可能会得到改进,但我还没有失败。

using Microsoft.Windows.Controls;
using Microsoft.Windows.Controls.Primitives;

public static class DataGridExtensions

    public static void LinkRowHeightsToUserChange(this DataGrid dataGrid)
    
        double? heightToApply = null;
        bool userTriggered = false;

        if (dataGrid.RowHeaderStyle == null)
            dataGrid.RowHeaderStyle = new Style(typeof(DataGridRowHeader));
        if (dataGrid.RowStyle == null)
            dataGrid.RowStyle = new Style(typeof(DataGridRow));

        dataGrid.RowStyle.Setters.Add(new EventSetter()
        
            Event = DataGridRow.SizeChangedEvent,
            Handler = new SizeChangedEventHandler((r, sizeArgs) =>
            
                if (userTriggered && sizeArgs.HeightChanged)
                        heightToApply = sizeArgs.NewSize.Height;
            )
        );
        dataGrid.RowHeaderStyle.Setters.Add(new EventSetter()
        
            Event = DataGridRowHeader.PreviewMouseDownEvent,
            Handler = new MouseButtonEventHandler(
                (rh,e) => userTriggered = true)
        );
        dataGrid.RowHeaderStyle.Setters.Add(new EventSetter()
        
            Event = DataGridRowHeader.MouseLeaveEvent,
            Handler = new MouseEventHandler((o, mouseArgs) =>
            
                if (heightToApply.HasValue)
                
                    userTriggered = false;
                    var itemsSource = dataGrid.ItemsSource;
                    dataGrid.ItemsSource = null;
                    dataGrid.RowHeight = heightToApply.Value;
                    dataGrid.ItemsSource = itemsSource;
                    heightToApply = null;
                
            )
        );
    

【讨论】:

这行得通,虽然虚拟化开启时速度很慢。 对不起,当列虚拟化关闭时..,如果我们打开它,我们无法水平滚动,但行确实调整了大小。 是的,我喜欢你只在有人实际调整大小时才为该功能支付大量费用,但代价高昂。您可能可以以某种方式缓存列,删除源并重新创建它而不重新生成列。 我们删除了将 itemsSource 设置为 null 的行,然后再次设置它。如果正在调整行高并且有人双击其中一个单元格并且没有项目源,我们发现这些行可能会给我们带来问题。它还显着加快了速度。你还记得这背后的基本原理吗? @Aran 恐怕我不记得了 :( 教我不要评论奇怪的代码。如果没有它就可以工作,那么它很有可能是可靠的。【参考方案3】:

@阿兰

你还记得这背后的基本原理吗?

我可以告诉你:如果你删除两行以取消设置并重置项目源(这确实会减慢整个过程),你调整大小的行将明确设置其高度。

似乎当您调整行的大小时,您直接更改了它的高度,这会覆盖您为该行设置的 dataGrid 的 RowHeight 属性的任何值。所以基本上,这就是你能得到的:

dataGrid 的 RowHeight = 20

您将一行的高度(比如第 5 行)更改为 30 => 此行的高度设置为 30,而 dataGrid 的行高度设置为 30。到目前为止一切都很好。

现在,将另一行的高度改回 20(比如第二行)。您将此行的高度设置为 20,将 DataGrid'RowHeight 设置为 20,这会将所有其他行设置为 20,除了保持在 30 的第 5 行。(因为它之前被强制设置为这个值)

清空源并重置它会强制重新加载每一行并考虑 dataGrid 的 RowHeight,从而消除了问题。

【讨论】:

很有趣,这正是我们的行为,我一直想看看它,但还没有开始,谢谢。【参考方案4】:

据我所知,调整行高时不会引发任何事件。

我的第一个建议是设置 RowStyle 以便在 DataGridRow 的高度属性和数据网格的 RowHeight 属性之间创建绑定 (OneWay),但是 如果在调整大小后检查行的高度,它不会改变,ActualHeight 是调整大小时包含行的“实际”高度的属性,并且 ActualHeight 不能设置,因为“它没有可访问的设置访问器”。

尝试后我想:DataGridRow 的 ActualHeight 的价值从何而来?

我记得这个post 解释了如何检测单击了哪个单元格和行,还显示了 DataGrid 的默认模板可视化树。

通过反复试验(使用上面链接中的可视树图像),我发现是 DataGridCellPresenter 存储了正在使用的高度(实际上我不是 100% 确定这一点,它是第一类自从 DataGridCell 以来,视觉树的高度是否发生了变化。

显然 DataGrid 没有公开 API 以从 DataGridRow 获取 DataGridCellsPresenter(我发现 here)

所以我的第一种方法是在 DataGrid 中获取所有 DataGridCellPresenter(通过可视化树),然后以编程方式在 DataGridPresenter 的 Height 属性和 DataGrid 的 RowHeight 属性之间创建绑定。

这是执行此操作的代码(我的 DataGrid 的实例名称是 dataGrid1):

获取所有DataGridCellPresenter:

    void GetAllDataGridCellPresenters(DependencyObject parent, List<DataGridCellsPresenter> presenters) 
    
        int numberOfChildren = VisualTreeHelper.GetChildrenCount(parent);         
        for (int i = 0; i < numberOfChildren; i++)
        
            if (VisualTreeHelper.GetChild(parent, i) is DataGridCellsPresenter)
            
                presenters.Add(VisualTreeHelper.GetChild(parent, i) as DataGridCellsPresenter);
            
            else if (VisualTreeHelper.GetChild(parent, i) != null)
            
                GetAllDataGridCellPresenters(VisualTreeHelper.GetChild(parent, i), presenters);
            
            else
                return;
        
    

以编程方式在所有这些上设置绑定(当 DataGrid 引发 Loaded 事件时调用它):

    void SetBindingInDataGridPresenter()
    
        List<DataGridCellsPresenter> presenters = new List<DataGridCellsPresenter>();
        GetAllDataGridCellPresenters(dataGrid1, presenters);
        foreach (DataGridCellsPresenter presenter in presenters)
            
            Binding binding = new Binding("RowHeight");
            binding.Source = dataGrid1;
            binding.Mode = BindingMode.TwoWay;
            presenter.SetBinding(DataGridCellsPresenter.HeightProperty, binding);
        
    

(注意:将绑定设置为 OneWayToSource 不起作用,我真的不知道为什么,我可能在这里遗漏了一些明显的东西......)

这确实有效...有点...因为我使用可视化树来获取 DataGridCellsPresenter 我只得到了可见的:P,但这表明它可以通过这种方式完成。

因此,最后,正确的做法是提供 DataGrid 控件模板,它可以与默认模板一样,但 DataGridCellsPresenter 的 Height 属性数据绑定到 DataGrid 的 RowHeight 属性。

我知道这并没有确切说明如何做到这一点,但你只需要学习(我也是:P) 给redefine a control's template;以某种方式获取默认的 DataGrid 模板(或者,如果您已经在使用另一个那么棒的模板,您可能比我更了解它并且已经知道如何做到这一点,以便将 DataGridCellsPresenter Height 属性自动绑定到 RowHeight DataGrid 属性)和用一点魔法来改变它,让两个高度属性都绑定。

【讨论】:

以上是关于调整一个行高时如何设置wpf数据网格的所有行高的主要内容,如果未能解决你的问题,请参考以下文章

请教:android gridlayout 如何设置列宽和行高?

第好几天。

怎样根据内容自动调整excel表格的行高列宽?

elementuitable左右固定如何调整每个单元格行高

怎么设置excel的行高和行宽

POI生成excel如何实现自动调整行高