调整某一行高时如何设置wpf datagrid的所有行高

发布于 2024-08-02 17:33:08 字数 104 浏览 8 评论 0原文

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

Im using the wpf datagrid and am looking for a way to set the height on all of the rows when the user adjusts one of them. I know the datagrid has a RowHeight property that sets all of the row heights at once, but the how of catching an individual row height changed escapes me

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

卷耳 2024-08-09 17:33:08

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

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

There aren't any events that could be use directly for that. What you could do is use another event that is fired when you resize the rows and other things. The event I'm thinking of right now is PreviewMouseUp, which is release when you release the mouse button anywhere if you datagrid.

What you could do is when the event is fired, you could check the row height of all of your rows and find the one that is different, then update all rows with it.

旧时浪漫 2024-08-09 17:33:08

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

就黑客而言,它具有简单性和使用预计不会改变的机制的优点。

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

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

I arrived on this by trial and error, so long ass you are using an ItemsSource data source it should work fine.
It should work with virtual rows and causes only a brief visual pause and it switches over (this seems mainly down to column autogeneration so can be avoided).

As hacks go it has the advantage of simplicity and the use of mechanics which are not expected to change.

The heuristic on user triggering of the action might be improved but it has not failed on me yet.

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;
                }
            })
        });
    }
一个人的旅程 2024-08-09 17:33:08

@阿兰

你还记得这背后的理由吗?

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

看起来,当您调整行的大小时,您会直接更改其高度,这会覆盖您为此行设置的 dataGrid RowHeight 属性的任何值。所以基本上,您可以得到以下结果:

dataGrid 的 RowHeight = 20

您将一行的高度(例如第 5 行)更改为 30 =>该行的高度设置为 30,dataGrid 的 RowHeight 设置为 30。到目前为止,一切看起来都很好。

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

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

@Aran

do you remember the rationale behind this?

I can tell you: If you remove both lines to unset and reset the items source (which indeed slows the whole process quite a bit), the row you resize will have its height definitively set.

It seems when you resize a row, you change its Height directly, and this overrides any value you set to the dataGrid's RowHeight property for this row in particular. So basically, here is what you can get :

dataGrid's RowHeight = 20

you change the Height of one Row (say the 5th) to 30 => this row's Height is set to 30 and the dataGrid's RowHeight is set to 30. Everything looking good so far.

now, change another row's Height back to 20 (say the 2nd row). you Set this row's Height to 20 and the DataGrid'RowHeight to 20, which puts all the other rows to 20, EXCEPT the 5th row which stays at 30. (because it had been forced to this value before)

emptying the source and resetting it forces each row to be reloaded and take the dataGrid's RowHeight into account, which eliminates the problem.

捎一片雪花 2024-08-09 17:33:08

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

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

尝试之后我想:DataGridRow 的 ActualHeight 从哪里获取它的值?

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

通过反复试验(使用上面链接中的可视化树图像),我发现 DataGridCellPresenter 存储了正在使用的高度(实际上我对此不是 100% 确定,它是第一个具有高度的类)自 DataGridCell 以来更改了可视化树)。

显然,DataGrid 没有公开从 DataGridRow 获取 DataGridCellsPresenter 的 API(正如我发现的 此处

因此,我的第一种方法是在填充 DataGrid 中的所有 DataGridCellPresenter(通过可视化树),并以编程方式在 DataGridPresenter 的 Height 属性和 DataGridPresenter 的 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;
        }
    }

以编程方式在所有 DataGridCellPresenter 上设置绑定(当 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)
重新定义控件的模板< /a>;以某种方式获取默认的 DataGrid 模板(或者,如果您已经使用了另一个很棒的模板,您可能比我更了解它,并且已经知道如何执行此操作,以便将 DataGridCellsPresenter Height 属性自动绑定到 RowHeight DataGrid 属性)并且使用绑定两个高度属性的魔法来改变它。

As far as I know there is no event that is raised when you resize a row's height.

My first suggestion would be to set the RowStyle in order to create a binding (OneWay) between the the DataGridRow's height property and the datagrid's RowHeight property, but
if you check the Row's height after you resize it, it is unchanged, the ActualHeight is the property that contains the row's "actual" height when you resize it, and ActualHeight cannot be set because "it does not have an accessible set accessor".

After trying this I thought: Where does DataGridRow's ActualHeight gets its value from?

I remembered this post that explains how to detect which cell and row got clicked and also shows the DataGrid's default template visual tree.

By trial and error (using the visual tree image in the link above) I found that it was DataGridCellPresenter that stored the Height that was being used (actually I'm not 100% sure about this, it was the first class that had the height changed up the visual tree since DataGridCell).

Apparently DataGrid doesn't expose the API to get the DataGridCellsPresenter from a DataGridRow (as I found out here)

So my first approach was to get all the DataGridCellPresenter in the DataGrid (through the visual tree) after it has been populated and programatically create a binding between the Height property of the DataGridPresenter and the RowHeight property of the DataGrid.

Here's the code to do that (my DataGrid's instance name is dataGrid1):

Getting all the 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;
        }
    }

Setting the bindings programatically on all of them (call this when the Loaded event is raised by the DataGrid):

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

(Note: Setting the binding as OneWayToSource didn't work, I really don't know why, I'm probably missing something obvious here...)

This did work... sort of... because I used the Visual Tree to get the DataGridCellsPresenter I only got the visible ones :P, but this shows it can be done this way.

So, finally, the right way to do it would be to supply the DataGrid control template, it can be just as the default one except with the DataGridCellsPresenter's Height property data bound to the RowHeight property of the DataGrid.

I know this does not show exactly how to do it, but you just have to learn (so do I :P)
to redefine a control's template; somehow get the default DataGrid template (or if you're already using another then great, you probably know more than me about it and already know how to do it in order to get the DataGridCellsPresenter Height property automatically bound to the RowHeight DataGrid property) and change it with that bit of magic that gets both height properties bound.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文