I need to add a list of names and (optional) links to my ASP.net page.
I learnt (hopefully) to use a GridView to do this, mostly working from the example given by this blog entry: adding-dynamic-rows-in-gridview.
I've added a GridView to my frontend:
<asp:GridView
id="NameLinksGridView" runat="server"
AutoGenerateColumns="false" ShowFooter="true">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:TextBox ID="Name" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Link">
<ItemTemplate>
<asp:TextBox ID="Link" runat="server" />
</ItemTemplate>
<FooterStyle HorizontalAlign="Right" />
<FooterTemplate>
<asp:Button
ID="addNameLink" runat="server"
Text="Add" onclick="addNameLink_Click"
CausesValidation="false" />
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
With the code behind:
private void setupInitialNameLinks()
{
DataTable dt = getNameLinksTable();
/* store DataTable in the ViewState for preservation across PostBack's */
ViewState["CurrentTable"] = dt;
/* bind the displayed DataGrid to our DataTable */
NameLinksGridView.DataSource = dt;
NameLinksGridView.DataBind();
/* set current TextBox values to the values in the DataTable
* surely the DataBind should do this for us?!? */
setTextBoxValuesToDataTableValues(dt);
}
private DataTable getNameLinksTable()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Name", typeof(string)));
dt.Columns.Add(new DataColumn("Link", typeof(string)));
var existingNameLinks = getExistingNameLinks();
if (existingNameLinks.Count() > 0)
{
foreach (var auth in existing)
{
DataRow r = dt.NewRow();
r["Name"] = auth.name;
r["Link"] = auth.link;
dt.Rows.Add(r);
}
}
else
{
DataRow emptyRow = getNameLinksEmptyRow(dt);
dt.Rows.Add(emptyRow);
}
return dt;
}
private static DataRow getNameLinksEmptyRow(DataTable dt)
{
DataRow emptyRow = dt.NewRow();
emptyRow["Name"] = String.Empty;
emptyRow["Link"] = String.Empty;
return emptyRow;
}
protected void addNameLink_Click(object sender, EventArgs e)
{
if (ViewState["CurrentTable"] != null)
{
DataTable dt = (DataTable) ViewState["CurrentTable"];
DataRow dr = getNameLinksEmptyRow(dt);
dt.Rows.Add(dr);
/* update our DataTable to the POST'ed data */
int numPostedRows = dt.Rows.Count - 1;
for (int i = 0; i < numPostedRows; i++)
{
TextBox tbName =
(TextBox)NameLinksGridView.Rows[i].Cells[0].FindControl("Name");
string name = tbName.Text; // get POST'ed value
dt.Rows[i]["Name"] = name; // set DataTable val to the POST'ed value
TextBox tbLink =
(TextBox)NameLinksGridView.Rows[i].Cells[1].FindControl("Link");
string link = tbLink.Text;
dt.Rows[i]["Link"] = link;
}
/* store our DataTable in the ViewState for preservation across PostBack */
ViewState["CurrentTable"] = dt;
/* bind the displayed DataGrid to our DataTable */
NameLinksGridView.DataSource = dt;
NameLinksGridView.DataBind();
/* set current TextBox values to the values in the DataTable
* needs to happen after the DataBind()
* surely the DataBind should do this for us?!? */
setTextBoxValuesToDataTableValues(dt);
}
else
{
throw new Exception("CurrentTable is null!");
}
}
private void setTextBoxValuesToDataTableValues(DataTable dt)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
TextBox tbName = (TextBox)NameLinksGridView.Rows[i].Cells[0].FindControl("Name");
tbName.Text = dt.Rows[i]["Name"].ToString();
TextBox tbLink = (TextBox)NameLinksGridView.Rows[i].Cells[1].FindControl("Link");
tbLink.Text = dt.Rows[i]["Link"].ToString();
}
}
Does any of this code look troublesome, prone to error, umnaintanable, not the right way of doing it, etc?
One big bit of confusion I have (even though the code works) — why do I need to store the DataTable in the ViewState manually and set my TextBox displayed values manually (in setTextBoxValuesToDataTableValues
)? Surely the GridView state should be maintained across the POST without me needing to worry about the ViewState and the DataBind should set the TextBox values automatically?