C
Chris Plowman
Hi all,
I was wondering if anyone can help me with a really annoying problem I have
been having. I made a derived datagrid class that will select the row when
a user clicks anywhere on a cell (multi-select without modifier keys). I
got that working fine, but I also wanted to keep rows selected after a sort,
which I do by storing the row's id in an arraylist. The idea was to do the
sort and then go back and re-select the rows with that row id, which will
now be in a different location. The problem is that to walk through the
datagrid I have to set the current cell and for some reason that is messing
up the array's values. I am at a total loss. Anyone know why this is
happening and/or know a fix? Have I screwed the code up somewhere? Any
help would be dearly appreciated. Here is my code:
public class RowSelectDataGrid : DataGrid
{
private const int OFFSET = 5;
private bool multiselect;
private string id_field;
private ArrayList data_id_array;
// these are used for single selects
private int lastrow;
private int lastID;
/// <summary>
/// Constructor for the row select data grid
/// </summary>
/// <param name="id_field_name">Name of the field that holds a record id for
your dataset</param>
/// <param name="multi">Boolean value to enable the multiselect feature of
the datagrid</param>
public RowSelectDataGrid(string id_field_name, bool multi) : base()
{
this.data_id_array = new ArrayList();
this.lastrow = -1; // default value
this.lastID = -1; // default value
this.id_field = id_field_name;
this.multiselect = multi;
}
/// <summary>
/// Select the rows that contain ids in the selections array of ints
/// </summary>
/// <param name="selections">ArrayList of the record ids to be
selected</param>
public void SetSelectionList( ArrayList selections )
{
this.data_id_array.Clear();
foreach( int id in selections )
{
if( this.multiselect )
this.data_id_array.Add( id );
else // single select only has 1 entry max
this.data_id_array[0] = id;
}
RefreshSelects();
}
/// <summary>
/// Get or Set the multiselect datagrid option
/// </summary>
public bool MultiSelect
{
get
{
return this.multiselect;
}
set
{
// when this changes, the selections are cleared
this.data_id_array.Clear();
this.multiselect = value;
}
}
/// <summary>
/// Reprocess the entire datagrid line by line, selecting previously
/// specified rows
/// </summary>
public void RefreshSelects()
{
// Debugging -- this works, just selects the wrong row
// this.Select(this.lastrow);
// go through each line and select if it is in the list, unselect otherwise
int iRow;
DataGridCell checkCell = new DataGridCell();
checkCell.ColumnNumber = 1;
BindingManagerBase bm;
DataRowView drv;
DataTable myTable = ((DataSet) this.DataSource).Tables[this.DataMember];
for( iRow = 0; iRow < myTable.Rows.Count; iRow++ )
{
// position at the first column of the current row
checkCell.RowNumber = iRow;
this.CurrentCell = checkCell; // ***************** THIS IS THE PROBLEM
LINE -- HOW DOES IT CHANGE THE PRIVATE ARRAYLIST???
bm = BindingContext[this.DataSource, this.DataMember];
drv = (DataRowView) bm.Current;
// if this row has an id in the selection list, make sure it is selected
if( this.data_id_array.Contains( Int32.Parse(
drv[this.id_field].ToString() ) ) )
{
this.Select( iRow );
// if single select, update the row (don't need to update the value)
if( !this.multiselect )
this.lastrow = iRow;
}
// else
// this.UnSelect( iRow );
}
}
/*
* Note that we can just use the datagrid's select(int row) method to do
* multiple selects and unselect to remove them.
* You should capture a column header click so that you can make sure
* the correct rows are selected after the sort
* */
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
DataGrid.HitTestInfo hti = this.HitTest(e.X, e.Y);
if( hti.Type == HitTestType.Cell )
{
// set up the data row view
DataGridCell clickedCell = new DataGridCell();
clickedCell.RowNumber = hti.Row;
clickedCell.ColumnNumber = hti.Column;
this.CurrentCell = clickedCell;
BindingManagerBase bm = BindingContext[this.DataSource, this.DataMember];
DataRowView drv = (DataRowView) bm.Current;
// if click on the append row (couldn't get it to not show) do nothing
if( drv[this.id_field].ToString() == "" )
return;
// otherwise select or unselect the row and add or remove it from the array
else
{
// if the row is currently in the selection list
if( this.data_id_array.Contains( Int32.Parse(
drv[this.id_field].ToString() ) ) )
{
this.data_id_array.Remove( Int32.Parse( drv[this.id_field].ToString() ) );
this.UnSelect( hti.Row );
// for single selects, clear the last selected row
if( !this.multiselect )
{
this.lastrow = -1;
this.lastID = -1;
}
}
// if it is not in the selection list
else
{
this.data_id_array.Add( Int32.Parse( drv[this.id_field].ToString() ) );
this.Select( hti.Row );
// for single selects, unselect the old, remove it from the list
// and update lastrow
if( !this.multiselect )
{
if( this.lastrow != -1 )
{
this.UnSelect( this.lastrow );
this.data_id_array.Remove( this.lastID );
}
this.lastrow = hti.Row;
this.lastID = Int32.Parse( drv[this.id_field].ToString() );
}
}
}
}
// sort and then fix the selections (since rows have changed)
else if( hti.Type == HitTestType.ColumnHeader )
{
base.OnMouseDown(e);
// this.RefreshSelects();
}
else
{
base.OnMouseDown(e);
}
}
protected override void OnCurrentCellChanged(System.EventArgs e)
{
base.OnCurrentCellChanged(e);
if(Control.MouseButtons != MouseButtons.Left)
{
Rectangle rect = this.GetCellBounds(this.CurrentCell);
MouseEventArgs e1 = new MouseEventArgs(MouseButtons.Left, 0, OFFSET, rect.Y
+ OFFSET, 0);
OnMouseDown(e1);
}
}
}
/// <summary>
/// This section of is very basic, but effective. By overriding the Edit
function, a cell in the column
/// cannot become active for editing, allowing the datagrid events to be
handled.
/// </summary>
public class DataGridNoActiveCellColumn : DataGridTextBoxColumn
{
protected override void Edit(System.Windows.Forms.CurrencyManager source,
int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string
instantText,
bool cellIsVisible)
{
// Empty. Do NOT call the base class!
}
}
I was wondering if anyone can help me with a really annoying problem I have
been having. I made a derived datagrid class that will select the row when
a user clicks anywhere on a cell (multi-select without modifier keys). I
got that working fine, but I also wanted to keep rows selected after a sort,
which I do by storing the row's id in an arraylist. The idea was to do the
sort and then go back and re-select the rows with that row id, which will
now be in a different location. The problem is that to walk through the
datagrid I have to set the current cell and for some reason that is messing
up the array's values. I am at a total loss. Anyone know why this is
happening and/or know a fix? Have I screwed the code up somewhere? Any
help would be dearly appreciated. Here is my code:
public class RowSelectDataGrid : DataGrid
{
private const int OFFSET = 5;
private bool multiselect;
private string id_field;
private ArrayList data_id_array;
// these are used for single selects
private int lastrow;
private int lastID;
/// <summary>
/// Constructor for the row select data grid
/// </summary>
/// <param name="id_field_name">Name of the field that holds a record id for
your dataset</param>
/// <param name="multi">Boolean value to enable the multiselect feature of
the datagrid</param>
public RowSelectDataGrid(string id_field_name, bool multi) : base()
{
this.data_id_array = new ArrayList();
this.lastrow = -1; // default value
this.lastID = -1; // default value
this.id_field = id_field_name;
this.multiselect = multi;
}
/// <summary>
/// Select the rows that contain ids in the selections array of ints
/// </summary>
/// <param name="selections">ArrayList of the record ids to be
selected</param>
public void SetSelectionList( ArrayList selections )
{
this.data_id_array.Clear();
foreach( int id in selections )
{
if( this.multiselect )
this.data_id_array.Add( id );
else // single select only has 1 entry max
this.data_id_array[0] = id;
}
RefreshSelects();
}
/// <summary>
/// Get or Set the multiselect datagrid option
/// </summary>
public bool MultiSelect
{
get
{
return this.multiselect;
}
set
{
// when this changes, the selections are cleared
this.data_id_array.Clear();
this.multiselect = value;
}
}
/// <summary>
/// Reprocess the entire datagrid line by line, selecting previously
/// specified rows
/// </summary>
public void RefreshSelects()
{
// Debugging -- this works, just selects the wrong row
// this.Select(this.lastrow);
// go through each line and select if it is in the list, unselect otherwise
int iRow;
DataGridCell checkCell = new DataGridCell();
checkCell.ColumnNumber = 1;
BindingManagerBase bm;
DataRowView drv;
DataTable myTable = ((DataSet) this.DataSource).Tables[this.DataMember];
for( iRow = 0; iRow < myTable.Rows.Count; iRow++ )
{
// position at the first column of the current row
checkCell.RowNumber = iRow;
this.CurrentCell = checkCell; // ***************** THIS IS THE PROBLEM
LINE -- HOW DOES IT CHANGE THE PRIVATE ARRAYLIST???
bm = BindingContext[this.DataSource, this.DataMember];
drv = (DataRowView) bm.Current;
// if this row has an id in the selection list, make sure it is selected
if( this.data_id_array.Contains( Int32.Parse(
drv[this.id_field].ToString() ) ) )
{
this.Select( iRow );
// if single select, update the row (don't need to update the value)
if( !this.multiselect )
this.lastrow = iRow;
}
// else
// this.UnSelect( iRow );
}
}
/*
* Note that we can just use the datagrid's select(int row) method to do
* multiple selects and unselect to remove them.
* You should capture a column header click so that you can make sure
* the correct rows are selected after the sort
* */
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
DataGrid.HitTestInfo hti = this.HitTest(e.X, e.Y);
if( hti.Type == HitTestType.Cell )
{
// set up the data row view
DataGridCell clickedCell = new DataGridCell();
clickedCell.RowNumber = hti.Row;
clickedCell.ColumnNumber = hti.Column;
this.CurrentCell = clickedCell;
BindingManagerBase bm = BindingContext[this.DataSource, this.DataMember];
DataRowView drv = (DataRowView) bm.Current;
// if click on the append row (couldn't get it to not show) do nothing
if( drv[this.id_field].ToString() == "" )
return;
// otherwise select or unselect the row and add or remove it from the array
else
{
// if the row is currently in the selection list
if( this.data_id_array.Contains( Int32.Parse(
drv[this.id_field].ToString() ) ) )
{
this.data_id_array.Remove( Int32.Parse( drv[this.id_field].ToString() ) );
this.UnSelect( hti.Row );
// for single selects, clear the last selected row
if( !this.multiselect )
{
this.lastrow = -1;
this.lastID = -1;
}
}
// if it is not in the selection list
else
{
this.data_id_array.Add( Int32.Parse( drv[this.id_field].ToString() ) );
this.Select( hti.Row );
// for single selects, unselect the old, remove it from the list
// and update lastrow
if( !this.multiselect )
{
if( this.lastrow != -1 )
{
this.UnSelect( this.lastrow );
this.data_id_array.Remove( this.lastID );
}
this.lastrow = hti.Row;
this.lastID = Int32.Parse( drv[this.id_field].ToString() );
}
}
}
}
// sort and then fix the selections (since rows have changed)
else if( hti.Type == HitTestType.ColumnHeader )
{
base.OnMouseDown(e);
// this.RefreshSelects();
}
else
{
base.OnMouseDown(e);
}
}
protected override void OnCurrentCellChanged(System.EventArgs e)
{
base.OnCurrentCellChanged(e);
if(Control.MouseButtons != MouseButtons.Left)
{
Rectangle rect = this.GetCellBounds(this.CurrentCell);
MouseEventArgs e1 = new MouseEventArgs(MouseButtons.Left, 0, OFFSET, rect.Y
+ OFFSET, 0);
OnMouseDown(e1);
}
}
}
/// <summary>
/// This section of is very basic, but effective. By overriding the Edit
function, a cell in the column
/// cannot become active for editing, allowing the datagrid events to be
handled.
/// </summary>
public class DataGridNoActiveCellColumn : DataGridTextBoxColumn
{
protected override void Edit(System.Windows.Forms.CurrencyManager source,
int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string
instantText,
bool cellIsVisible)
{
// Empty. Do NOT call the base class!
}
}