The problem is that you are creating the CommandField button in a postback event: SumbitSearch_Click
. Take a look at Wictor's wonderful graphic.
Instead, you should be overriding the CreateChildControls
function and creating most of your controls there. You'll notice, however, that on a postback the CreateChildControls
will be called before your SumbitSearch_Click
function (which makes sense if you think about it.. the button must exist before the button click event can fire).
You could use the Page.IsPostback
within CreateChildControls
to have some separate logic or use OnPreRender
to make the decision since it will run after your events.
Here is an example which uses the session as a DataSource.

The code:
class SimpleGridView : CompositeControl
{
private const string SESSION_ID = "SalesData";
private ObjectDataSource _gridDS;
private TextBox _txtQuery;
private SPGridView _grid;
public DataView SelectData(string query)
{
DataTable dataSource = null;
// using session to simulate a "delete", otherwise it would do nothing each time :)
if (null == this.Page.Session[SESSION_ID])
{
dataSource = new DataTable();
// setup columns
dataSource.Columns.Add("ID", Type.GetType("System.Int32"));
dataSource.Columns.Add("Name");
dataSource.Columns.Add("Region");
dataSource.Columns.Add("Total Sales", Type.GetType("System.Double"));
dataSource.Columns.Add("Created", Type.GetType("System.DateTime"));
// add some bogus data
dataSource.Rows.Add(1, "J. Smith", "Europe", 10000, new DateTime(2010, 10, 7, 8, 0, 0));
dataSource.Rows.Add(2, "J. Smith", "North America", 15000, new DateTime(2010, 10, 8, 9, 00, 0));
dataSource.Rows.Add(3, "J. Smith", "Asia", 5000, new DateTime(2010, 10, 7, 10, 0, 0));
dataSource.Rows.Add(4, "S. Jones", "Europe", 7000, new DateTime(2010, 10, 9, 11, 0, 0));
dataSource.Rows.Add(5, "S. Jones", "North America", 30000, new DateTime(2010, 10, 7, 9, 00, 0));
dataSource.Rows.Add(6, "S. Jones", "Asia", 8700, new DateTime(2010, 10, 9, 8, 0, 0));
dataSource.Rows.Add(7, "W. Nguyen", "Europe", 3000, new DateTime(2010, 10, 8, 8, 0, 0));
dataSource.Rows.Add(8, "W. Nguyen", "North America", 50000, new DateTime(2010, 10, 7, 12, 30, 0));
dataSource.Rows.Add(9, "W. Nguyen", "Asia", 25000, new DateTime(2010, 10, 7, 8, 0, 0));
dataSource.Rows.Add(10, "C. O'Reilly", "Europe", 200, new DateTime(2008, 1, 16, 15, 0, 0));
dataSource.Rows.Add(11, "C. O'Reilly", "North America", 5000, new DateTime(2009, 11, 1, 16, 0, 0));
dataSource.Rows.Add(12, "C. O'Reilly", "Asia", 8000, new DateTime(2010, 10, 9, 19, 0, 0));
this.Page.Session[SESSION_ID] = dataSource;
}
else
{
dataSource = (DataTable)this.Page.Session[SESSION_ID];
}
DataView view = new DataView(dataSource);
if (!string.IsNullOrEmpty(query))
{
query = query.Replace("'", "''");
view.RowFilter = string.Format("Name LIKE '%{0}%'", query);
}
return view;
}
public void DeleteItem(int row_id)
{
// do some sort of delete action against our datasource
// in this simple case, we're just deleting rows from the session
DataTable dataSource = (DataTable)this.Page.Session[SESSION_ID];
DataRow[] rows = dataSource.Select(string.Format("ID = {0}", row_id));
if (null != rows && rows.Length == 1)
{
rows[0].Delete();
}
}
private void AddBoundField(string name)
{
BoundField column = new BoundField();
column.DataField = name; // must match the column name in the datasource
column.SortExpression = name;
column.HeaderText = name;
_grid.Columns.Add(column);
}
protected override void CreateChildControls()
{
base.CreateChildControls();
_txtQuery = new TextBox();
_txtQuery.ID = "txtQuery";
this.Controls.Add(_txtQuery);
// really all this is good for is causing the page to post back
Button btnSubmit = new Button();
btnSubmit.ID = "btnSubmit";
btnSubmit.Text = "Submit";
btnSubmit.Click += new EventHandler(btnSubmit_Click);
this.Controls.Add(btnSubmit);
if (Page.IsPostBack)
{
// don't display the grid until they click submit at least once
CreateGridView();
}
}
void btnSubmit_Click(object sender, EventArgs e)
{
// clear so that they can query multiple times
_gridDS.SelectParameters.Clear();
// they clicked the button so set our query
_gridDS.SelectParameters.Add("query", _txtQuery.Text);
}
private void CreateGridView()
{
const string GRIDID = "grid";
const string DATASOURCEID = "gridDS";
// initialize the ODS
_gridDS = new ObjectDataSource();
_gridDS.ID = DATASOURCEID;
// calling our SelectData function in this CompositeControl
_gridDS.SelectMethod = "SelectData";
// calls our Delete Item method when they click our CommandField delete button
_gridDS.DeleteMethod = "DeleteItem";
// so our grid datasource knows which object to ask for data
_gridDS.TypeName = this.GetType().AssemblyQualifiedName;
_gridDS.ObjectCreating += new ObjectDataSourceObjectEventHandler(gridDS_ObjectCreating);
// we need this event in order to add parameters that will be used for our delete method
// otherwise it can't find it
_gridDS.Deleting += new ObjectDataSourceMethodEventHandler(_gridDS_Deleting);
this.Controls.Add(_gridDS);
// Initialize the grid and link it to the ObjectDataSource
_grid = new SPGridView();
_grid.ID = GRIDID;
_grid.DataSourceID = _gridDS.ID;
_grid.AutoGenerateColumns = false;
// Paging
_grid.AllowPaging = true;
_grid.PageSize = 10;
// Sorting
_grid.AllowSorting = true;
this.Controls.Add(_grid);
// add our columns to the grid
AddBoundField("ID");
AddBoundField("Name");
AddBoundField("Region");
AddBoundField("Total Sales");
AddBoundField("Created");
// our column that shows the delete button a user can click
CommandField cfDelete = new CommandField();
cfDelete.ShowDeleteButton = true;
cfDelete.ButtonType = ButtonType.Button;
cfDelete.DeleteText = "Delete";
_grid.Columns.Add(cfDelete);
// needed to allow deleting rows using ID
// if you don't have this, nothing is sent to your ObjectDataSource "Deleting" event
string[] dataKeyNames = { "ID" };
_grid.DataKeyNames = dataKeyNames;
// setting PagerTemplate to null here will display page numbers for paging
_grid.PagerTemplate = null;
}
void _gridDS_Deleting(object sender, ObjectDataSourceMethodEventArgs e)
{
// This function isn't needed if your ObjectDataSource.DeleteMethod parameters
// exactly match the column names in your datasource. I found this confusing
// so I do a little conversion
// it will be null if the column is not present in the grid's DataKeyNames
if (null != e.InputParameters["ID"])
{
int id = (int)e.InputParameters["ID"];
// clear the collection of parameters or it will try to find
// a delete function that has every single parameter
e.InputParameters.Clear();
// notice that this parameter name matches the parameter in our delete method
// which was set on the grid's objectdatasource
e.InputParameters.Add("row_id", id);
}
}
private void gridDS_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
// our ObjectDataSource uses this CompositeControl to select data
e.ObjectInstance = this;
}
}