第一行的 C# DataGridViewRow.Displayed 错误?

C# DataGridViewRow.Displayed bug for first row?

我有一个表单,其中一些控件根据 DataGridView 中当前显示的行(及其位置)进行更新。因此,在 dataGridView1.VerticalScrollBar.ValueChanged 事件中,我遍历所有行并检查它们是否显示。

所以这是滚动回到顶部(通过鼠标滚轮)时的问题:

检查https://referencesource.microsoft.com后,我发现FirstDisplayedCell实际上使用了this.Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen),这似乎有点不正确,或者我错了? 我该如何解决这个问题,以便获得正确显示的信息/第一行的矩形?

提前致谢!

代码:

public partial class Form1 : Form
{
    private List<Person> persons = new List<Person>()
    {
        {new Person("mike", 20) },
        {new Person("tim", 31) },
        {new Person("steven", 15) },
        {new Person("dave", 41) },
        {new Person("tom", 35) },
        {new Person("bob", 18) },
        {new Person("peter", 22) },
        {new Person("sarah", 55) },
        {new Person("robert", 69) },
        {new Person("andre", 23) },
    };
    public Form1()
    {
        this.InitializeComponent();
        this.dataGridView1.DataSource = new BindingSource(this.persons, null);

        if (typeof(DataGridView).GetProperty("VerticalScrollBar", BindingFlags.Instance | BindingFlags.NonPublic) is PropertyInfo pi &&
             pi.GetValue(this.dataGridView1, null) is ScrollBar sb)
        {
            sb.ValueChanged += this.Sb_ValueChanged;
        }
    }

    private void Sb_ValueChanged(object sender, EventArgs e)
    {
        int scrollValue = (sender as ScrollBar).Value;
        int index = this.dataGridView1.FirstDisplayedCell.RowIndex;
        int index2 = this.dataGridView1.FirstDisplayedScrollingRowIndex;
        DataGridViewRow row = this.dataGridView1.Rows[index];
        bool displayed = row.Displayed;
        Rectangle rect = this.dataGridView1.GetRowDisplayRectangle(index, false);
    }
}

看来 DataGridViewRow 的状态直到滚动事件处理完成后才更新。这就是为什么 DataGridViewRow.Displayed 属性 在向上滚动以显示以前未显示的行时显示 false 的原因。

您可以使用一些 hack,在滚动事件完成后立即将方法排队到 运行,方法是使用 Control.BeginInvoke

此外,没有理由使用反射来入侵 DataGridView 的 VerticalScrollBar。请改用 DataGridView.Scroll Event

以下是对代码的修改,使用 DataGridView.Scroll 事件和 BeginInvoke 来显示行状态并显示在滚动事件中和事件之后立即获得的矩形值。

private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
    if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
    {
        int scrollValue = e.NewValue;
        int index = this.dataGridView1.FirstDisplayedCell.RowIndex;
        int index2 = this.dataGridView1.FirstDisplayedScrollingRowIndex;
        DataGridViewRow row = this.dataGridView1.Rows[index];
        bool displayed = row.Displayed;
        Rectangle rect = this.dataGridView1.GetRowDisplayRectangle(index, false);

        Debug.WriteLine($"Scroll: index={index}, rect={rect}, state = {row.GetState(index)}");

        BeginInvoke(new Action(() =>
        {
            Rectangle r = dataGridView1.GetRowDisplayRectangle(index, false);
            Debug.WriteLine($"Invoked: index={index}, rect={r}, state = {row.GetState(index)}");
        }), null);
    }
}