Ok, I never used a datalist, but it sure looks like a NICE alternative to the repeater.
So, say we have this markup:
<asp:DataList ID="DataList1" runat="server" DataKeyField="ID" >
<ItemTemplate>
<div style="border-style:solid;color:black;width:300px;margin-left:25px">
<div style="padding:5px;text-align:right">
<p>Hotel Name: <asp:TextBox ID="HotelName" runat="server" Text ='<%# Eval("HotelName") %>' /></p>
<p>First Name: <asp:TextBox ID="FirstName" runat="server" Text ='<%# Eval("FirstName") %>' /></p>
<p>Last Name: <asp:TextBox ID="LastName" runat="server" Text ='<%# Eval("LastName") %>' /></p>
<p>City: <asp:TextBox ID="City" runat="server" Text ='<%# Eval("City") %>' /></p>
<p>Province: <asp:TextBox ID="Province" runat="server" Text ='<%# Eval("Province") %>' /></p>
Active: <asp:CheckBox ID="Active" runat="server" Checked = '<%# Eval("Active") %>'/>
<br />
<asp:Button ID="cmdThisRow" runat="server" Text="Row click This item"
OnClick="cmdThisRow_Click"/>
</div>
</div>
<div style="height:15px"></div>
</ItemTemplate>
</asp:DataList>
Ok, and then our code to fill:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
DataList1.DataSource = MyRst("SELECT TOP 3 * from tblHotels ORDER BY HotelName");
DataList1.DataBind();
}
public DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection con = new SqlConnection(Properties.Settings.Default.TEST3))
using (SqlCommand cmdSQL = new SqlCommand(strSQL, con))
{
con.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
return rstData;
}
Ok, and now we have this:

So we have two use cases. A button outside of the control - process ALL records.
That code/loop would be like this:
protected void Button1_Click(object sender, EventArgs e)
{
foreach (DataListItem gRow in DataList1.Items)
{
Debug.Print("Row index = " + gRow.ItemIndex.ToString());
int pkID = (int)(DataList1.DataKeys[gRow.ItemIndex]);
Debug.Print("Data base PK id = " + pkID.ToString());
Debug.Print("Hotel name = " + ((TextBox)(gRow.FindControl("HotelName"))).Text);
Debug.Print("City = " + ((TextBox)(gRow.FindControl("City"))).Text);
Debug.Print("--------------------");
}
}
OutPut:
Row index = 0
Data base PK id = 20187
Hotel name = Albert Royal Hotel
City = Edmotnon
--------------------
Row index = 1
Data base PK id = 78
Hotel name = Athabasca Hotel
City = Kelowna
--------------------
Row index = 2
Data base PK id = 99
Hotel name = Banff Airporter
City = Kimberley
--------------------
Ok, now lets wire up the button inside of the datalist.
we can't double click on the button to create the event, but we can do this:
We use intel-sense, and when you in markup type in OnClick= then we see this:

so, we choose create new event. Does not seem like anything occurred, but flip to code behind, and you see the code stub:
The code to get/use the click on the one row is this:
protected void cmdThisRow_Click1(object sender, EventArgs e)
{
Button btn = (Button)sender;
DataListItem gRow = (DataListItem)btn.Parent ;
Debug.Print("Row index = " + gRow.ItemIndex.ToString());
int pkID = (int)(DataList1.DataKeys[gRow.ItemIndex]);
Debug.Print("Data base PK id = " + pkID.ToString());
Debug.Print("Hotel name = " + ((TextBox)(gRow.FindControl("HotelName"))).Text);
Debug.Print("City = " + ((TextBox)(gRow.FindControl("City"))).Text);
Debug.Print("--------------------");
}
thus, we now have both a loop for a button outside of the Datalist to process each row.
And we have a button inside of the Datalist to get/process the one row we clicked on.
do keep in mind that you can use the built in events of this control. They quite much follow listview/gridview etc in which you can have the indexchanged and other events fire - but often the above trick to just drop in a button, use btn.parent to get the row and work from that is less work and effort.
However, even if you decide to use the built in events for the DataList, the use of findcontrol as per above is how you pluck out the controls, and then ultimatly the values from those controls.
So, you can't reference the controls directly, since the whole "thing" is being repeated over and over - hence you get the row, and THEN get the values out of that row.
Edit: =================================================
So, the user is wondering how to send the edits back to the database?
Well, as noted, we dumped the world poverty mess of using extra templates.
so, if we wanted to be able to freely edit each of the repeated rows?
Then do this, as then ONE SINGLE update command to the database will update all changes we have.
so right below the data list lets add 2 buttons (save, and un-do).
</ItemTemplate>
</asp:DataList>
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" Width="120px" />
<asp:Button ID="cmdUnDo" runat="server" Text="Undo edits" CssClass="btn" Width="120px"
style="padding-left:25px"/>
<br />
<br />
So, now our load code becomes this (we persit the data table).
DataTable rstData;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadGrid();
ViewState["MyData"] = rstData;
}
else
rstData = (DataTable)ViewState["MyData"];
}
void LoadGrid()
{
string strSQL;
strSQL = "SELECT TOP 3 * FROM tblHotels ORDER BY HotelName";
rstData = MyRst(strSQL);
DataList1.DataSource = rstData;
DataList1.DataBind();
}
Note HOW we are persiting the data table here!!!
Now, to save ALL edits in one shot?
We send the data list control BACK to table, and THEN execute a single database update on that table like this:
void GridToTable()
{
// pull datalist rows back to table.
foreach (DataListItem rRow in DataList1.Items)
{
int RecordPtr = rRow.ItemIndex;
DataRow OneDataRow;
OneDataRow = rstData.Rows[RecordPtr];
OneDataRow["FirstName"] = ((TextBox)rRow.FindControl("FirstName")).Text;
OneDataRow["LastName"] = ((TextBox)rRow.FindControl("LastName")).Text;
OneDataRow["City"] = ((TextBox)rRow.FindControl("City")).Text;
OneDataRow["Province"] = ((TextBox)rRow.FindControl("Province")).Text;
OneDataRow["HotelName"] = ((TextBox)rRow.FindControl("HotelName")).Text;
OneDataRow["Active"] = ((CheckBox)rRow.FindControl("Active")).Checked;
}
}
protected void cmdSave_Click(object sender, EventArgs e)
{
GridToTable();
// now send table back to database with updates
string strSQL;
strSQL = "SELECT * FROM tblHotels WHERE ID = 0";
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
SqlDataAdapter daupdate = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder cmdBuild = new SqlCommandBuilder(daupdate);
daupdate.Update(rstData);
}
}
So we go:
Controls back to table,
Then table back to database. Now I did not wire up the un-do button, but it would simple call LoadGrid again (and hence a un-do all edits).
Ok, so now we have this:

So you are free to tab around - change any of the above displayed.