0

I'm working on a WinForms project, I have a Form with 5 data-bound DataGridView Controls.
I need to set a custom height to each Row:

enter image description here

I use the following code:

private void dgv_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
     dgv.SuspendLayout();
     var rowHeight = CalculateRowHeight((MyDto)dgvBatches.Rows[e.RowIndex].DataBoundItem));
     dgvBatches.Rows[e.RowIndex].Height = rowHeight;
     dgvBatches.ResumeLayout();
}

It works but the DataGridView loading and scrolling is slow.
Is there any tricks to improve loading and scrolling speed?

Jimi
  • 29,621
  • 8
  • 43
  • 61
Masoud
  • 8,020
  • 12
  • 62
  • 123
  • You ca try to [doublebuffer](https://stackoverflow.com/questions/44185298/update-datagridview-very-frequently/44188565#44188565) the dgv.. Also: Are you sure you need to set the row hights do often? – TaW May 10 '21 at 09:30
  • @TaW I used `doublebuffer` too, yes each row height depend on row data. – Masoud May 10 '21 at 09:42
  • _each row height depends on row data_ sure, we can see that but adapting the row height should only be necessary when the hieght/data actually do change. Log out the event calls to see how often it happens now! – TaW May 10 '21 at 10:03
  • @TaW How can I Log out event calls? – Masoud May 10 '21 at 10:23
  • Insert a Console.Writeline into the dgv_RowPrePaint event – TaW May 10 '21 at 10:40
  • So, my question(s) is “WHAT” determines a rows height? What is `CalculateRowHeight` doing? What would “change” a rows height once it has been set? I question why you did not post the `CalculateRowHeight` code in the first place? This code is obviously relevant. – JohnG May 10 '21 at 18:12
  • @Jimi Thanks, your suggestions solved the problem, please post your suggestions as answer. – Masoud May 11 '21 at 04:26
  • @JohnG `MyDto` has a `Weight` property and The height of each row depends on the weight of `MyDto.Weight`. you are right, My mistake was to change the height every time. – Masoud May 12 '21 at 06:18

1 Answers1

1

The RowPrePaint event is raised constantly: each time you scroll the DataGridView (multiple times), click on a Cell (2 or more times: 1 for the Header), when you hover the Mouse Pointer over 1 or more Cells, when you change a value etc.
It appears that the calculation of the Rows' height is only needed when a source of data is assigned to the Control, my suggestion is to perform this operation when data binding is complete.

The Control notifies that the binding is complete, raising the DataBindingComplete event.
In its handler, you can then suspend the layout, perform the calculations needed and assign the result to the each Row added to the grid:

private void dgv_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
    if (e.ListChangedType == ListChangedType.Reset) {
        dgvBatches.SuspendLayout();
        for (int i = 0; i < dgvBatches.Rows.Count - 1; i++) {
            if (i == dgvBatches.NewRowIndex) continue;
            var rowHeight = CalculateRowHeight((MyDto)dgvBatches.Rows[e.RowIndex].DataBoundItem));
            dgvBatches.Rows[e.RowIndex].Height = rowHeight;
        }
        dgvBatches.ResumeLayout(false);
    }
    else if (e.ListChangedType == ListChangedType.ItemChanged) {
        // Partner with CellValueChanged
    }
}

The DataBindingComplete event lets you know what caused the binding notification.
When you first assign a value to the DataSource property, the e.ListChangedType member will be set to ListChangedType.Reset, when a DataBoundItem value changes, it will be ListChangedType.ItemChanged, so you can determine what action is needed in this context.
You probably don't need to recalculate everything if a single Cell value changes, if it can be changed at all.
In case it does, you can resize a single Row, or all Rows that follow the Current, etc.

Since this layout may become complex, you could also benefit from double-buffering your DataGridView, unless you have a high number 1 of Rows (it doesn't look like, but it could be).

Try to add this to the Form Constructor, see whether it's better or not:

var flags = BindingFlags.Instance | BindingFlags.NonPublic;
dgvBatches.GetType().GetProperty("DoubleBuffered", flags).SetValue(dgvBatches, true);

1 - What a high number is depends on multiple factors, not just the number of Rows. You know the number of Rows is high when you test how the grid responds to different forms of interaction and you find it doesn't react as expected.

Jimi
  • 29,621
  • 8
  • 43
  • 61