Rebind Repeater using Next / Previous buttons in the ItemTemplate of the same Repeater - asp.net

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();
}

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

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

Event handlers for controls in an ASP Repeater

I need to be able to trigger events when a user clicks on a radio button that is generated within an control on my page.
I've added an OnSelectedIndexChanged handler to the RadioButtonList and created a function in my code behind that should handle the selection of the RadioButtonList's ListItems, but I don't know how to pass a value to that function.
Here's my code:
<asp:Repeater runat="server" ID="rptQuestions">
<HeaderTemplate><table width="100%"></HeaderTemplate>
<ItemTemplate>
<tr>
<td colspan="2"><%# Container.DataItem("QuestionText")%></td>
</tr>
<tr>
<td>
<asp:RadioButtonList runat="server" ID="rblAnswerSelection" RepeatDirection="Horizontal" AutoPostBack="true" OnSelectedIndexChanged="CheckAnswer">
<asp:ListItem Text="True" Value="1"></asp:ListItem>
<asp:ListItem Text="False" Value="0"></asp:ListItem>
</asp:RadioButtonList>
<asp:Label runat="server" ID="lblCorrectAnswer" Text="<%#Container.DataItem("CorrectAnswer") %>"></asp:Label>
</td>
<td><asp:Label runat="server" ID="lblResult"></asp:Label></td>
</tr>
</ItemTemplate>
<FooterTemplate></table></FooterTemplate>
</asp:Repeater>
Private Function CheckAnswer(ByVal SelectedAnswer As Integer) As Boolean
Select Case SelectedAnswer
Case 1 ' User answered True
If lblCorrectAnswer.Text = "True" Then
Return True
Else
Return False
End If
Case 0 ' User answered False
If lblCorrectAnswer.Text = "False" Then
Return True
Else
Return False
End If
End Select
End Function
The idea is that the user is to be informed whether or not their selected answer was correct. The value of the CheckAnswer function would determine a "CORRECT" or "INCORRECT" message displaying on lblResult. If there's a way to do this without a PostBack, then that would be ideal.
Any suggestions will be greatly appreciated.
Here's brief outline of client side solution (w/o post-back). Use html radio-buttons along with a hidden field to store the correct answer. Attach click event handlers on radio-buttons to match the value with the value from hidden field.
Repeater Mark-up:
<tr>
<td colspan="2"><%# Container.DataItem("QuestionText")%></td>
</tr>
<tr>
<td class="answerContainer">
<input type="radio" class="option" name='answer_<%# Container.ItemIndex %>' value="1">True
<input type="radio" class="option" name='answer_<%# Container.ItemIndex %>' value="0">False
<input type="hidden" id="correct_answer" value='<%#Container.DataItem("CorrectAnswer") %>' />
</td>
<td>
<span class="result" />
</td>
</tr>
Java-script (using jquery):
$(document).ready(function() {
$('td.answerContainer .option').click(function() {
var opt = $(this);
var correctAns = opt.next('#correct_answer');
var result = (opt.val() == correctAns.val()) ? "Correct" : "Incorrect";
opt.parent().next('td').find('.result').html(result);
});
});
Disclaimer: untested code
Note that radio button names are suffixed with the item index so that on the server side, you can read the user's answers by looking into the request (for example, value of Request["answer_1"] should tell user selected answer for second question -> "1" : True, "0": False and empty string: no selection).
The obvious disadvantage is that correct answer is stored in the html and so it will be simpler to cheat the system. Solution can be to make ajax call to the server to check if the answer is correct or not - this will need creating salted time-bound pseudo-random tokens to identify questions (otherwise user can make same ajax calls to get answers).
Cut your RadioButton code from repeater somewhere outside repeater, go to Design view and click on that RadioButton (now it's not included in repeater and you can assign event handler to it without typing any code manually).
When you select this RadioButton in DesignView, click events button in your proporties tab and click OnSelectedIndexChanged and this will auto generate eventhandler function. You have to enable autopostback on this radiobutton (arrow in top right corner of control - in Design view).
Now cut back this RadioButton and place it inside your repeater. Now every radiobutton change will cause calling generated handler function where you can cast sender object to RadioButton, and you can access its value with SelectedValue property.
I've tried this solution many times, but only in C# so I am sorry if something is different in VB.NET
You can't use Function as an event handler. It must be a sub which takes two argument.
Take a look at this sample.
Markup
<asp:Repeater runat="server" ID="rptQuestions">
<HeaderTemplate><table width="100%"></HeaderTemplate>
<ItemTemplate>
<tr>
<td colspan="2"><%# Eval("Name")%></td>
</tr>
<tr>
<td>
<asp:RadioButtonList runat="server"
ID="rblAnswerSelection"
RepeatDirection="Horizontal"
AutoPostBack="true"
OnSelectedIndexChanged="CheckAnswer">
<asp:ListItem Text="True" Value="1"></asp:ListItem>
<asp:ListItem Text="False" Value="0"></asp:ListItem>
</asp:RadioButtonList>
<asp:Label runat="server"
ID="lblCorrectAnswer"
Text='<%#Eval("CorrectAnswer") %>'>
</asp:Label>
</td>
<td><asp:Label runat="server" ID="lblResult"></asp:Label></td>
</tr>
</ItemTemplate>
<FooterTemplate></table></FooterTemplate>
</asp:Repeater>
Code-behind
Public Class Data
Public Property QuestionText As String
Public Property CorrectAnswer As String
End Class
Dim lst As List(Of Data)
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
lst = New List(Of Data)
lst.Add(New Data() With {.QuestionText = "A", .CorrectAnswer = "True"})
lst.Add(New Data() With {.QuestionText = "B", .CorrectAnswer = "False"})
lst.Add(New Data() With {.QuestionText = "C", .CorrectAnswer = "True"})
rptQuestions.DataSource = lst
rptQuestions.DataBind()
End If
End Sub
Protected Sub CheckAnswer(sender As Object, e As System.EventArgs)
Dim rad As RadioButtonList = DirectCast(sender, RadioButtonList)
Dim item As RepeaterItem = DirectCast(rad.Parent, RepeaterItem)
Dim correctAns As Label = DirectCast(item.FindControl("lblCorrectAnswer"), Label)
Dim result As Label = DirectCast(item.FindControl("lblResult"), Label)
Dim SelectedAnswer As String = rad.SelectedItem.Text
If correctAns.Text = SelectedAnswer Then
result.Text = "Correct"
Else
result.Text = "Incorrect"
End If
End Sub

asp.net ListView Sorting using DataBind

Sort listview using column headings in the LayoutTemplate
I am able to sort a basic list view using asp:SqlDataSource and setting the list view property DataSourceID by pointing it to the asp:SqlDataSource ID. I am having an issue when sorting when not using the asp:SqlDataSource and just DataBinding from the code behind.
SqlDataSource Example:
<asp:ListView ID="ContactsListView" DataSourceID="ContactsDataSource" runat="server">
<LayoutTemplate>
<table width="640px" runat="server">
<tr class="header" align="center" runat="server">
<td>
<asp:LinkButton runat="server" ID="SortByFirstNameButton" CommandName="Sort" Text="First Name" CommandArgument="FirstName" />
</LayoutTemplate>
....
</asp:ListView>
<asp:SqlDataSource ID="ContactsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:MainConnString %>"
SelectCommand="SELECT * FROM TableName">
</asp:SqlDataSource>
DataBind Example:
<asp:ListView ID="ContactsListView" DataSourceID="ContactsDataSource" runat="server">
<LayoutTemplate>
<table width="640px" runat="server">
<tr class="header" align="center" runat="server">
<td>
<asp:LinkButton runat="server" ID="SortByFirstNameButton" CommandName="Sort" Text="First Name" CommandArgument="FirstName" />
</LayoutTemplate>
....
</asp:ListView>
protected void Page_Load(object sender, EventArgs e)
{
String SQL = "SELECT * FROM Customer";
SqlDataAdapter da= new SqlDataAdapter(SQL, ConnStr);
DataSet ds = new DataSet();
da.Fill(ds);
ContactsListView.DataSource = ds.Tables[0];
ContactsListView.DataBind();
}
Both code samples populate the list view, but the second example data binding does not work for sorting. With the first example, the sorting just works with the added asp:LinkButton in the LayoutTemplate adding the CommandName="sort" and setting the CommandArugment="ColumnName", but it does not work with the second example.
Can anyone please explain why and how to get the sorting working using the code behind DataBind method?
Thanks!
I solved my issue.
I added an event to handle the sorting. The event grabs the command name (Data column) and passes it and the sorting direction into a function which will make another call to the database sort the returned results and rebind to the List View.
I had to create a view state to hold the List View Sort Direction because for some reason, the onsorting event handler kept saying that the sort direction was ascending.
Front End
<asp:ListView ID="ContactsListView" OnSorting="ContactsListView_Sorting" runat="server">
<LayoutTemplate>
<table width="640px" runat="server">
<tr class="header" align="center" runat="server">
<td>
<asp:LinkButton runat="server" ID="SortByFirstNameButton" CommandName="Sort" Text="First Name" CommandArgument="FirstName" />
</LayoutTemplate>
....
</asp:ListView>
Back End
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
BindContacts(string.Empty);
}
}
protected SortDirection ListViewSortDirection
{
get
{
if (ViewState["sortDirection"] == null)
ViewState["sortDirection"] = SortDirection.Ascending;
return (SortDirection)ViewState["sortDirection"];
}
set { ViewState["sortDirection"] = value; }
}
protected void ContactsListView_Sorting(Object sender, ListViewSortEventArgs e)
{
BindContacts(e.SortExpression + " " + ListViewSortDirection.ToString());
// Check the sort direction to set the image URL accordingly.
string imgUrl;
if (ListViewSortDirection == SortDirection.Ascending)
ListViewSortDirection = SortDirection.Descending;
else
ListViewSortDirection = SortDirection.Ascending;
}
private void BindContacts(string sortExpression)
{
sortExpression = sortExpression.Replace("Ascending", "ASC");
sortExpression = sortExpression.Replace("Descending", "DESC");
using (SqlConnection conn = new SqlConnection(_connStr))
{
conn.Open();
using (SqlDataAdapter dAd = new SqlDataAdapter("SELECT * FROM Customer", conn))
{
DataTable dTable = new DataTable();
dAd.Fill(dTable);
// Sort now
dTable.DefaultView.Sort = sortExpression;
// Bind data now
ContactsListView.DataSource = dTable;
ContactsListView.DataBind();
}
conn.Close();
}
}
I guess you could try to add a handler for ListView's Sorting event. There you are given the sorting column and the sort order in the event arguments. This could be easily usable to build a specific query and bind it to the list.
Because depending on the sort expression (which you defined in your markup) SqlDataSource will very likely do something like this (I'm not sure this is exactly what it does) for you behind the scenes:
Expression<Func<DataRow,object>> myExpression = row => row["SortExpressionYouDefinedForTheColumn"];
IEnumerable<DataRow> ex = ds.Tables[0].AsEnumerable().OrderBy(myExpression.Compile());
Or SqlDataSource may be using a DataView to sort the DataTable.
SqlDataSource can do this because by default it uses a DataSet to store the result set (or so says the documentation):
The data retrieval mode identifies how a SqlDataSource control retrieves data from the underlying database.
When the DataSourceMode property is set to the DataSet value, data is
loaded into a DataSet object and stored in memory on the server. This
enables scenarios where user interface controls, such as GridView,
offer sorting, filtering, and paging capabilities..
Since you chose to bind manually to a DataSet, you need to do the "magic" yourself; in other words, you handle the OnSort command, get the sort expression, get your data source again (however you do it, from Session or by calling the database again) and do your sort similarly to the lines shown above and rebind to your Gridview.

clearing checkboxlist

on my save button i want to clear all values of form i did the following for CheckBoxList beach.
But it doesn't work. Why so, it doesn't make values clear for checkbox list
Branch is filled like this:
protected void course_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
int courseId = Convert.ToInt32(course.SelectedValue);
DataTable dt;
dt = placementManager.GetBranchList(courseId);
if (dt.Rows.Count != 0)
{
Branch.DataSource = dt;
Branch.DataTextField = "branch_name";
Branch.DataValueField = "branch_id";
Branch.DataBind();
}
Btnsave.Visible = false;
GridView1.Visible = false;
}
catch (Exception ex)
{
COMMON.logger.Error("Error on course_SelectedIndexChanged:CompanySelected.aspx.cs", ex);
}
if (b)
{
gridNotExist.Text = "Records Successfully inserted for the displayed table, To insert new records select new entries";
this.ViewState.Remove("selectedList");
this.ViewState.Remove("dt");
Session.Abandon();
passout.SelectedIndex = 0;
company.SelectedIndex = 0;
txtpackage.Text = "";
course.SelectedIndex = 0;
Branch.DataSource = null;
Branch.DataBind();
vistDate.Text = "";
txtvenue.Text = "";
//GridView1.Visible = true;
}
}
aspx page has, of course, branch like this:
<asp:UpdatePanel id="update" runat="server">
<contenttemplate>
<td>
<asp:DropDownList ID="course" runat="server" AutoPostBack="True" OnSelectedIndexChanged="course_SelectedIndexChanged" />
<asp:CustomValidator ID="coursenecessaryForSaveButton" runat="server" ControlToValidate="course" ErrorMessage="Select A Course" OnServerValidate="coursenecessaryForSaveButton_ServerValidate" ValidationGroup="save" />
</td>
<td>
<asp:CustomValidator ID="courseNecessary" runat="server" ErrorMessage="Select A Course" OnServerValidate="courseNecessary_ServerValidate" ValidationGroup="verify" />
</td>
<td style="width: 101px">
Branch Name*
</td>
<td style="width: 126px">
<asp:CheckBoxList id="Branch" runat="server" />
</td>
</contenttemplate>
</asp:UpdatePanel>
<asp:CheckBoxList id="Branch" runat="server" >
is in a different UpdatePanel to the control which is triggering the asynchronous postback - and firing your eventhandler: course_SelectedIndexChanged.
Put the Checkbox List inside the UpdatePanel with id="update" and it will solve your problem.
Still, i would consider refactoring your HTML (and code while you're at it).
For instance - you have two UpdatePanel's - why?
The second one has nothing in it besides checkboxes. The purpose of an UpdatePanel is to dynamically update portions of a page (ie dynamic information) without a full postback.
Having nothing but static controls (checkboxes) inside an UpdatePanel isn't really serving any purpose.
However, my above answer is a stab in the dark at what you're after. You're code-behind code does'nt really make sense:
if (b == true)
Where is b defined??
Branch.DataSource = null
Branch.DataBind()
What are you binding??

Resources