第一行的 C# DataGridViewRow.Displayed 错误?
C# DataGridViewRow.Displayed bug for first row?
我有一个表单,其中一些控件根据 DataGridView 中当前显示的行(及其位置)进行更新。因此,在 dataGridView1.VerticalScrollBar.ValueChanged
事件中,我遍历所有行并检查它们是否显示。
所以这是滚动回到顶部(通过鼠标滚轮)时的问题:
dataGridView1.VerticalScrollBar.Value
是 0
-> OK
dataGridView1.FirstDisplayedCell.RowIndex
是 0
-> OK
dataGridView1.FirstDisplayedScrollingRowIndex
是 0
-> OK
dataGridView1.Rows[0].Displayed
仍然是 false
-> 嗯?
dataGridView1.GetRowDisplayRectangle(0, false)
也是 {0,0,0,0}
-> ...?
检查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);
}
}
我有一个表单,其中一些控件根据 DataGridView 中当前显示的行(及其位置)进行更新。因此,在 dataGridView1.VerticalScrollBar.ValueChanged
事件中,我遍历所有行并检查它们是否显示。
所以这是滚动回到顶部(通过鼠标滚轮)时的问题:
dataGridView1.VerticalScrollBar.Value
是0
-> OKdataGridView1.FirstDisplayedCell.RowIndex
是0
-> OKdataGridView1.FirstDisplayedScrollingRowIndex
是0
-> OKdataGridView1.Rows[0].Displayed
仍然是false
-> 嗯?dataGridView1.GetRowDisplayRectangle(0, false)
也是{0,0,0,0}
-> ...?
检查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);
}
}