Creating Webform IDs dynamically within loop - asp.net

New to asp.net web forms and I'm having a problem figuring out how to control variables within the loops in my HTML. For Example
<%for (int j = 0; j < Model.Route.Length; j++) { %>
<div class="row">
<hr />
<h3 class="col-lg-12">Route
<%=j %>
</h3>
<div class="row col-lg-12">
<asp:Label ID="Source1_Route1_ID_Label" runat="server" CssClass="col-lg-5">ID</asp:Label>
<asp:TextBox ID="Source1_Route1_ID_Input" runat="server" CssClass="col-lg-6"></asp:TextBox>
</div>
<div class="row col-lg-12">
<asp:Label ID="Source1_Route1_Input_Label" runat="server" CssClass="col-lg-5">Input</asp:Label>
<asp:TextBox ID="Source1_Route1_Input_Input" runat="server" CssClass="col-lg-6"></asp:TextBox>
</div>
<div class="row col-lg-12">
<asp:Label ID="Source1_Route1_Output_Label" runat="server" CssClass="col-lg-5">Output</asp:Label>
<asp:TextBox ID="Source1_Route1_Output_Input" runat="server" CssClass="col-lg-6"></asp:TextBox>
</div>
</div>
<% } %>
<!-- Start Wrapping Routes Rows -->
The above code looks at an array and makes repeats this section for each instance in the array. The problem is that I need control over each of the instances, but right now if in the code behind section I call Source1_Route1_ID.Input.Text = "TEST" It will change all created instances to "TEST". I wanted to create them like <asp:TextBox ID="Source1_Route<%=j%>_ID_Input> but it's throwing an error saying I can't use <%= %> In my ID. Is there an easier way to solve this problem?

In most cases, you don't need or want a loop.
And in most cases, you don't need to inject/put the code in the markup
So, you want to "repeat" somthing over and over?
And then how do you wire up click events etc.? (huge pain point).
So, in webforms land, unlike PHP etc. that inter-mingle code,and use code to spit out markup?
In the VAST majority of cases, with webforms you don't take that design patter approach. More amazing, in 99% of cases you do not need to.
So, a bit of a "gear" shift is required with webforms, and the result is less effort, less code, and no looping in most cases is required.
So, you want to "repeat" something?
Then use a "repeater" (great name, don't you think???).
So,
Say this markup:
<asp:Repeater ID="repHotels" runat="server" >
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server" Width="200px"
Text='<%# Eval("HotelName") %>' Font-Size="X-Large" >
</asp:Label>
<asp:TextBox ID="txtDescription" runat="server"
Text='<%# Eval("Description") %>'
TextMode="MultiLine" Rows="4" Columns="40" Style="margin-left:20px" >
</asp:TextBox>
<asp:Button ID="cmdView" runat="server" Text="View Hotel" CssClass="btn"
style="margin-left:35px;margin-top:25px"
OnClick="cmdView_Click" CommandArgument=<%# Eval("ID") %>
/>
<div style="border-top: 1px solid black;margin-top:8px;margin-bottom:8px"></div>
</ItemTemplate>
</asp:Repeater>
So, you put the markup you want to repeat inside of the item template.
thus, code to fill is this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadData();
}
void LoadData()
{
SqlCommand cmdSQL =
new SqlCommand("SELECT * FROM tblHotelsA ORDER BY HotelName");
repHotels.DataSource = MyRstP(cmdSQL);
repHotels.DataBind();
}
public DataTable MyRstP(SqlCommand cmdSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (cmdSQL)
{
cmdSQL.Connection = conn;
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
And now we get/see this:
but, above is NOT really the bonus part.
The button click MOST certainly is the Rossetta stone, since now we have a simple button, but it tied to the row of data.
So this button click code:
protected void cmdView_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
RepeaterItem gRow = (RepeaterItem)btn.NamingContainer;
Debug.Print("row index click = " + gRow.ItemIndex);
Debug.Print("PK passed by button = " + btn.CommandArgument);
Label txtHotel = (Label)gRow.FindControl("txtHotel");
Debug.Print("Value of Hotel = " + txtHotel.Text);
}
Output:
row index click = 1
PK passed by button = 5
Value of Hotel = Inns of Banff
It stands to reason, not "only" would one want to repeat data, but ALSO tie/have/enjoy/use some button click for each row of data.
Note also, do you see any looping??? (nope!!)
Do you see messy code inermixed with the markup? (nope!!!).
So, webforms requires a bit of a different kind of thinking, and design patter approach. Once you get the hang of webforms? You will have sorrowful symphany for how most other framework's function, since as above shows, you don't have to write much code to repeat data, and bonus points is you can simply drop in a button into that repeating data, and get the row information with great ease.

Related

Generating button in backend ASP Repeater and then finding the control in c#

So I create a button in the behind code using a function in an Repeater. And then, there's a button that should take these automatically generated buttons (if I check the checkbox in button A, I take the values of row A in repeater,same goes for button B and so on...).
I can see checking the code while debugging that the checkbox are generated correctly. So I should just be able to find it, but can not. Even tho I use the findControl but have not been able to do it.
Here is the code from the front:
<asp:Panel ID="mainPanel" runat="server">
<asp:Repeater ID="repStudents" runat="server">
....
<%# GetButton(Eval("Role").ToString()) %>
....
<asp:LinkButton runat="server" OnClick="showChosen" ClientIDMode="Static"
CausesValidation="true">Save</asp:LinkButton>
And here I generate the button and then try to show the chosen value:
protected string GetButton(string status)
{
string love="";
if(status == "Rel")
{
love = "<input id='relBtn' type='checkbox' runat='server'/>";
}
else
{
love = "<input id='rfBtn' checked type='checkbox' runat='server'/>";
}
return love;
}
protected void showChosen(object sender, EventArgs e)
{
CheckBox cb = (CheckBox)(mainPanel.FindControl("relBtn"));
if(cb.Checked)
lblError.Text = "Checkbox is checked";
else
lblError.Text = "Checkbox is not checked";
divError.Visible = true;
All I keep getting is the Null Reference Exception, even tho, there is just one relBtn in the whole page. If I look into the page generated code, I can see the relBtn, but for some reason, I can not find the Checkbox.
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

ASP.NET label expression

I use this syntax all the time in case of databinding.
<asp:Label ID="myLabel" Text='<%# display_labelText(eval("myValue")) %>' runat="server" />
So I thought it would be easy to use this outside databinding:
<asp:Label ID="myLabel" Text='<%= display_labelText("myValue") %>' runat="server" />
In this case, however, the literal text between the quotes is displayed, and not the intended function value. What is wrong here?
As noted, the "#" is ONLY for use inside of a databound control (listview, repeater, gridview etc.).
However, you CAN use databound expressions for a textbox or a label, but the amount of work is well, not worth it.
However, in place of a "#" (for data bound controls), you can use "=" for any public exposed function in code behind.
BUT ONLY FOR NON server controls!!!
So,say this public function in code behind:
This works:
<h2><%= MyHotel %></h2>
But, this does not:
<asp:TextBox ID="TextBox1" runat="server"
Text='<%= MyHotel %>'
></asp:TextBox>
(becuase TextBox is a server based control - YOU already have full use in code behind.
However, in place of that above expression, I could just as well do this:
<h2 id="myHeader" runat="server"></h2>
And thus in code behind, do this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
myHeader.InnerText = MyHotel()
End If
End Sub
So, any public function in the code behind, then use "=", but NOT for built in server side controls - those expression(s) don't work, but then again, you have full use of such controls in code behind anyway.
However, I do see "over and over" like a broken record people "hand coding" the filling out of controls. That makes VERY little sense in this day an age
Hand coding to fill out values from a database into a web page? That sets back the industry to pre-windows days, and before programs like FoxPro, Ms-access etc came out (in which you don't have to hand-code filling out of controls).
Before such programs, lots of "code" generators appeared. Again, kind of messy.
but, with web forms, gridview, listview, datalist view etc. in most cases can do a LOT of the dirty work for you.
However, I just wrote my own "loader" that simple repeats the code we type over and over for each page. I just "made up/cooked up" my own standard.
So, for each control, I just add a "f" attribute (means data colum field).
So, my code takes a given div, loops the controls, and shoves in the database values for me. You write that code one time, and then use it over and over.
So, then I can drop in a div of controls like this:
<div id="EditRecord" runat="server"
style="float: left; display: none" clientidmode="Static">
<br />
<div style="float: left" class="iForm">
<label>HotelName</label>
<asp:TextBox ID="txtHotel" runat="server" f="HotelName" Width="280">
</asp:TextBox>
<br />
<label>First Name</label>
<asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox>
<br />
<label>Last Name</label>
<asp:TextBox ID="tLN" runat="server" f="LastName" Width="140"></asp:TextBox>
<br />
<label>City</label>
<asp:TextBox ID="tCity" runat="server" f="City" Width="140"></asp:TextBox>
<br />
<label>Province</label><asp:TextBox ID="tProvince" runat="server" f="Province" Width="75"></asp:TextBox>
</div>
<div style="float: left; margin-left: 20px" class="iForm">
<div style="float: left">
<label>Description</label>
<br />
<asp:TextBox ID="txtNotes" runat="server" Width="260px" TextMode="MultiLine"
Height="100px" f="Description"></asp:TextBox>
</div>
etc. etc.
So, now to load the above, I do this:
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intPK
Dim rstData As DataTable = Myrst(strSQL)
' load up our edit div
fLoader(Me.EditRecord, rstData.Rows(0))
ViewState("PK") = intPK
So, this loads the controls for me, and I get this:
the point is I do/did not have to write code over and over to fill out the "div" of controls.
And in above, I have a routine to do the reverse, (take controls, shove back to data table).
So, my save button code then becomes this:
Dim intPK As Integer = ViewState("PK")
Dim strSQL As String = "SELECT * FROM tblHotelsA WHERE ID = " & intPK
Dim rstData As DataTable = Myrst(strSQL)
Call fWriterW(EditRecord, rstData.Rows(0))
Call MyRstSave(rstData, "tblHotelsA")
' re-fresh grid
LoadGrid()
So, I have fLoader (table row to div)
and hten fWriter (div to table)
And then a save routine.
So, the built in databinding is much like a hug, but without the kiss.
It can bind/fill controls for you, but it does not give you the reverse (sending control values back to the database).
I can post the above loader, and writer code. I have both a c#, and vb version.
It just a simple loop, and looks for the "f" tag inside of the passed div, and then fills out the control value from the database column.
I mean, really, writing the same code over and over to fill out controls on a page makes VERY little sense. With just a wee bit more effort, you can make such code "general" and it can be used for any web page.

ASP.NET repeater dynamic value

I try to use a repeater in repeater in ASP.NET, but I want to change datasource from every repeat.
My aspx markup is:
<div class="container px-4 py-5" id="custom-cards">
<asp:Repeater ID="RepeaterKategori" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<h2 class="pb-2 border-bottom"><%#Eval("kategoriAd") %></h2>
<div class="row row-cols-1 row-cols-md-3 g-4">
<asp:Repeater ID="RepeaterAltKategori" runat="server">
<ItemTemplate>
<div class="col">
<div class="card h-100">
<img src="<%#Eval("altkategoriResim") %>" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title"><%#Eval("altkategoriBaslik") %></h5>
<p class="card-text"><%#Eval("altkategoriAciklama") %></p>
</div>
<div class="card-footer">
<small class="text-muted">Teşekkürler : <%#Eval("altkategoriDestekci") %></small>
</div>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
</div>
<div class="d-grid gap-2 my-4">
Tümünü Görüntüle
</div>
</ItemTemplate>
</asp:Repeater>
</div>
My aspx.cs code behind is:
rehber kod = new rehber();
protected void Page_Load(object sender, EventArgs e)
{
RepeaterKategori.DataSource = kod.getDataTable("SELECT KategoriAd FROM kategoriler");
RepeaterKategori.DataBind();
}
protected void ItemBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item || args.Item.ItemType == ListItemType.AlternatingItem)
{
DataTable katsay = kod.getDataTable("SELECT * FROM altkategoriler");
int kategoris = katsay.Rows.Count;
for (int i = 1; i == kategoris; i++)
{
Repeater RepeaterAltKategori = (Repeater)args.Item.FindControl("RepeaterAltKategori");
RepeaterAltKategori.DataSource = kod.getDataTable("SELECT TOP 3 * FROM altkategoriler WHERE kategoriId="+i+"");
RepeaterAltKategori.DataBind();
}
}
}
I want another id data like 1,2,3,4 for every repeat. How can I do this? Thanks..
You need to nest two repeaters then.
You have
top table - feed the main repeater - 1 record for each.
child table - feed the child repeater - child table - many records for each.
Also, make sure you ALWAYS use if (!isPostBack) to have a real first page load - if you don't do most (if not all) of your loading and databinding inside of the !IsPostback?
you cannot even really quite much build a working page that can survive post-backs.
Ok, so we have the master (top) repeater.
for the child repeater, you might have one record, maybe many. It don't matter. But WHAT DOES matter is you now have a whole new seperate data set and whole new set of Eval() and binding that belongs to that child table or child data set.
So, lets take a main repeater (our hotels), and for each instance, we drop in another repeater to display the child data (in this case people booked in the hotel).
So, we have this mark-up - note the nested 2nd repeater.
<asp:Repeater ID="repHotels" runat="server" OnItemDataBound="repHotels_ItemDataBound">
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server" Width="240px"
Text='<%# Eval("HotelName") %>' Font-Size="X-Large" >
</asp:Label>
<asp:TextBox ID="txtDescription" runat="server"
Text='<%# Eval("Description") %>'
TextMode="MultiLine" Rows="5" Columns="40" Style="margin-left:20px" >
</asp:TextBox>
<br />
<h4>People Booked</h4>
<asp:Repeater ID="repPeopleBooked" runat="server">
<ItemTemplate>
<asp:Label ID="lFirst" runat="server" Text='<%# Eval("FirstName") %>'>
</asp:Label>
<asp:Label ID="lLast" runat="server" Text='<%# Eval("LastName") %>'
style="margin-left:10px">
</asp:Label>
<br />
</ItemTemplate>
</asp:Repeater>
<%-- our repeat separator --%>
<div style="height:20px">
<hr style="border:1px solid" />
</div>
</ItemTemplate>
</asp:Repeater>
And our code to load. We ONLY load the main repeater data source, and THAT will trigger the item data bound event, and in that event then we deal with EACH separate instance of the child repeater.
Code like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadData();
}
void LoadData()
{
SqlCommand cmdSQL = new SqlCommand("SELECT * from MyHotels ORDER BY HotelName");
repHotels.DataSource = MyRstP(cmdSQL);
repHotels.DataBind();
}
protected void repHotels_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if ( (e.Item.ItemType == ListItemType.Item) ||
(e.Item.ItemType == ListItemType.AlternatingItem))
{
// get child nested repeater
Repeater rChild = e.Item.FindControl("repPeopleBooked") as Repeater;
DataRowView rRow = ((DataRowView)e.Item.DataItem);
string strSQL = "SELECT * FROM People WHERE Hotel_ID = #id";
SqlCommand cmdSQL = new SqlCommand(strSQL);
cmdSQL.Parameters.Add("#id", SqlDbType.Int).Value = rRow["ID"];
rChild.DataSource = MyRstP(cmdSQL);
rChild.DataBind();
}
}
public DataTable MyRstP(SqlCommand cmdSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (cmdSQL)
{
cmdSQL.Connection = conn;
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
and we now get this:
So, we see the 3 main records that drive the main repeater, and then we see the child records for the nested repeater.
Now, in your case, you might only ever have one child record - but it don't matter. What really matters is that you have the ability to deal with, and bind, and have separate Eval() and data for that child repeater.

asp.net ajax like reorder list

I'm trying to create a draggable list to change the order some pictures are displayed in a product page. I wan't to be able to do this in the product edit page (same place where I add the pics and their description). So, when creating I have nothing inserted on the database and since the AJAX toolkit reorderlist only works with a datasource I was specifying the list as the source of the reorderlist and calling the databind method. In the item template of the reorder list I have a textbox to edit the pic description and a img that displays the photo. I can drag the items and the list gets reordered, however when I click save I can't get the updated text on the textbox and the order property on the picture doesn't get updated. I've tried manually getting the items in the reorderlist but they're always null even though the list shows 20 items the DataItem is empty. I've enabled viewstate and didn't help either.
Here's my code:
<ajaxToolkit:ReorderList ID="rdlPhotos" runat="server" SortOrderField="PhotoOrder" AllowReorder="true" PostBackOnReorder="true" ClientIDMode="AutoID" EnableViewState="true" ViewStateMode="Enabled">
<ItemTemplate>
<div>
<%--<eva:PhotoView ID="iPV" runat="server" Photo='<%# Container.DataItem %>' />--%>
<asp:Image ID="imgPhoto" runat="server" ImageUrl='<%# string.Format("http://eva-atelier.net/sparkle{0}", Eval("Path").ToString().Substring(1)) %>' Width="150" Height="150" />
<div class="formGrid">
<label class="formLabel">Title</label>
<asp:TextBox ID="txtTitle" runat="server" Text='<%#Bind("FileTitle") %>' />
<br />
<label class="formLabel">Description</label>
<asp:TextBox ID="txtDescription" runat="server" Text='<%#Bind("FileDescription") %>' />
<br />
</div>
<p>
<asp:Button ID="btnRemove" runat="server" Text="Remover" />
</p>
</div>
</ItemTemplate>
<ReorderTemplate>
<asp:Panel ID="pnlReorder" runat="server" />
</ReorderTemplate>
<DragHandleTemplate>
<div style="width:20px;height:20px;background-color:Red"></div>
</DragHandleTemplate>
</ajaxToolkit:ReorderList>
And below the C# code:
private void AddPhotosView()
{
if (_currentProduct.Photos != null && _currentProduct.Photos.Count > 0)
{
rdlPhotos.DataSource = _currentProduct.Photos;
rdlPhotos.DataBind();
}
}
I'm new to Asp.net I come from a large WPF background, sorry if I'm making basic question :)
Thanks
For updating order of ReorderList items add your handler for it's OnItemReorder event. In this case your handler may looks like this:
protected void ReorderHandler(object sender, ReorderListItemReorderEventArgs e)
{
var movedPhoto = _currentProduct.Photos[e.OldIndex];
_currentProduct.Photos.RemoveAt(e.OldIndex);
_currentProduct.Photos.Insert(e.NewIndex, movedPhoto);
_currentProduct.Photos.Save();
}
For updating FileTitle and FileDescription of single Photo it is easy to use OnUpdateCommand event of ReorderList and a button with attribute CommandName="Update" for each Photo.
And for updating all Photos at once just iterate through rdlPhotos.Items in next manner:
protected void SaveAllHandler(object sender, EventArgs e)
{
foreach (var riItem in rdlPhotos.Items)
{
var id = ((HiddenField)riItem.FindControl("itemID")).Value;
var title = ((TextBox)riItem.FindControl("txtTitle")).Text;
var description = ((TextBox)riItem.FindControl("txtDescription")).Text;
UpdatePhoto(id, title, description);
}
}
Remember that rdlPhotos.Items here are in initial order. And for identifying which Photo should be updated add hidden field with Photo.ID-value to ReorderList's ItemTemplate like this:
<asp:HiddenField runat="server" ID="itemID" Value='<%# Eval("ID") %>' />

Render a string[] as a series of editable controls?

I have a string[] containing individual words parsed out of a paragraph of text. I need to display each word in its own cell, and have each cell be assigned a dynamic width based on the word's length. I want to be able to have as many words as possible within the maximum width of each row.
In short, I'm trying to take a given paragraph of text and present it as a series of editable controls in a way which resembles how it might appear as a plain text document, with each word consuming only its required space on each "line".
I first tried using a DataList with RepeatLayout in Table mode and RepeatColumns to a set value of 10, with a Repeater within containing a Label control; this resulted in 10 words per row but each cell with a fixed width.
I've considered using a GridView with a single column which I would cram with as many words (in the form of Label controls) per row as will fit, adding new rows as necessary until the entire paragraph is built.
Could anyone please share an elegant way to do this?
The magic you're looking for is the "contenteditable" attribute. Works in IE, Firefox and Chrome.
I'm not sure what the hell you're doing with this, lmao... but this ought to work:
Your Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
//creating some bogus collection of strings.
string[] parts = { "this", "is", "a", "test", "of", "the", "goofiest", "ui", "ever" };
//bind it to the repeater.
rptr.DataSource = parts;
rptr.DataBind();
//now we'll add them to a JavaScript array we can access client side.
StringBuilder sb = new StringBuilder();
sb.Append("document.stringItems = new Array();");
for (int i = 0; i < parts.Length; i++)
sb.AppendFormat("document.stringItems[{0}] = '{1}';", i, parts[i]);
ScriptManager.RegisterClientScriptBlock(this, GetType(), "someKey", sb.ToString(), true);
}
protected void btnTest_Click(object sender, EventArgs e)
{
//display the result just so we can see it's working.
lblResult.Text = hdnResult.Value;
}
Your .ASPX:
<asp:Repeater ID="rptr" runat="server">
<ItemTemplate>
<span contenteditable="true" style="border: solid 1px;" onkeyup="updateItem(event, <%#Container.ItemIndex%>)">
<%#Container.DataItem%></span>
</ItemTemplate>
</asp:Repeater>
<asp:HiddenField ID="hdnResult" runat="server" />
<asp:Button ID="btnTest" runat="server" Text="Test" OnClick="btnTest_Click" />
<script type="text/javascript" language="javascript">
//<![CDATA[
function updateItem(e, index) {
//get the source element. (magic here for cross browser lameness)
var src;
if(window.event) {
e = window.event;
src = e.srcElement;
}else{
src = e.target;
}
//update our item in our array.
document.stringItems[index] = src.innerHTML;
//update our hidden field.
var s = '';
var space = false;
for (var i = 0; i < document.stringItems.length; i++) {
if (space)
s += ' ';
else
space = true;
s += document.stringItems[i];
}
var hdnResult = document.getElementById('<%=hdnResult.ClientID%>');
hdnResult.value = s;
}
//]]>
</script>
<asp:Label ID="lblResult" runat="server"></asp:Label>
You'll have to add some javascript for the onkeypress too to make sure they're not adding carriage returns or spaces or whatever it is you don't want them putting in... but this is the basic idea.
I hope that helps. Good luck.
I'll start off by saying that, from a user interface perspective, I'm not entirely sure as to why you would need to do this, but if it must be done you'd do something along these lines:
Start off with your string[]
Create a List<List<string>>
Loop through your original array, adding the strings to a new List<string>
Once you've reached your desired total character length, add the List<string> to your List<List<string>> (which contains your lines)
Repeat these steps until you've gone all the way through your original array
Once this is done, you bind the list o' list to a repeater like this below (pseudo code; might be off a bit):
<asp:Repeater id="rpt1" runat="server">
<ItemTemplate>
<div>
<asp:Repeater id="rpt2" runat="server" DataSource='<%# Container.DataItem %>'>
<ItemTemplate>
<asp:TextBox id="txt1" runat="server" Text='<%# Container.DataItem %>' Width='<%# MyWidthAlgorithm(Container.DataItem) %>'
</ItemTemplate>
</asp:Repeater>
</div>
</ItemTemplate>
</asp:Repeater>
Best way I can think of approaching something like this is to simply use a repeater and float everything left.
<asp:Repeater runat="server" id="rptr">
<ItemTemplate>
<span style="float:left; height:22px; line-height:22px;"><%# Eval("Word") %></span>
</ItemTemplate>
</asp:Repeater>

Resources