How to scroll DataGridView in pixel increments

2019-02-17 22:50发布

问题:

My original approach was to enclose my DataGridView inside a panel, set its height outside the boundary of the panel and then change control panel's scroll bar position. However, I just found out that the number of rows inside my DataGridView would reach 2000, which forces me to size the DataGridView to at least:

65px (row height) x 2,000 = 130,000px.

The maximum allowed size for a DataGridView control on my 64bit machine is 65,535. Anyone can think of a way around this issue?

回答1:

Hmm, no, you cannot have it both ways. The size restriction of 65535 pixels is not artificial, it is a hard-baked limitation in the operating system. The underlying reason is the WM_VSCROLL message. The value of the passed LPARAM argument is documented as:

The HIWORD specifies the current position of the scroll box if the LOWORD is SB_THUMBPOSITION or SB_THUMBTRACK

HIWORD are the upper 16 bits in the value, LOWORD are the lower 16 bits. Which can only provide a range of 0 .. Pow(2, 16)-1 = 0..65535. This limitation is visible in other places, a ListBox misbehaves badly if you stuff it with more than (65535 + visible) items. You can't scroll to the bottom anymore.

Sounds like a pretty crummy problem, surely Microsoft ought to fix such horrid limitations in their software. But no, this is not on top of their priority list. Not unlike the seemingly strange limitation that WaitHandle.WaitAny() cannot deal with an array that contains more than 64 items. This is intentional, they think that you are doing it wrong, not them. And you are, a user interface that expects a user to have to claw through thousands of rows of information is not a very usable or friendly interface. Take a cue from, say, the way Google displays hundreds of thousands of query hits.



回答2:

Sorry to say but this requirement is not the way a DataGridView works. You don't actually interact directly with the scrollbar.

Take this code for example, the only property you have any control of is the FirstDisplayedScrollingRowIndex.

You cannot scroll a DataGridView row as a fraction (eg a half row scroll or... with more granularity down to the pixel level as you desire.)

private void button1_Click(object sender, EventArgs e)
{
    var dt = new DataTable();
    dt.Columns.Add("test");

    var rnd = new Random();
    for (int i = 0; i < 10000; i++)
    {
        var dr = dt.NewRow();
        dr[0] = rnd.Next().ToString();
        dt.Rows.Add(dr);
    }
    dataGridView1.DataSource = dt;
}

int i = 0;
int lastTopRow = 0;
var isRecursive = false;
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
    if (isRecursive) return;
    //Honor each 20th scroll for "*attempted gradual*" scrolling
    if (i % 20 == 0)
    {
        lastTopRow++;
    }

    isRecursive = true;
    dataGridView1.FirstDisplayedScrollingRowIndex = lastTopRow;
    isRecursive = false;

    //Boundary check
    if (i > int.MaxValue) i = 0;
    i++;
}

You might need to resort to screenshots (ie using Blips) so you can trick the user into thinking the grid is scrolling one pixel at a time when really its just a image. I wouldn't recommend this approach.

The other way is using a ViewPort like a Panel or a GroupBox and changing the top position of the GridView.

Unless you want users of your application getting RSI or CTS I dont see the point in scrolling so precisely.



回答3:

Maybe you could set the row height to be something like 1 pixel so all rows become 1 pixel high at the moment it all loads in. Then assuming you know how many rows can be visible in the maximum height of the DataGridView that you have you could make sure 1 row is selected and gets placed in the center of it. Then with a loop you could set the height of the selected row and x rows above and below the selected row to the 65 pixels. And have when the selected row changes (when the user uses arrow keys etc) have it scroll the selected to the center and resize the rows again. That's the only way I can think of for this case.

I hope that it is some help to you.