MSDN Sample - The ListManager's position must be equal to rowNum

  • Thread starter Thread starter GK
  • Start date Start date
G

GK

Hi All,
I used DataGridComboBoxColumn from MSDN magazine (
http://msdn.microsoft.com/msdnmag/issues/03/08/DataGrids/default.aspx).
Normally things work fine.
but in the following cases I get an error 'The ListManager's
position must be equal to rowNum'.
================================
1) when I click on a comboboxcolumn cell
2) and scroll the datagrid
3) and click on another comboboxcolumn cell
OR
1) navigate to a tabpage other than the one datagrid is on
2) and navigate back to datagrid tabpage
3) and click on a comboboxcolumn cell
================================

Good news is I made a change to the code and the problem went away.
The bad news is I can't understand why!
Just wonder if the experts here might be able to help?

The change that I made to DataGridComboBoxColumn are
1) remove combobox.hide from datagrid_scroll event
2)add code in edit method to hide the combo when current cell is not
visible
(I'll attach both codes)

I have trace output for the following sequence of action
1) click on datagridcombobox column cell at row 3 (index 2)
2) then click on datagridcombobox column cell at row 5 (index 4)
3)scroll one cell
4)then click on datagridcombobox column cell at row 6 (index 5)

==============TRACE OUTPUT ORIGINAL CODE====================raises
the error
Edit 2
Leave: column1 column1
SetColumnValueAtRow 2 column1
Edit 4
-------------I scroll now
Edit 4
Leave: column1 column1
SetColumnValueAtRow 4 column1
Scroll
-------------combo hidden is hidden now
Leave: column1 column1
SetColumnValueAtRow 4 column1
Edit 4 <------things start to go wrong here? why is it entering edit
4...when it should be going to edit 5
Edit 5
Leave: column1 column1
SetColumnValueAtRow 4 column1
===============TRACE OUTPUT MODIFIED CODE====================no
errors raised here
Edit 2
Leave: column1 column1
SetColumnValueAtRow 2 column1
Edit 4
-------------scroll
Edit 4
Leave: column1 column1
SetColumnValueAtRow 4 column1
Scroll
-------------combo stays
Leave: column1 column1
SetColumnValueAtRow 4 column1
Edit 5
==================================

notice that things start to go a bit crazy in last 3-4 lines. The thing that
seems to cause the error is Edit method being called in the first case for
rowindex 4 and then rowindex5 immediately....unlike the second case....

It may not make much sense w/o the code so I attach both cases with the
mail.

====================ORIGINAL CODE===================
// Derive class from DataGridTextBoxColumn

public class DataGridComboBoxColumn : DataGridTextBoxColumn

{

// Hosted ComboBox control

private ComboBox comboBox;

private CurrencyManager cm;

private int iCurrentRow;


// Constructor - create combobox, register selection change event handler,

// register lose focus event handler

public DataGridComboBoxColumn()

{

this.cm = null;

// Create ComboBox and force DropDownList style

this.comboBox = new ComboBox();

this.comboBox.DropDownStyle = ComboBoxStyle.DropDownList;


// Add event handler for notification of when ComboBox loses focus

this.comboBox.Leave += new EventHandler(comboBox_Leave);

}


// Property to provide access to ComboBox

public ComboBox ComboBox

{

get { return comboBox; }

}


// On edit, add scroll event handler, and display combo box

protected override void Edit(System.Windows.Forms.CurrencyManager source,
int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string
instantText, bool cellIsVisible)

{

Debug.WriteLine(String.Format("Edit {0}", rowNum));

base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);

if (!readOnly && cellIsVisible)

{

// Save current row in the datagrid and currency manager associated with

// the data source for the datagrid

this.iCurrentRow = rowNum;

this.cm = source;



// Add event handler for datagrid scroll notification

this.DataGridTableStyle.DataGrid.Scroll += new
EventHandler(DataGrid_Scroll);


// Site the combo box control within the bounds of the current cell

this.comboBox.Parent = this.TextBox.Parent;

Rectangle rect = this.DataGridTableStyle.DataGrid.GetCurrentCellBounds();

this.comboBox.Location = rect.Location;

this.comboBox.Size = new Size(this.TextBox.Size.Width,
this.comboBox.Size.Height);

// Set combo box selection to given text

this.comboBox.SelectedIndex =
this.comboBox.FindStringExact(this.TextBox.Text);

// Make the ComboBox visible and place on top text box control

this.comboBox.Show();

this.comboBox.BringToFront();

this.comboBox.Focus();

}

// else

// {

// this.comboBox.Hide();

// }

}

// Given a row, get the value member associated with a row. Use the value

// member to find the associated display member by iterating over bound
datasource

protected override object
GetColumnValueAtRow(System.Windows.Forms.CurrencyManager source, int rowNum)

{

//Debug.WriteLine(String.Format("GetColumnValueAtRow {0}", rowNum));

// Given a row number in the datagrid, get the display member

object obj = base.GetColumnValueAtRow(source, rowNum);


// Iterate through the datasource bound to the ColumnComboBox

// Don't confuse this datasource with the datasource of the associated
datagrid

CurrencyManager cm = (CurrencyManager)

(this.DataGridTableStyle.DataGrid.BindingContext[this.comboBox.DataSource]);

// Assumes the associated DataGrid is bound to a DataView, or DataTable that

// implements a default DataView

DataView dataview = ((DataView)cm.List);


int i;

for (i = 0; i < dataview.Count; i++)

{

if (obj.Equals(dataview[this.comboBox.ValueMember]))

break;

}


if (i < dataview.Count)

return dataview[this.comboBox.DisplayMember];


return DBNull.Value;

}

// Given a row and a display member, iterating over bound datasource to find

// the associated value member. Set this value member.

protected override void
SetColumnValueAtRow(System.Windows.Forms.CurrencyManager source, int rowNum,
object value)

{

Debug.WriteLine(String.Format("SetColumnValueAtRow {0} {1}", rowNum,
value));

object s = value;

// Iterate through the datasource bound to the ColumnComboBox

// Don't confuse this datasource with the datasource of the associated
datagrid

CurrencyManager cm = (CurrencyManager)

(this.DataGridTableStyle.DataGrid.BindingContext[this.comboBox.DataSource]);

// Assumes the associated DataGrid is bound to a DataView, or DataTable that

// implements a default DataView

DataView dataview = ((DataView)cm.List);

int i;

for (i = 0; i < dataview.Count; i++)

{

if (s.Equals(dataview[this.comboBox.DisplayMember]))

break;

}

// If set item was found return corresponding value, otherwise return
DbNull.Value

if(i < dataview.Count)

s = dataview[this.comboBox.ValueMember];

else

s = DBNull.Value;


base.SetColumnValueAtRow(source, rowNum, s);

}

// On datagrid scroll, hide the combobox

private void DataGrid_Scroll(object sender, EventArgs e)

{

Debug.WriteLine("Scroll");

this.comboBox.Hide();

}

// On combo box losing focus, set the column value, hide the combo box,

// and unregister scroll event handler

private void comboBox_Leave(object sender, EventArgs e)

{

DataRowView rowView = (DataRowView) this.comboBox.SelectedItem;

string s = (string) rowView.Row[this.comboBox.DisplayMember];

Debug.WriteLine(String.Format("Leave: {0} {1}", this.comboBox.Text, s));

SetColumnValueAtRow(this.cm, this.iCurrentRow, s);

Invalidate();

this.comboBox.Hide();

this.DataGridTableStyle.DataGrid.Scroll -= new
EventHandler(DataGrid_Scroll);

}

}

===================================MODIFIED
CODE=========================================

// Derive class from DataGridTextBoxColumn

public class DataGridComboBoxColumn : DataGridTextBoxColumn

{

// Hosted ComboBox control

private ComboBox comboBox;

private CurrencyManager cm;

private int iCurrentRow;


// Constructor - create combobox, register selection change event handler,

// register lose focus event handler

public DataGridComboBoxColumn()

{

this.cm = null;

// Create ComboBox and force DropDownList style

this.comboBox = new ComboBox();

this.comboBox.DropDownStyle = ComboBoxStyle.DropDownList;


// Add event handler for notification of when ComboBox loses focus

this.comboBox.Leave += new EventHandler(comboBox_Leave);

}


// Property to provide access to ComboBox

public ComboBox ComboBox

{

get { return comboBox; }

}


// On edit, add scroll event handler, and display combo box

protected override void Edit(System.Windows.Forms.CurrencyManager source,
int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string
instantText, bool cellIsVisible)

{

Debug.WriteLine(String.Format("Edit {0}", rowNum));

base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);

if (!readOnly && cellIsVisible)

{

// Save current row in the datagrid and currency manager associated with

// the data source for the datagrid

this.iCurrentRow = rowNum;

this.cm = source;



// Add event handler for datagrid scroll notification

this.DataGridTableStyle.DataGrid.Scroll += new
EventHandler(DataGrid_Scroll);


// Site the combo box control within the bounds of the current cell

this.comboBox.Parent = this.TextBox.Parent;

Rectangle rect = this.DataGridTableStyle.DataGrid.GetCurrentCellBounds();

this.comboBox.Location = rect.Location;

this.comboBox.Size = new Size(this.TextBox.Size.Width,
this.comboBox.Size.Height);

// Set combo box selection to given text

this.comboBox.SelectedIndex =
this.comboBox.FindStringExact(this.TextBox.Text);

// Make the ComboBox visible and place on top text box control

this.comboBox.Show();

this.comboBox.BringToFront();

this.comboBox.Focus();

}

else

{

this.comboBox.Hide();

}

}

// Given a row, get the value member associated with a row. Use the value

// member to find the associated display member by iterating over bound
datasource

protected override object
GetColumnValueAtRow(System.Windows.Forms.CurrencyManager source, int rowNum)

{

//Debug.WriteLine(String.Format("GetColumnValueAtRow {0}", rowNum));

// Given a row number in the datagrid, get the display member

object obj = base.GetColumnValueAtRow(source, rowNum);


// Iterate through the datasource bound to the ColumnComboBox

// Don't confuse this datasource with the datasource of the associated
datagrid

CurrencyManager cm = (CurrencyManager)

(this.DataGridTableStyle.DataGrid.BindingContext[this.comboBox.DataSource]);

// Assumes the associated DataGrid is bound to a DataView, or DataTable that

// implements a default DataView

DataView dataview = ((DataView)cm.List);


int i;

for (i = 0; i < dataview.Count; i++)

{

if (obj.Equals(dataview[this.comboBox.ValueMember]))

break;

}


if (i < dataview.Count)

return dataview[this.comboBox.DisplayMember];


return DBNull.Value;

}

// Given a row and a display member, iterating over bound datasource to find

// the associated value member. Set this value member.

protected override void
SetColumnValueAtRow(System.Windows.Forms.CurrencyManager source, int rowNum,
object value)

{

Debug.WriteLine(String.Format("SetColumnValueAtRow {0} {1}", rowNum,
value));

object s = value;

// Iterate through the datasource bound to the ColumnComboBox

// Don't confuse this datasource with the datasource of the associated
datagrid

CurrencyManager cm = (CurrencyManager)

(this.DataGridTableStyle.DataGrid.BindingContext[this.comboBox.DataSource]);

// Assumes the associated DataGrid is bound to a DataView, or DataTable that

// implements a default DataView

DataView dataview = ((DataView)cm.List);

int i;

for (i = 0; i < dataview.Count; i++)

{

if (s.Equals(dataview[this.comboBox.DisplayMember]))

break;

}

// If set item was found return corresponding value, otherwise return
DbNull.Value

if(i < dataview.Count)

s = dataview[this.comboBox.ValueMember];

else

s = DBNull.Value;


base.SetColumnValueAtRow(source, rowNum, s);

}

// On datagrid scroll, hide the combobox

private void DataGrid_Scroll(object sender, EventArgs e)

{

Debug.WriteLine("Scroll");

// this.comboBox.Hide();

}

// On combo box losing focus, set the column value, hide the combo box,

// and unregister scroll event handler

private void comboBox_Leave(object sender, EventArgs e)

{

DataRowView rowView = (DataRowView) this.comboBox.SelectedItem;

string s = (string) rowView.Row[this.comboBox.DisplayMember];

Debug.WriteLine(String.Format("Leave: {0} {1}", this.comboBox.Text, s));

SetColumnValueAtRow(this.cm, this.iCurrentRow, s);

Invalidate();

this.comboBox.Hide();

this.DataGridTableStyle.DataGrid.Scroll -= new
EventHandler(DataGrid_Scroll);

}

}



Thanks if you've got here... :-)

GK
 
Back
Top