I'm making a simple form. I have Default.aspx, Default.aspx.vb, and Default.aspx.designer.vb.
Default.aspx.vb contains a Public jsonItems As List(Of JsonItem)
, where a JsonItem
is just an object with a few strings defined, like Id
, Label
, and Type
. This is generated by Page_Load
, which reads a Json file to tell the page the form's contents.
Then, Default.aspx contains:
<ul>
<%For Each item In jsonItems%>
<li>
...
<ol>
<asp:TextBox ID="<%=item.Label%>" runat="server"></asp:TextBox>
</ol>
</li>
<%Next%>
</ul>
This, however, doesn't work as I would expect it to. I believe this is because the Designer page wants to know exactly what form elements will be on the page... It autogenerates Protected WithEvents <%=item.label%> As ...
in the designer page, and complains that it isn't a valid name.
Is there a way to do what I'm trying to do? Should I be building the entire form in the.. code-behind (Default.aspx.vb
) instead of what I'm doing now?
For the record, I'm used to working in React, which is why I initially took this direction (I wrote it first in JS and I was adapting it to VB).
CodePudding user response:
Injection of asp.net controls into a page does not tend to work very well at all.
You can inject HTML, but then again , you now writing your own code anyway, and again that not a huge help.
As a general approach?
Should I be building the entire form in the.. code-behind
No, you don't want to write that much code. You lay out the markup with the designer - code behind is to pull data say to fill out those existing controls.
I mean, you have to define that markup some place. But with web forms, the general idea is that you drop in the controls you want.
Of course, there is a boat load of controls, and many are data aware. So, the trick, the idea, the goal?
Well, with so many control choices, it often hard to pick a control.
So, for a table like layout, then GridView. For fancy table layout, then ListView.
For repeating records - say like a "card view", then datalist works well.
so in webforms land? You tend to layout the markup with the desinger.
Then you use code behind to fill out the data, or whatever.
So, say I drop in a Gridview, like this:
<asp:GridView ID="GHotels" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chkActive" runat="server" Checked='<%# Eval("Active") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Hotel Information" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="Info" CssClass="btn"
OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
I did use the wizard to help me create the GV. (but then I removed the sql datasource that was created on the page).
So, now my code to say load up the grid would be this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
LoadGrid
End If
End Sub
Sub LoadGrid()
Dim rstData As DataTable
rstData = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName")
GHotels.DataSource = rstData
GHotels.DataBind()
End Sub
Public Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
rstData.TableName = strSQL
End Using
End Using
Return rstData
End Function
And I get this:
Note how I did set the GV to CssClass="table". (that is the boot strap class - so you get a nice looking gird).
And I have a button - edit that row.
Again, just a plain jane button click for that row.
So, right below the Grid, I could drop in a div, and some controls to display/edit that one row.
Say, like this:
<div id="HotelInfo" runat="server"
style="float:left;display: none;border:solid;padding:15px;background-color:white;width:48%"
clientidmode="Static" >
<style>
.iForm label {display:inline-block;width:120px}
.iForm input {border-radius:8px;border-width:1px;margin-bottom:10px}
.iForm textarea {border-radius:8px;border-width:1px;margin-bottom:10px}
.iForm input[type=checkbox] {margin-right:4px}
</style>
<div style="float:left" >
<label>HotelName</label><asp:TextBox ID="txtHotel" runat="server" width="280"></asp:TextBox> <br />
<label>First Name</label><asp:TextBox ID="txtFirst" runat="server" width="280"></asp:TextBox> <br />
<label>Last Name</label><asp:TextBox ID="txtLast" runat="server" width="280"></asp:TextBox> <br />
<label>City</label><asp:TextBox ID="tCity" runat="server" Width="140"></asp:TextBox> <br />
<label>Province</label><asp:TextBox ID="tProvince" runat="server" Width="75"></asp:TextBox> <br />
</div>
<div style="float:left;margin-left:20px" >
<label>Description</label> <br />
<asp:TextBox ID="txtNotes" runat="server" Width="450" TextMode="MultiLine"
Height="150px" ></asp:TextBox> <br />
<asp:CheckBox ID="chkActive" Text=" Active" runat="server" TextAlign="Right" />
<asp:CheckBox ID="chkBalcony" Text=" Has Balcony" runat="server" TextAlign="Right" />
<asp:CheckBox ID="chkSmoking" Text="Smoking Rooms" runat="server" TextAlign="Right" />
</div>
<div style="clear:both">
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" />
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" CssClass="btn"
style="margin-left:20px" OnClientClick="$('#HotelInfo').dialog('close');return false;"
/>
<asp:Button ID="cmdDelete" runat="server" Text="Delete" CssClass="btn"
style="margin-left:40px"
/>
</div>
</div>
And, since we all ahve jquery instaleld, I added jquery.UI.
So right after above, I have this:
<script>
function popinfo(pbtn, strHotelName) {
// btn = row buttion - we use that for position of pop
btn = $(pbtn)
myDialog = $("#HotelInfo")
myDialog.dialog({
title: strHotelName,
modal: true,
position: { my: "left top", at: "right bottom", of: btn },
width: "48%",
appendTo: 'form'
})
}
</script>
So, now our row click event:
Protected Sub cmdView_Click(sender As Object, e As EventArgs)
' Row click - get hotel informaton, fill out the pop div
Dim btn As Button = sender
Dim gRow As GridViewRow = btn.NamingContainer
Dim intPK As Integer = GHotels.DataKeys(gRow.RowIndex).Item("ID")
Dim rstData As DataTable = MyRst("SELECT * FROM tblHotelsA WHERE ID = " & intPK)
Call EditOne(rstData, btn)
End Sub
Sub EditOne(rstData As DataTable, btn As Button)
With rstData.Rows(0)
txtHotel.Text = .Item("HotelName").ToString
txtFirst.Text = .Item("FirstName").ToString
txtLast.Text = .Item("LastName").ToString
tCity.Text = .Item("City").ToString
tProvince.Text = .Item("Province").ToString
chkActive.Checked = .Item("Active")
chkBalcony.Checked = .Item("Balcony")
chkSmoking.Checked = .Item("Smoking")
txtNotes.Text = .Item("Description").ToString
End With
' ok, div information filled out, now call our js pop function.
Dim strHotelName = """Edit Information for " & rstData.Rows(0).Item("HotelName") & """"
Dim strJavaScript As String = "popinfo(" & btn.ClientID & "," & strHotelName & ");"
ViewState("rstData") = rstData
Page.ClientScript.RegisterStartupScript(Me.GetType(), "My Pop script", strJavaScript, True)
End Sub
So, now we see/get this when we click on a row button:
the save button in above, say this code:
Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
Dim rstData As DataTable = ViewState("rstData")
With rstData.Rows(0)
.Item("HotelName") = txtHotel.Text
.Item("FirstName") = txtFirst.Text
.Item("LastName") = txtLast.Text
.Item("City") = tCity.Text
.Item("Province") = tProvince.Text
.Item("Active") = chkActive.Checked
.Item("Balcony") = chkBalcony.Checked
.Item("Smoking") = chkSmoking.Checked
.Item("Description") = txtNotes.Text
End With
Using conn As New SqlConnection(My.Settings.TEST4)
Using cmdSQL As New SqlCommand("SELECT * FROM tblHotelsA", conn)
conn.Open()
Dim da As New SqlDataAdapter(cmdSQL)
Dim daU As New SqlCommandBuilder(da)
da.Update(rstData)
End Using
End Using
LoadGrid()
End Sub
So, the above should give you some ideas here.
I actually became tired of wirting the same code to send a row of data to controls on a page, so I wrote a simple routine to do that for me. Same goes for saving the data back to the table.
so, with a few helper routines, then you quite much just drag drop to add controls to a page - and then a few helper routines.
So, my load code, and save code to load up controls? that is now a general routine and I thus don't even hand code that anymore.
I adopted a extra attribuote that I add to controls.
So, my markup becomes say this:
<div style="float:left" >
<label>HotelName</label>
<asp:TextBox ID="txtHotel" runat="server" width="280" f="HotelName" ></asp:TextBox> <br />
<label>First Name</label>
<asp:TextBox ID="txtFirst" runat="server" width="280" f="FirstName" ></asp:TextBox> <br />
<label>Last Name</label>
<asp:TextBox ID="txtLast" runat="server" width="280" f="LastName"></asp:TextBox> <br />
So, in palce of those multiple lines of code to load up the controls, I now can do this:
This:
Sub EditOne(rstData As DataTable, btn As Button)
With rstData.Rows(0)
txtHotel.Text = .Item("HotelName").ToString
txtFirst.Text = .Item("FirstName").ToString
txtLast.Text = .Item("LastName").ToString
tCity.Text = .Item("City").ToString
tProvince.Text = .Item("Province").ToString
chkActive.Checked = .Item("Active")
chkBalcony.Checked = .Item("Balcony")
chkSmoking.Checked = .Item("Smoking")
txtNotes.Text = .Item("Description").ToString
End With
' ok, div information filled out, now call our js pop function.
Dim strHotelName = """Edit Information for " & rstData.Rows(0).Item("HotelName") & """"
becomes:
Call fLoader(HotelInfo, rstData.Rows(0))
So, now, it one line of code to load 5 or 25 controls with the data.
I can post that routine.
But, all in all?
You don't really write code to create the markup. But, code behind supports the data operations of what you want to do against the controls.
So, some kind of on-the fly generation of markup? Web forms not the best choice for that kind of design approach - it just not.
so, it rather difficult to generate controls and a id, but worse as you note, you have even more difficult time adding events to such controls.