How To: Maintaining Selected Cell in DataGridView When Sorting
- From: "Kevin Spencer" <kevin@xxxxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Wed, 15 Feb 2006 07:32:25 -0500
Hi all,
I had a devil of a time figuring this out, so I thought I'd share it for
anyone who might want to do it.
Problem: The DataGridView's default behavior is to maintain the selection on
the cell relative to the ViewPort when sorting, rather than maintaining the
currently selected cell or row. That is, when the DataGridView is sorted,
the selected cell will change from the currently selected cell to whatever
cell is in the same position relative to the ViewPort.
Conditions: For this particular project, I am designing a custom composite
Control from System.Windows.Forms.UserControl. It contains a DataGridView
(among other things). I therefore am not using the protected event handler
methods, but handling the events with my own event handlers.
In addition, this Control is designed to work with a
System.Windows.Forms.BindingSource (the DataSource property of the
DataGridView) that will always be linked to a DataSet and a DataTable in
that DataSet. So, this solution assumes a DataTable is bound to the Control.
However, what the DataSet is, what the DataTable contains, and any other
specific information is not known, as this Control is designed to accomodate
*any* DataSet/DataTable as a BindingSource (DataSource).
Solution: I started by creating a number of private properties to hold
information about the currently-selected cell:
// Tracks Currently-selected Cell
// The DataGridView.CurrentCell is not useful for this,
// As it doesn't change until *during* sorting.
private DataGridViewCell _CurrentCell = null;
// Used for selecting the new Current Cell
private Point _CurrentPoint = new Point(0,0);
// Prevents event handling when it interferes with the process
private bool _Sorting = false;
// Used for selecting the new Current Cell
private DataRow _CurrentDataRow = null;
// Prevents certain events from being handled
// when a DataException occurs
private bool _DataException = false;
//The following event handler for the DataException event
// sets _DataException to true
// It is, of course, important to have some mechanism for
// resetting _DataException to false somewhere.
private void dataGridView1_DataError(object sender,
DataGridViewDataErrorEventArgs e)
{
// Other error-handling code
_DataException = true;
}
// These lines are copied from the InitializeComponent method
// Of the parent Control
this.dataGridView1.MouseDown += new
System.Windows.Forms.MouseEventHandler(
this.dataGridView1_MouseDown);
this.dataGridView1.Sorted += new
System.EventHandler(this.dataGridView1_Sorted);
this.dataGridView1.DataError += new
System.Windows.Forms.DataGridViewDataErrorEventHandler(
this.dataGridView1_DataError);
this.dataGridView1.CellEnter += new
System.Windows.Forms.DataGridViewCellEventHandler(
this.dataGridView1_CellEnter);
// This event handler occurs as soon as a cell is entered.
// It sets the _CurrentCell
private void dataGridView1_CellEnter(object sender,
DataGridViewCellEventArgs e)
{
if (!_DataException)
{
if (_CurrentCell !=
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex])
_CurrentCell =
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
}
}
// Gets Current Cell and Current Row
// This event is used because it happens before anything else changes
// It uses the ColumnHeadersHeight to test if a Column Header has been
// clicked on. It sets the _CurrentRow and _CurrentPoint,
// which are used to reset the selected cell after sorting.
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Y < dataGridView1.ColumnHeadersHeight)
{
_Sorting = true;
_CurrentDataRow = ((System.Data.DataRowView)
(BindingSource.Current)).Row;
_CurrentPoint = new Point(_CurrentCell.ColumnIndex,
_CurrentCell.RowIndex);
}
}
// This event happens after sorting. The DataGridView.CurrentCell property
has already
// changed. This method changes it to the "last" selected cell.
// Note: The MaintainSelectedRecordOnSort is a public member that
// can be used to turn this behavior off. It is not necessary to use it in
order\
// to set the selected Cell.
private void dataGridView1_Sorted(object sender, EventArgs e)
{
if (_Sorting && MaintainSelectedRecordOnSort)
{
_Sorting = false;
DataView dv = ((System.Data.DataRowView)
(dataGridView1.CurrentRow.DataBoundItem)).DataView;
for (int i = 0; i < dv.Count; i++)
{
if (dv[i].Row.Equals(_CurrentDataRow))
{
dataGridView1.CurrentCell =
dataGridView1.Rows[i].Cells[_CurrentPoint.X];
}
}
}
}
A few more notes: Again, this is a solution for a specifically-purposed
DataGridView. However, it can be modified to support other scenarios. It
occurs to me, however, that using a DataSource that is a BindingSource to a
DataSet/DataTable is likely to be a common use for the DataGridView.
--
HTH,
Kevin Spencer
Microsoft MVP
..Net Developer
We got a sick zebra a hat,
you ultimate tuna.
.
- Prev by Date: FlowLayoutPanel and MinimumSize
- Next by Date: Re: TextBox TextChanged event.
- Previous by thread: FlowLayoutPanel and MinimumSize
- Next by thread: Re: Improving Control Painting
- Index(es):
Relevant Pages
|