c# DataSource를 DataGridView에 바인드한 때 스크롤 잠기는 오류

DataGridView에 무언가를 출력할 때 이걸 직접 제어하는 건 부하가 크다. 보통은 DataTable 같은 걸 만들어서 연결한 뒤 이걸 제어하여 자동으로 DataGridView에 출력되게 한다. 더 복잡해 보이지만 더 빠르다. 예를 들어 이렇게 바인드를 하면 DataGridView의 VirtualMode를 따로 설정하지 않아도 알아서 작동한다. 극단적인 경우지만 DataGridView가 수백 만 줄의 내용을 담아야 할 때 DataGridView에 직접 Add나 Insert로 내용을 넣으면서 VirtualMode를 이용하지 않으면 제대로 작동하지 않는다. 물론 이렇게 많은 행들을 한꺼번에 로드하는 일은 피해야 하며 그렇게 로드를 할 일도 없긴 하지만 …

하지만 이렇게 DataSource를 이용하는 건 한 단계라도 중간에 거치는 게 추가되기 때문에 DataGridView를 직접 제어하는 거보다 덜 깔끔하다. 아래의 코드는 DataTable을 연결하여 맨 윗줄에 새 행을 계속 삽입하는 건데 제대로 작동하지 않는다. 행들이 많아져서 스크롤바가 생긴 상태에서 아무 셀이나 클릭을 한 뒤 스크롤바를 옮기면 새로 행 삽입이 일어날 때 아까 선택된 셀이 맨 아래에 보이는 상태로 스크롤바가 다시 옮겨진다.

DataTable DataTable1 = new();

private void Form1_Load(object sender, EventArgs e)
{
    DataTable1.Columns.Add();

    dataGridView1.DataSource = DataTable1;
}

private void timer1_Tick(object sender, EventArgs e)
{
    DataRow dataRow = DataTable1.NewRow();

    dataRow[0] = DateTime.Now;

    DataTable1.Rows.InsertAt(dataRow, 0);

}

DataGridView에 직접 행을 삽입할 때에는 생기지 않는 문제다. 버그 같은데 어쨌든 번거롭지만 아래의 방법으로 해결할 수 있다. 행을 삽입하기 전에 현재 상태의 스크롤 위치를 정했다가 삽입을 한 뒤 다시 그 위치를 불러오는 거다.

private void timer1_Tick(object sender, EventArgs e)
{
    int index = dataGridView1.FirstDisplayedScrollingRowIndex;

    DataRow dataRow = DataTable1.NewRow();

    dataRow[0] = DateTime.Now;

    DataTable1.Rows.InsertAt(dataRow, 0);

    dataGridView1.FirstDisplayedScrollingRowIndex = index;
}