DataGridColumnCollection.Add & Update

K

Karine Proot

Hello,

(I'm impressed there's a dedicated newsgroup for datagrids !)

I am trying to build a webpage with a list showing some database table names displaying as links. Clicking on one of them will fill a datagrid with it, providing Edit and Delete functionnalitites.
Displaying the datagrid went great, and deleting items is ok also. My problem is with editing.

Here is the datagrid :

<asp:datagrid id="dgTAB" Runat="server" AutoGenerateColumns="False" OnEditCommand="TAB_Edit"
OnCancelCommand="TAB_Cancel" OnUpdateCommand="TAB_Update" OnDeleteCommand="TAB_Delete"><Columns><asp:ButtonColumn HeaderText="Supprimer" Text="<img src='../images/boutons/supprimer.gif' />" CommandName="Delete" /><asp:EditCommandColumn HeaderText="Modifier" EditText="<image src='../images/boutons/modifier.gif'></image>"
CancelText="<image src='../images/boutons/non.gif'></image>" UpdateText="<image src='../images/boutons/oui.gif'></image>" /></Columns></asp:datagrid>


I add the other columns in the code behind :

col = new BoundColumn ();
col.DataField = ...;
col.HeaderText = ...;
dgTAB.Columns.Add (col);

Now when I click on 'Edit', all the columns are filled with Textboxes containing the old value, which is perfect.

public void TAB_Edit (Object sender, DataGridCommandEventArgs e)

Here, e.Item has only two cells (the one for Delete and the one for Edit). I read this is normal in http://msdn.microsoft.com/library/d...rolsdatagridcolumncollectionclassaddtopic.asp :

"The DataGrid control does not store the contents of its Columns collection into the view state. To add or remove a column dynamically, you must programmatically add or remove the column each time the page is refreshed. Provide a Page_Init function that adds or removes the column before the DataGrid control can reload its state and rebuild itself. Otherwise, the changes to the Columns collection are not reflected in the DataGrid control when it is displayed."

But, if I do add the columns in Page_Init, I have to write again the "col.DataField=" part, which will overwrite anything changed in the textboxes.

How do I get the Textboxes new values ?

Or did I make something wrong ?

Karine Proot
 
S

Steven Cheng[MSFT]

Hi Karine,

Thanks for posting in the community!
From your description, you'd like to add bind a webform DataGrid with some
datas. The datagrid has two command button column which contain the Edit
and Delete button. Also, when the edit button is clicked, the current edit
row's data will be displayed in textboxes so as for being edited. In
addition, you'd like to dynamically create a BoundColumn and add it into
the DataGrid and found it can't remain value when the page is posted back,
yes?

If there is anything I misunderstood, please feel free to let me know.

As for this problem, I'd like to confirm with some further things:
1. Do you only use BoundColumns in the DataGrid?

2. Why do you have to dynamically add the BoundColumn rather than add it in
the static page template?

3. Have you used Template columns?

Based on my experience, if you'd like to provide the Editing data and
update functions as I mentioned above, you need to use Template columns.
The BoundColumn could only provide binding and display function. It won't
automatidally provide TextBox for editing the data when you clicked Edit
button of the DataGrid. The template column has serveral Template such as
"ItemTemplate", "EditTemplate", which can help you define the Display style
and Edit time style of the DataRow in DataGrid. For example:
<asp:TemplateColumn HeaderText="Name" >
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"name") %>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtName" Runat=server Text='<%#
DataBinder.Eval(Container.DataItem,"name") %>'>
</asp:TextBox>
</EditItemTemplate>
</asp:TemplateColumn>

Also, the TemplateColumns are commonly add in the Page Template rather than
dynamically in Codebehind unless you provide a custom TemplateColumn class
which implement the ITemplate interface. For more detailed info on template
column, you can view the following reference in MSDN:
#Creating Custom Columns for the ASP.NET Datagrid
http://msdn.microsoft.com/library/en-us/dnaspp/html/creatingcustomcolumns.as
p?frame=true

In addtion, based on your descriptions, I've made a sample page using the
Template column to provide the display, update, delete data's functions,
you may have a look to see whether it is suitable for your situation. Here
is the page's code:
----------------------------------aspx page-----------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>DGrid</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<table width="500" align="center">
<tr>
<td>
<asp:DataGrid id="dgMain" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="Index" >
<ItemTemplate>
<asp:Label ID="lblIndex" Runat=server Text='<%#
DataBinder.Eval(Container.DataItem,"index") %>'>
</asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Name" >
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"name") %>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtName" Runat=server Text='<%#
DataBinder.Eval(Container.DataItem,"name") %>'>
</asp:TextBox>
</EditItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Description" >
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"description") %>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtDescription" Runat=server TextMode=MultiLine
Text='<%# DataBinder.Eval(Container.DataItem,"description") %>' >
</asp:TextBox>
</EditItemTemplate>
</asp:TemplateColumn>
<asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Update"
HeaderText="Operations" CancelText="Cancel"
EditText="Edit"></asp:EditCommandColumn>
<asp:ButtonColumn Text="Delete" HeaderText="Delete"
CommandName="Delete"></asp:ButtonColumn>
</Columns>
</asp:DataGrid></td>
</tr>
<tr>
<td></td>
</tr>
</table>
</form>
</body>
</HTML>

----------------------------------codebehind class--------------------
public class DGrid : System.Web.UI.Page
{
protected System.Web.UI.WebControls.DataGrid dgMain;
System.Web.UI.WebControls.TemplateColumn col;

private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
if(!IsPostBack)
{
LoadData();
BindGrid();
}
}

protected void LoadData()
{
DataTable tb = new DataTable();
tb.Columns.Add("index");
tb.Columns.Add("name");
tb.Columns.Add("description");

for(int i=0;i<15;i++)
{
int index = i+1;
DataRow newrow = tb.NewRow();

newrow["index"] = index.ToString();
newrow["name"] = "Name" + index.ToString();
newrow["description"] = "Description" + index.ToString();

tb.Rows.Add(newrow);
}


Session["TEMP_DATA"] = tb;

}

protected void BindGrid()
{
DataTable tb = (DataTable)Session["TEMP_DATA"];
dgMain.DataSource = tb;
dgMain.DataBind();
}


#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dgMain.CancelCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_CancelComm
and);
this.dgMain.EditCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_EditComman
d);
this.dgMain.UpdateCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_UpdateComm
and);
this.dgMain.DeleteCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_DeleteComm
and);
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion

private void dgMain_DeleteCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
DataTable tb = (DataTable)Session["TEMP_DATA"];
Label lblIndex = (Label)e.Item.Cells[0].FindControl("lblIndex");
string select = "index = " + lblIndex.Text;
DataRow[] rows = tb.Select(select);
if(rows.Length > 0)
{
tb.Rows.Remove(rows[0]);
}

BindGrid();
}

private void dgMain_EditCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{

dgMain.EditItemIndex = e.Item.ItemIndex;

BindGrid();
}

private void dgMain_UpdateCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
DataTable tb = (DataTable)Session["TEMP_DATA"];
Label lblIndex = (Label)e.Item.Cells[0].FindControl("lblIndex");
TextBox txtName =(TextBox)e.Item.Cells[1].FindControl("txtName");
TextBox txtDesc = (TextBox)e.Item.Cells[2].FindControl("txtDescription");

string select = "index = " + lblIndex.Text;
DataRow[] rows = tb.Select(select);
if(rows.Length > 0)
{
rows[0]["name"] = txtName.Text;
rows[0]["description"] = txtDesc.Text;
}

dgMain.EditItemIndex = -1;

BindGrid();
}

private void dgMain_CancelCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
dgMain.EditItemIndex = -1;
BindGrid();
}
}



In the meantime, if you feel my suggestion not quite suitable for you.
Please feel free to provide some more infos on your requirement, I'll be
willing to help you.



Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
K

Karine Proot

Thanks for your fast reply. Indeed, I need to explain a bit more..

----- Steven Cheng[MSFT] wrote: ----
Hi Karine
Thanks for posting in the community!
From your description, you'd like to add bind a webform DataGrid with some
datas. The datagrid has two command button column which contain the Edit
and Delete button. Also, when the edit button is clicked, the current edit
row's data will be displayed in textboxes so as for being edited. In
addition, you'd like to dynamically create a BoundColumn and add it into
the DataGrid and found it can't remain value when the page is posted back,
yes

So far, that's it
As for this problem, I'd like to confirm with some further things
1. Do you only use BoundColumns in the DataGrid?

So far, yes, but I may try some TemplateColumns later. Let's assume there are only BoundColumns, as I would like it to work this way first
2. Why do you have to dynamically add the BoundColumn rather than add it in
the static page template

I want to be able, on the same webform with one DataGrid, to bind this DataGrid with any data source. In fact, the webform will display a list of data sources you can click on, and clicking on one of those data source (by data source, I mean only a LinkButton, which text is supposed to be self-explanatory, eg: "Books", "Authors", "Vendors", ...) will populate the DataGrid with it (this is already done, my database was built in order to do this : I can retrieve an SQL query from it which depends on the LinkButton clicked, and that query will allow me to build the DataGrid's datasource). So, depending on the LinkButton clicked, the number of BoundColumns needed will change. That's why I need to add the BoundColumn dynamically (the DataField and HeaderText properties are also retrieved from my database)

I hope I made myself clear... and maybe my solution is not the right one. If you need it, I could post all the code from this webform
3. Have you used Template columns?
Based on my experience, if you'd like to provide the Editing data and
update functions as I mentioned above, you need to use Template columns.
The BoundColumn could only provide binding and display function. It won't
automatidally provide TextBox for editing the data when you clicked Edit
button of the DataGrid

I don't follow you there. So far, for simpler DataGrids, I use only BoundColumns and clicking on Edit will provide TextBoxes through the usual function
public void Grid_Edit (Object sender, DataGridCommandEventArgs e)

myDataGrid.EditItemIndex = e.Item.ItemIndex
BindGrid()

By the way, clicking on Edit in my DataGrid with dynamic BoundColumns do provide TextBoxes
The template column has serveral Template such as
"ItemTemplate", "EditTemplate", which can help you define the Display style
and Edit time style of the DataRow in DataGrid.

The problem would be the same with a TemplateColumn, I guess : if I try to add it dynamically, it will not be in the DataGridColumnCollection when I click on Update
In the meantime, if you feel my suggestion not quite suitable for you.
Please feel free to provide some more infos on your requirement, I'll be
willing to help you

Steven Chen
Microsoft Online Suppor


Thanks
Karine Proot
 
S

Steven Cheng[MSFT]

Hi Karine,


Thanks for your followup and your further information on this issue. I've
completely got your ideas now. As for the problem that add Columns to
DataGrid dynamically so as to meet with the different DataTable(am I
right?), here are my suggestions:

In ASP.NET webpage, all the dynamically control should be added into page's
controls collection or its parent control's controls collection everytime
the page is loaded so that it can be remained in the page's or parent
control's control stucture so as to properly reload its viewstate and
rebuild its state. And on way to create it is in the Page_Init event, when
the page's controls structure is building and the viewstate haven't been
loaded. And here is a KB article on this topic, you may have a view if you
feel necessary:

#HOW TO: Dynamically Create Controls in ASP.NET by Using Visual C# .NET
http://support.microsoft.com/?id=317794

As for our situation in this issue, since you want to dynamically bind
different DataTable and dynamially add columns into DataGrid according to
the Different Table Link user has selected, I don't thinks add columns in
Page_Init will be the proper way. However, I've another suggestion that we
add enough columns into the DataGrid at first time the page is loaded,
since we can dynamically set the each column's Visible property to show and
hide it, then when different DataTAble is selected and bind to the
DataGrid, we dynamically set the proper number of columns's Visible as
true(also set their HeaderText and DataField ) and set the other columns'
Visible as false. How do you think of this way?

In addition, I've made an sample page using the above suggestion, here is
the page code:
-----------------------------aspx
page---------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>MultiGrid</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<table align="center" width="500">
<tr>
<td>
<table width="100%" align="center">
<tr>
<td>
<asp:LinkButton id="LinkButton1"
runat="server">Table1</asp:LinkButton>
</td>
<td>
<asp:LinkButton id="LinkButton2"
runat="server">Table2</asp:LinkButton>
</td>
<td>
<asp:LinkButton id="LinkButton3"
runat="server">Table3</asp:LinkButton>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<asp:DataGrid id="dgMain" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundColumn></asp:BoundColumn>
<asp:BoundColumn></asp:BoundColumn>
<asp:BoundColumn></asp:BoundColumn>
<asp:BoundColumn></asp:BoundColumn>
<asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Update"
HeaderText="Edit Operation" CancelText="Cancel"
EditText="Edit"></asp:EditCommandColumn>
<asp:ButtonColumn Text="Delete" HeaderText="Delete Operation"
CommandName="Delete"></asp:ButtonColumn>
</Columns>
</asp:DataGrid></td>
</tr>
<tr>
<td><INPUT id="hdTableIndex" type="hidden" value="0"
name="hdTableIndex" runat="server"></td>
</tr>
</table>
</form>
</body>
</HTML>

---------------------code behind page class---------------------------
public class MultiGrid : System.Web.UI.Page
{
protected System.Web.UI.WebControls.LinkButton LinkButton1;
protected System.Web.UI.WebControls.LinkButton LinkButton3;
protected System.Web.UI.WebControls.LinkButton LinkButton2;
protected System.Web.UI.HtmlControls.HtmlInputHidden hdTableIndex;
protected System.Web.UI.WebControls.DataGrid dgMain;

private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
LoadData();
BindGrid(0);
}
}

protected void LoadData()
{
DataSet ds = new DataSet();


DataTable tb1 = new DataTable("tb1");
tb1.Columns.Add("index");
tb1.Columns.Add("name");

DataTable tb2 = new DataTable("tb2");
tb2.Columns.Add("index");
tb2.Columns.Add("name");
tb2.Columns.Add("description");

DataTable tb3 = new DataTable("tb3");
tb3.Columns.Add("index");
tb3.Columns.Add("name");
tb3.Columns.Add("description");
tb3.Columns.Add("state");

for(int i=1;i<=10;i++)
{

DataRow newrow1 = tb1.NewRow();
newrow1["index"] = i.ToString();
newrow1["name"] = "TB1_Name_" + i.ToString();
tb1.Rows.Add(newrow1);

DataRow newrow2 = tb2.NewRow();
newrow2["index"] = i.ToString();
newrow2["name"] = "TB2_Name_" + i.ToString();
newrow2["description"] = "TB2_Description_" + i.ToString();
tb2.Rows.Add(newrow2);

DataRow newrow3 = tb3.NewRow();
newrow3["index"] = i.ToString();
newrow3["name"] = "TB3_Name_" + i.ToString();
newrow3["description"] = "TB3_Description_" + i.ToString();
newrow3["state"] = "TB3_State_" + i.ToString();
tb3.Rows.Add(newrow3);
}

ds.Tables.Add(tb1);
ds.Tables.Add(tb2);
ds.Tables.Add(tb3);

Session["TEMP_DATA"] = ds;
}



protected void BindGrid(int tableindex)
{
DataSet ds = (DataSet)Session["TEMP_DATA"];
DataTable tb = ds.Tables[tableindex];

for(int i=0;i<dgMain.Columns.Count-2;i++)
{
if(i<tb.Columns.Count)
{
BoundColumn bcol = (BoundColumn)dgMain.Columns;
bcol.HeaderText = tb.Columns.ColumnName;
bcol.DataField = tb.Columns.ColumnName;
bcol.Visible = true;

}
else
{
BoundColumn bcol = (BoundColumn)dgMain.Columns;
bcol.HeaderText = "";
bcol.DataField = "";
bcol.Visible = false;
}
}

dgMain.DataSource = tb;
dgMain.DataBind();

}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dgMain.CancelCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_CancelComm
and);
this.dgMain.EditCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_EditComman
d);
this.dgMain.UpdateCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_UpdateComm
and);
this.dgMain.DeleteCommand += new
System.Web.UI.WebControls.DataGridCommandEventHandler(this.dgMain_DeleteComm
and);
this.LinkButton1.Click += new
System.EventHandler(this.LinkButton1_Click);
this.LinkButton2.Click += new
System.EventHandler(this.LinkButton2_Click);
this.LinkButton3.Click += new
System.EventHandler(this.LinkButton3_Click);
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion

private void dgMain_CancelCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
dgMain.EditItemIndex = -1;
int tableindex = int.Parse(hdTableIndex.Value);
BindGrid(tableindex);

}

private void dgMain_DeleteCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
dgMain.EditItemIndex = -1;
int tableindex = int.Parse(hdTableIndex.Value);
BindGrid(tableindex);
}

private void dgMain_EditCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
dgMain.EditItemIndex = e.Item.ItemIndex;
int tableindex = int.Parse(hdTableIndex.Value);
BindGrid(tableindex);
}

private void dgMain_UpdateCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
dgMain.EditItemIndex = -1;
int tableindex = int.Parse(hdTableIndex.Value);
BindGrid(tableindex);
}

private void LinkButton1_Click(object sender, System.EventArgs e)
{
int tableindex = int.Parse(hdTableIndex.Value);
if(tableindex != 0)
{
tableindex = 0;
hdTableIndex.Value = tableindex.ToString();
BindGrid( tableindex);
}
}

private void LinkButton2_Click(object sender, System.EventArgs e)
{
int tableindex = int.Parse(hdTableIndex.Value);
if(tableindex != 1)
{
tableindex = 1;
hdTableIndex.Value = tableindex.ToString();
BindGrid(tableindex);
}
}

private void LinkButton3_Click(object sender, System.EventArgs e)
{
int tableindex = int.Parse(hdTableIndex.Value);
if(tableindex != 2)
{
tableindex = 2;
hdTableIndex.Value = tableindex.ToString();
BindGrid(tableindex);
}
}
}

----------------------------------------------------------------------------
--

Please check out my suggestions. If you feel anything unclear , please feel
free to let me know.



Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
S

Steven Cheng[MSFT]

Hi Karine,


Thanks for your feedback. I'm very glad that my suggestion has helped you.
As for the TemplateColumns are behind the BoundColumns. I think you can try
adjust the columns's position ,swap their positions in the DataGrid.Columns
collections so as to manually adjust the columns's display index. Anyway,
please always feel free to post here if you need any help.



Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top