Generating button in backend ASP Repeater and then finding the control in c# - asp.net

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

Related

How to get Repeater's current ItemIndex in code behind

While Container.ItemIndex as a way to get current repeater's item index in data binding expression works perfectly fine <%# Container.ItemIndex %>, it does not work in pure code behind. Container is not declared or inaccessible.
How can I get repeater's current item index here:
<ItemTemplate>
<% If Container.ItemIndex = 2 Then %>
TRUE/some longer HTML here/
<% Else %>
false/some longer HTML here/
<% End If %>
</ItemTemplate>
EDIT
For cases with not much HTML code this will work, but I am looking for Code Render Block solution as per example above.
<%#: If(Container.ItemIndex = 2, "TRUE", "false") %>
Ok, just drop in a button and pick up the repeter row.
So, say we have this repeater markup:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<div style="border-style:solid;color:black;width:290px;float:left;padding:10px">
<div style="padding:5px;text-align:right">
Hotel Name: <asp:TextBox ID="txtHotelName" runat="server" Text ='<%# Eval("HotelName") %>' />
<br />
First Name: <asp:TextBox ID="txtFirst" runat="server" Text ='<%# Eval("FirstName") %>' />
<br />
Last Name: <asp:TextBox ID="txtLast" runat="server" Text ='<%# Eval("LastName") %>' />
<br />
City: <asp:TextBox ID="City" runat="server" Text ='<%# Eval("City") %>' />
<br />
Province: <asp:TextBox ID="Province" runat="server" Text ='<%# Eval("Province") %>'/>
<br />
Active: <asp:CheckBox ID="chkActive" runat="server" Checked = '<%# Eval("Active") %>'/>
<br />
<asp:Button ID="cmdRowC" runat="server" Text="Row Click" OnClick="cmdRowC_Click"/>
</div>
</div>
<div style="clear:both;height:5px"></div>
</ItemTemplate>
</asp:Repeater>
and our code behind to fill this repeater:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack = False Then
LoadGrid()
End If
End Sub
Sub LoadGrid()
Using cmdSQL As New SqlCommand("SELECT * FROM tblHotels ORDER BY HotelName",
New SqlConnection(My.Settings.TEST3))
cmdSQL.Connection.Open()
Repeater1.DataSource = cmdSQL.ExecuteReader
Repeater1.DataBind()
End Using
End Sub
And we now have this:
So now the button code. Note how I just dropped in a standard button.
But, we now can't double click on the button to automatic wire up and create a event.
BUT YOU CAN do this to create a event:
In the button code markup, type in OnClick=
WHEN YOU HIT "=", then intel-sense will pop up a dialog for you to create a button click event like this:
So, now click on create new event. It "seems" like nothing occured, but if we flip to code behind you find the click event stub.
So, now in our click event code we can easy pick up the current repeater row, the values and yes even the index.
The code works like this:
Protected Sub cmdRowC_Click(sender As Object, e As EventArgs)
Dim cBtn As Button = sender
Dim rRow As RepeaterItem = cBtn.Parent
Debug.Print("Row clicked = " & rRow.ItemIndex)
Debug.Print("First Name = " & DirectCast(rRow.FindControl("txtFirst"), TextBox).Text)
Debug.Print("Last Name = " & DirectCast(rRow.FindControl("txtLast"), TextBox).Text)
Debug.Print("Hotel Name = " & DirectCast(rRow.FindControl("txtHotelName"), TextBox).Text)
End Sub
output:
Row clicked = 1
First Name = Darcy
Last Name = Caroll
Hotel Name = Athabasca Hotel
So, just drop in a plane jane button. When you click on it the event stub runs, and as you can see, we pick up the "Repeater row item".
From that row, we can get the row index, and of course pluck out any other control value from that row using find control.
eg:
Dim txtHotel as TextBox = rRow.FindControl("txtHotel")
debug.print ("Hotel name = " & txtHotel.Text)
So, once you have the repeater row, you don't need some container.ItemIndex expression, since the row item lets you get the given index row with:
rRow.ItemIndex
So, ItemIndex is available, and is avilable regardless if you have or include or use the conttin.ItemIndex in the markup.
There is VERY LITTLE need to mess around with that code in the markup.
Note how VERY clean and simple the above is. I suggest you MAKE HUGE efforts to avoid dumping vb code inside of the markup like you are doing. If you even want to convert to c#, or even just maintain that code? Put that code in the code behind area - not in the markup.
As above shows, for repeating data etc., you can use a Repeater, or even often I use a listview - as it allows a repeating layout to be created automatic for you, and without having to write looping code.
******************** EDIT ***************************
Ok, now that we have this working, say we wanted to put in a message in the repeater that
This hotel is Active
or
This hotel is NOT active!
Ok, so in our markup, we could drop in a label like this:
And now from the repeater property sheet, double click here to create the item data bound event:
It is now a simple matter to put in our code logic for each row like this:
Protected Sub Repeater1_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles Repeater1.ItemDataBound
Dim rRow As RepeaterItem = e.Item
Dim ckbox As CheckBox = rRow.FindControl("chkActive")
Dim MyLabel As Label = rRow.FindControl("Label1")
If ckbox.Checked Then
MyLabel.Text = "This Hotel is Active!!!"
Else
MyLabel.Text = "Not active!"
End If
End Sub
And output is now this:
So, once again, to conditional format the repeater, add color to active hotel, or whatever? Use the item bound event.
Say i wanted add to above that the HotelName is to be color blue when active, then we could do this:
Protected Sub Repeater1_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles Repeater1.ItemDataBound
Dim rRow As RepeaterItem = e.Item
Dim ckbox As CheckBox = rRow.FindControl("chkActive")
Dim MyLabel As Label = rRow.FindControl("Label1")
If ckbox.Checked Then
MyLabel.Text = "This Hotel is Active!!!"
Else
MyLabel.Text = "Not active!"
End If
Dim txtHotel As TextBox = rRow.FindControl("txtHotelName")
If ckbox.Checked Then
txtHotel.BackColor = Drawing.Color.AliceBlue
End If
End Sub
Again, nice easy clean code - no mess markup. We seperate the code logic from the markup.
Output now is this:
Again, note how we did not even write ANY looping code here!!!!
So you want to leverage the built in options for the repeater, and for a GridView, listView, and a repeater?
Use the row data bind event - it lets you format, even do math, or whatever you want for EACH row of the repeating controls. And as you can see, its plain jane easy to write code as a bonus.

I can't access the ID nested inside the ListView <itemtemplate>

I am trying to grab the value of the checkbox every time a user checks it using the following markup:
<!---- <asp:CheckBoxList ID="CheckboxWattage" runat="server" RepeatColumns="1" CellSpacing="-1" RepeatDirection="Vertical" RepeatLayout="Flow" TextAlign="Right" Width="300px">
<asp:ListItem text="" value=""></asp:ListItem>
</asp:CheckBoxList>--->
<asp:Listview id="filterListView" runat="server" DataSourceID="" onitemdatabound="filterListView_ItemDataBound" >
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" value='<%#DataBinder.Eval(Container.DataItem,"UDF_WATTAGE") %>' runat="server"/>
<asp:label ID="LabelValue" AssociatedControlID="CheckBox1" runat="server" text='<%#Eval("UDF_WATTAGE") %>' ></asp:label>
<asp:label Style="font-size:12px; font-style: italic;" ID="countLabel" runat="server" text='<%# "(" + DataBinder.Eval(Container.DataItem,"CountofUDF_WATTAGE") +")" %>' ></asp:label>
</br>
</ItemTemplate>
</asp:Listview>
<asp:Button id="wattagebtn" text="Apply" class="btn btn-danger" style="float:right;" onclick="wattageApply_Click" runat="server"></asp:Button>
<asp:Label ID="Label1" runat="server" Text="hi"></asp:Label>
</div>
</div>
code behind update! I think I was able to grab the CheckBox1 ID. how can I grab the checkbox value? When checked?
I will update once I find the solution for anyone that's having the same issue.
UPDATEEE FOUND THE SOLUTION. i had to put if(!IsPostBack){ }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dapter.Fill(ds);
filterListView.DataSource = ds;
filterListView.DataBind();
}
protected void filterListView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
}
}
protected void wattageApply_Click(Object sender, System.EventArgs e)
{
foreach (ListViewItem item in filterListView.Items)
{
CheckBox cb = item.FindControl("CheckBox1") as CheckBox;
if (cb.Checked == true)
{
string ch;
Label1.Text = cb.Text;
}
else
{
Label2.Text = cb.Text;
}
}
<asp:ListView ID="ListView1" runat="server" DataSourceID="">
<ItemTemplate>
<div>
<asp:CheckBox ID="CheckBox1" runat="server"
OnCheckedChanged="ListViewItemCheckChanged"
Text='<%#DataBinder.Eval(Container.DataItem,"UDF_WATTAGE") %>' />
.
.
.
</div>
</ItemTemplate>
</asp:ListView>
Code Behind (VB.NET):
Protected Sub ListViewItemCheckChanged(sender As Object, e As EventArgs)
Dim cb As CheckBox = sender
// at this point the variable cb contains the user selected checkbox
End Sub
This will allow you to examine the last Checkbox the user clicked
Keep in mind there will be a postback on each click.
This will give you what you are asking for but is probably not the best interface experience for the user.
Typically when presented with multiple check boxes you allow all the user selections and then process the selections with one postback via some submit style button. This would require:
Looping through the ListView's Item Collection,
For each ListViewDataItem find each CheckBox via FindControl("CheckBox1")
Check for CheckBox1.Checked() and act accordingly
Addendum: How to Loop through ListView Items (VB.NET):
Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
Dim cb As CheckBox
For Each lvi As ListViewDataItem In ListView1.Items
cb = lvi.FindControl("CheckBox1")
If cb.Checked Then
...
Else
...
End If
Next
End Sub
Addendum: A word about Checkboxes:
An ASP CheckBox doesn't have a value per se. It's "value" is the Boolean "cb.Checked". But there are two ways you can associate a value with CheckBox
1) If the value is not a security issue and not particulary lengthy you can add an Attribute in the ListView ItemDataBound event
Private Sub ListView1_ItemDataBound(sender As Object, e As ListViewItemEventArgs) Handles ListView1.ItemDataBound
If e.Item.ItemType = ListViewItemType.DataItem Then
Dim drv As DataRowView = e.Item.DataItem
Dim cb As CheckBox = e.Item.FindControl("CheckBox1")
cb.Attributes("some-legal-html-attribute-name") = drv("Some_dataset_field")
End If
End Sub
You are doing something similar with this in your aspx:
<asp:CheckBox ID="CheckBox1" runat="server"
value='<%#DataBinder.Eval(Container.DataItem,"UDF_WATTAGE") %>'/>
the above should render to something like this:
<input id="ListviewMangledprefix_CheckBox1" type="checkbox"
value="[value of UDF_WATTAGE FIELD]" />
Inspect the field in the rendered browser page and see if the "value" attribute is being rendered. If so you should be able to access it in the code behind as:
Dim wattage as String = cb.Attributes("value")
2) Better and more secure would be to implement and use ListView1.DataKeys. But this is a much more extensive topic that you should research when you have time

Iterating through EditItemTemplate textboxes in asp:Gridview

I have a gridview displaying data within a template field which requires more information about the record to be displayed by clicking on a linkbutton. Right now, I have the "details" linkbutton display the information by calling the edit command on the gridview so that it switches to the EditItemTemplate. Within the EditItemTemplate I have a linkbutton for cancel and then an edit button that, when clicked, displays the update button with the update command, but I need it to iterate through that row and set all the textboxes within the EditItemTemplate to ReadOnly=false so as to allow them to be edited before the update command is selected. Here is a summary of the code:
<ItemTemplate>
*basic information displayed*
<asp:LinkButton runat="server" CommandName="Edit" Text="Details"></asp:LinkButton>
</ItemTemplate>
<EditItemTemplate>
*A bunch of readonly textboxes that display the extra information*
<asp:LinkButton runat="server" CommandName="Update" Text="Update" Visible="false"></asp:LinkButton>
<asp:LinkButton runat="server" Text="Edit" OnClick="editButton_Click"></asp:LinkButton>
</EditItemTemplate>
And the code for the event which makes the buttons appear the way I want, but I'm not sure how to iterate through the EditItemTemplate, or even if this is what I should do:
Protected Sub editButton_Click(sender As Object, e As EventArgs)
sender.FindControl("updateButton").Visible = True
sender.FindControl("editbutton").Visible = False
For Each t In ?EditItemTemplate?
Dim textbox = TryCast(t, System.Web.UI.WebControls.TextBox)
If textbox IsNot Nothing Then
textbox.ReadOnly = False
End If
Next
End Sub
So my question is either how to get this to work, or how I should set up the GridViewCommands otherwise
You should use a PlaceHolder in your EditItemTemplate. Place all of your Controls/LinkButtons inside this placeholder.
<EditItemTemplate>
<asp:PlaceHolder ID="TestPlaceHolder" runat="server">
// Sample Link Buttons
<asp:LinkButton runat="server" CommandName="Update" Text="Update"
Visible="false"></asp:LinkButton>
<asp:LinkButton runat="server" Text="Edit" OnClick="editButton_Click"></asp:LinkButton>
// Sample Text Box
<asp:TextBox runat="server" ID="FirstName" ...>...</TextBox>
</asp:PlaceHolder>
</EditItemTemplate>
Handle the RowEditing event of GridView. Inside the edit event handler, first find the Placeholder, then use the PlaceHolder's Controls property to iterate over the Controls...
protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
// Get the Placeholder for the row being edited.
PlaceHolder testPlacehldr =
GridView.Rows[e.NewEditIndex].FindControl("TestPlaceholder") as PlaceHolder;
// Iterate over the controls
if(testPlacehldr.Controls.Count > 0)
{
foreach (Control ctrl in testPlacehldr.Controls)
{
if (ctrl is LinkButton)
{
LinkButton lnkBtn = ctrl as LinkButton
if(lnkBtn.Text== "Update")
{
lnkBtn.Visible = false;
}
// More IF conditions follow....
}
if (ctrl is TextBox)
{
TextBox txtBox = ctrl as TextBox;
if(txtBox.ID == "FirstName")// use any property of TexBox you prefer
{
txtBox.ReadOnly= true;
}
// More IF conditions follow....
}
}
}
//At the End, set the EditIndex and Bind the data
GridView1.EditIndex = e.NewEditIndex;
BindGridViewData();
}
I hope you can workout the logic yourself now for hiding/showing the controls.
So I figured out how to do it (needed it in vb) by using the placeholder within the EditItemTemplate, here's the code behind it:
Protected Sub editButton_Click(sender As Object, e As EventArgs)
sender.FindControl("editbutton").Visible = False
sender.FindControl("updateButton").Visible = True
Dim testPlacehldr As PlaceHolder = sender.FindControl("TestPlaceholder")
If testPlacehldr.Controls.Count > 0 Then
Dim btn As LinkButton = sender.FindControl("editButton")
If btn.Visible = False Then
For Each ctrl As Control In testPlacehldr.Controls
If ctrl.GetType Is GetType(TextBox) Then
Dim box As TextBox = TryCast(ctrl, TextBox)
box.ReadOnly = False
End If
Next
End If
End If
End Sub
This works fine for what I need it to do. Credit to the user R.C. for the idea about placeholders

Formview.ChangeMode is not working

This is my webpage:
<asp:FormView
ID = "frmView1"
DefaultMode = "Insert"
runat = "server"
>
<ItemTemplate>
Item Template
</ItemTemplate>
<InsertItemTemplate>
Insert Item Template
</InsertItemTemplate>
</asp:FormView>
<asp:Button ID="btnSubmit" Text="Submit" runat="server" />
and this is the code-behind:
Private Sub btnSubmit_Click(sender As Object, e As System.EventArgs) Handles btnSubmit.Click
frmView1.ChangeMode(FormViewMode.ReadOnly)
End Sub
When I'm clicking the btnSubmit the formview is not showing anything! Is this the right way to change mode in code behind?
Moreover, in code behind instead of ChangeMode if write this:
frmView1.DefaultMode = FormViewMode.ReadOnly
forview shows the content of InsertItemTemplate. Could someone please explain what's going on here?
FormView won't show unless you bind, something like this:
using(SqlDataAdapter adapter = new SqlDataAdapter(sql,connection))
{
DataTable table = new DataTable();
adapter.Fill(table);
FormView1.DataSource = table;
FormView1.DataBind();
}
And just add EmptyDataTemplate tag to show empty message if your datasource don't return any value.
<EmptyDataTemplate>
There is nothing to see here.
</EmptyDataTemplate>
follow this tutorial and more about this on MSDN
In your code
<asp:Button ID="btnSubmit" Text="Submit" runat="server" />
must change to
<asp:Button ID="btnSubmit" Text="Submit" runat="server" command="ChangeModeToReadOnly" OnCommand="Button_Command" />
Instead of implementing OnClick event implement this
Protected Sub Button_Command(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.CommandEventArgs)
Select Case e.CommandName
Case "ChangeModeToReadOnly"
frmView1.ChangeMode(FormViewMode.ReadOnly)
End Select
End Sub
You can try with adding the following DataBound function
protected void myFormView_DataBound(object sender, EventArgs e)
{
frmView1.ChangeMode(FormViewMode.ReadOnly);
}
The syntax is in C#

Rebind Repeater using Next / Previous buttons in the ItemTemplate of the same Repeater

Sorry if this post appears to be long winded.
I have a parent repeater with nested repeaters.
My problem is I am trying to use 'Next' and 'Previous' buttons to rebind my repeater (rptTabContent) and thus the inner repeaters also, but when I try to manipulate the state of these links during the ItemCommand event they are overwritten by the bind.
So I cannot disable or enable the buttons if there is to be no data after a consquent click of either 'Next' or 'Previous' links which is what I am trying to do.
The nested repeaters show a weeks worth of data in 7 columns. The next button is meant to show the next weeks data and previous the previous weeks data. Initially the previous button is inactive.
The parent repeater is bound again because initially it loops out 4 divs each containing a table of data. If one next button is hit on one repeater all tables must show their next 7 days of data.
Each repeater has the exact same number of items.
Initially on page load all of the data bind correctly.
I know the date of the very first item and last item in my collection so I am able to calculate the date range of the objects I need to bind to my parent repeater.
The HTML is below
<asp:Repeater ID="rptTabContent" runat="server" OnItemCommand="rptTabContent_ItemCommand">
<ItemTemplate>
<div id="<%# Eval("SlotTypeUrl") %>" class="delivery-timetable-container">
<table cellpadding="0" cellspacing="0" border="0">
<asp:Repeater ID="rptDeliverySlots" runat="server">
<ItemTemplate>
<tr class="time-slots">
<th><asp:Label id="lblRowTime" runat="server"></asp:Label></th>
<asp:Repeater ID="rptDeliverySlot" runat="server">
<ItemTemplate>
<td id="tdDay" runat="server">
<cc1:RepeaterRadioButton id="rptrdoDeliverySlot" groupname="del-times" runat="server" />
</td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>
<tr>
<td colspan="8"> </td>
</tr>
<tr>
<td colspan="4">
<div class="eco-rollover">
<div>
<img src="icon.gif" />
</div>
</div>
</td>
<td colspan="4">
<asp:LinkButton id="lnkPreviousWeek" enabled="false" runat="server" commandargument="Previous" cssclass="inactive">< Previous week</asp:LinkButton>|
<asp:LinkButton id="lnkNextWeek" runat="server" commandargument="Next" cssclass="active" >Next week ></asp:LinkButton>
</td>
</tr>
</table>
</div>
</ItemTemplate>
</asp:Repeater>
My ItemCommand event is here:
Protected Sub rptTabContent_ItemCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs)
If (e.Item.ItemType = ListItemType.Item) Or (e.Item.ItemType = ListItemType.AlternatingItem)
Dim filteredProposedDeliveries As New List(Of ProposedDeliverySlotDTO)
Dim firstDate, lastDate As Date
Dim startDate, endDate As Date
If (Session("FirstDeliveryDate") IsNot Nothing) AndAlso (Session("LastDeliveryDate") IsNot Nothing) Then
firstDate = CType(Session("FirstDeliveryDate"), Date)
lastDate = CType(Session("LastDeliveryDate"), Date)
Select Case e.CommandArgument.ToString()
Case "Next"
'Get new startDate using firstDate and store to use next time
'disable next button if startDate > lastDate
Case "Previous"
'Get new startDate from current displayed date and overright stored startdate
'disable previous button if startDate < firstDate
End Select
endDate = startDate.AddDays(7)
End If
'filteredProposedDeliveries = Get object between startDate and EndDate
Dim slots As List(Of SlotType) = PrepareSlotsForBinding(filteredProposedDeliveries)
Dim rptTabContent As Repeater = CType(e.Item.BindingContainer, Repeater)
rptTabContent.DataSource = slots
rptTabContent.DataBind()
End If
End Sub
How can I manage my 'Next' and 'Previous' links under these conditons.
Many thanks
Are you sure you need next/previous buttons four times?
Anyway, do your button management on rptTabContent_ItemDataBound. On ItemCommand, you get new starting point, get data and do data bind. On ItemDataBound, you get the Next button and the Previous button and enable or disable them based on what current date is. Both buttons should be disabled in the markup as a default state (for when there is no data).
Here is a working sample that simulates a data source of four items.
Page
<asp:Repeater ID="Repeater1" runat="server" onitemcommand="Repeater1_ItemCommand" onitemdatabound="Repeater1_ItemDataBound">
<ItemTemplate>
<div>
Label: <asp:Label ID="itemLabel" runat="server"></asp:Label><br />
<asp:LinkButton id="lnkPreviousWeek" Enabled="false" runat="server" commandargument="Previous" cssclass="inactive">< Previous week</asp:LinkButton> |
<asp:LinkButton id="lnkNextWeek" Enabled="false" runat="server" commandargument="Next" cssclass="active" >Next week ></asp:LinkButton>
<hr />
</div>
</ItemTemplate>
</asp:Repeater>
Code
private int current = 1;
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["current"] == null)
ViewState["current"] = current;
else
current = (int)ViewState["current"];
if (!IsPostBack)
{
Repeater1.DataSource = GetData(current);
Repeater1.DataBind();
}
}
private List<int> GetData(int start)
{
//pseudo data; simulates a page of 2 records
List<int> ds = new List<int>();
ds.Add(start);
ds.Add(start + 1);
return ds;
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if ((string)e.CommandArgument == "Next")
current++;
else if ((string)e.CommandArgument == "Previous")
current--;
ViewState["current"] = current;
Repeater1.DataSource = GetData(current);
Repeater1.DataBind();
}
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
LinkButton prev = (LinkButton)e.Item.FindControl("lnkPreviousWeek");
LinkButton next = (LinkButton)e.Item.FindControl("lnkNextWeek");
//here 1 and 3 are artificial boundaries for data for the sake of example
if (current > 1)
prev.Enabled = true;
if (current < 3)
next.Enabled = true;
Label label = (Label)e.Item.FindControl("itemLabel");
label.Text = e.Item.DataItem.ToString();
}

Resources