Ok, unless you crossed the desert, climbed mountains, exhausted every "reasonable" alternative?
You have to ask WHY you want to write code to inject a button when THAT is the WHOLE idea of quite near EVERY control from repeater, listview, Gridview and more!
In other words, the need for writing code code, and a GOOD strong case has not been made yet.
and why do we need a huge spectacular earth shaking narrative as to why the approach is being used?
Because in 99% of cases, you don't need to do this, and the data aware controls and darn near the WHOLE asp.net system is designed around you NOT having to take that road.
So, if you have some button, or whatever? Just drop it into the repeater control, and it will "repeat" over and over for you!!!
So, say this simple repeater:
<asp:Repeater ID="Repeater1" runat="server" >
<ItemTemplate>
<div style="border-style:solid;color:black;width:320px;height:450px;float:left;margin-right:10px;margin-bottom:10px">
<div style="text-align:center;padding:2px 10px 2px 10px" class="cards" >
<asp:Button ID="cmdMyView" runat="server" Text="View"
CssClass="btn-info" style="float:right"
CommandArgument = '<%# Eval("ID") %>'
OnClick="cmdMyView_Click" />
<br />
<h3 id="hFighter" runat="server"><%# Eval("Fighter") %></h3>
<asp:Image ID="Image2" runat="server"
ImageUrl = '<%# Eval("ImagePath") %>' Width="170" />
<h4>Engine</h4>
<asp:Label ID="EngineLabel2" runat="server" Text='<%# Eval("Engine") %>' />
<h4>Description</h4>
<asp:Label ID="DescLabel" runat="server" Text='<%# Eval("Description") %>' />
<br />
</div>
</div>
</ItemTemplate>
</asp:Repeater>
Note the button and the plain jane click event for that button.
So, to fill the repeater, we have this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadData()
End If
End Sub
Sub LoadData()
Using conn As New SqlConnection(My.Settings.TEST4)
Dim strSQL As String =
"SELECT * FROM Fighters"
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
Dim rstData As New DataTable
rstData.Load(cmdSQL.ExecuteReader)
Repeater1.DataSource = rstData
Repeater1.DataBind()
End Using
End Using
End Sub
And now we see/get this:

And now that button click from above (the view button).
Protected Sub cmdMyView_Click(sender As Object, e As EventArgs)
Dim btn As Button = sender
Dim rRow As RepeaterItem = btn.NamingContainer
Debug.Print("Row index = " & rRow.ItemIndex)
Debug.Print("DATA ID pk = " & btn.CommandArgument)
Dim hFighter As HtmlGenericControl = rRow.FindControl("hFighter")
Debug.Print("Figher name = " & hFighter.InnerText)
End Sub
output:
Row index = 3
DATA ID pk = 4
Figher name = Lockheed Martin F-35 Lightning II
So, note how the the simple button click picks up the current row of data.
From that, we can use find control, or get the "row index" of the click, or in our case, we also included the database PK id in the button command arugment.
So, VERY hard to make a case to do all those "hand stands" to write code to "inject" a button when darn near ALL of the data aware controls will repeat the data AND the controls for you over and over with great ease, and in fact ZERO code to have such buttons or controls repeat for you.
there are RARE cases to write code to inject, but they should be the last resort, since in 99% of cases, no such code is required, and worse yet, when you post-back, such injected controls will NOT persist, and you have to re-inject on every post back for such pages to work.
Edit: So, this could be used
markup:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<i id="iHotel" runat="server"><%# Eval("HotelName") %></i>
<br />
<asp:CheckBox ID="chkREL" runat="server"
Checked='<%# Eval("Status").ToString() == "Rel" ? true : false %>' />
<br />
<asp:Button ID="Button1" runat="server" Text="Button"
OnClick="Button1_Click" />
<hr />
</ItemTemplate>
</asp:Repeater>
Say code to load:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadData();
}
}
void LoadData()
{
Repeater1.DataSource = General.MyRst("SELECT * FROM tblHotelsA");
Repeater1.DataBind();
}
result is this:

Button click:
protected void Button1_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
RepeaterItem iRow = (RepeaterItem)btn.NamingContainer;
Debug.Print($"Row click index = {iRow.ItemIndex}");
CheckBox chkRel = (CheckBox)iRow.FindControl("chkREL");
Debug.Print($"Check box checked = {chkRel.Checked}");
HtmlGenericControl iHotel = (HtmlGenericControl)iRow.FindControl("iHotel");
Debug.Print($"Hotel name from 'p' item = {iHotel.InnerText}");
}
output:
Row click index = 1
Check box checked = True
Hotel name from 'p' item = Ramada Lodge