Hiding a table row (the ItemPlaceholderID row) in a ListView - asp.net

I have a table row that holds my paging controls in a ListView as follows (partial layout):
<asp:ListView ID="lvOrderItems" runat="server"
DataSourceID="odsShoppingCart"
DataKeyNames="ProductNumber"
ItemPlaceholderID="lvItemContainer">
<LayoutTemplate>
<table id="lvCart" runat="server">
<tr id="lvHeader" runat="server">
...
</tr>
<tr id="lvItemContainer" runat="server"></tr>
<tr id="lvPaging" runat="server">
...
</tr>
</table>
</LayoutTemplate>
In my code-behind, I handle the DataBound event as follows, and I am planning on hiding the entire lvItemContainer row conditionally (for now, I am just trying to hide the row itself without conditions):
Protected Sub lvOrderItems_DataBound(ByVal sender As Object, ByVal e As EventArgs) Handles lvOrderItems.DataBound
Dim lvItemContainer As HtmlTableRow = CType(lvOrderItems.FindControl("lvItemContainer"), HtmlTableRow)
If Not lvItemContainer Is Nothing Then
Response.Write("hit1")
lvItemContainer.Visible = False
End If
Dim lvPaging As HtmlTableRow = CType(lvOrderItems.FindControl("lvPaging"), HtmlTableRow)
If Not lvPaging Is Nothing Then
Response.Write("hit2")
lvPaging.Visible = False
End If
End Sub
So when this runs on DataBound, hit1 is never fired, but hit2 is... any ideas what is happening here?

HTML purists don't like it, but this approach works:
In the OnLayoutCreated event, try one of the following approaches:
Take the runat="server" out of the table and child rows, and do this:
<asp:Panel ID="pnlItemContainer" runat="server">
<tr id="lvItemContainer"></tr>
</asp:Panel>
pnlItemContainer.Visible = false;
Or you can do this:
<tr id="lvItemContainer" runat="server"></tr>
EDIT: Embedded style element because setting visible to false does not work in the layout template.
lvItemContainer.Style["display"] = "none";
I'm not sure how it will work with a layout template, but it's worked for me in other situations.

Instead of attempting to (unsuccessfully) adjust the visibility of the ItemPlaceholderID container, I've set the visibility of the individual rows in the container.

Related

ASP Showing/Hiding a Table during an Event

I have a table that has a loading gif inside of it, that starts with the visible property set to false. I also have a drop down list that has a selection change event that will update information on the page depending on what the user selects. What I want to happen is the loading table to become visible at the beginning of the selection change event, the processing of new information with the selection made by the user to occur, then the loading table to go back to being invisible.
Here is what some of my code looks like:
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<table id="tblLoading" runat="server" align="center" width="100%" visible="false">
<tr>
<td align="center">
<img src="../images/Loading_Gif.gif" class="auto-style1" alt="Load" />
</td>
</tr>
<tr align="center">
<td>
<asp:Label runat="server" Font-Names="Tahoma" Font-Size="Larger" Font-Bold="True">Loading...</asp:Label>
</td>
</tr>
</table>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlPayrollPeriodReport" EventName="SelectedIndexChanged"/>
</Triggers>
</asp:UpdatePanel>
<asp:DropDownList ID="ddlPayrollPeriodReport" runat="server" AutoPostBack="True" Font-Size="Large" Font-Names="Tahoma"></asp:DropDownList>
Private Sub ddlPayPeriodReport_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlPayrollPeriodReport.SelectedIndexChanged
'set up the link to the HR system
Me.tblLoading.Visible = True
Dim datStart As Date = CDate(Me.ddlPayrollPeriodReport.SelectedItem.Value)
SetUptblReport(datStart)
Me.tblLoading.Visible = False
End Sub
I found out that I need to place the loading table in an UpdatePanel in order for it to change the value of the visible property. However, I noticed that the table will become visible if I don't have the call that turns it invisible. If I have both calls, then both calls are ignored. I am still new to ASP, so is there something I need to know in order to get this to work the way I want it to work?
The changes made in code-behind become visible only after the postback has completed and the page (or the UpdatePanel) has been refreshed. In your case, the page will show the last change that you made to tblLoading in the SelectedIndexChanged event handler, which is to set visible="False". In other words, it was not visible before the postback, and it is still not visible after it.
To show the loading message, you must make the table visible in client code, before the postback is triggered. In order to do that, the table has to be present in the page. The visible="false" setting does not work well for that because it causes ASP.NET to remove the control from the HTML ouptut. You can hide the table with the display: none style property:
<table id="tblLoading" runat="server" align="center" width="100%" style="display: none;">
To make the table visible before the postback, you can process the DropDownList selection change on the client side:
<asp:DropDownList ID="ddlPayrollPeriodReport" ... onchange="showLoadingMessage();">
The Javascript function could be defined as follows:
<script type="text/javascript">
function showLoadingMessage() {
document.getElementById('<%= tblLoading.ClientID %>').style.display = '';
}
</script>
Note: the <% ... %> syntax ensures that any modification to the ID of the control made by ASP.NET is taken into account.
Finally, you can hide the table in your event handler on the server side:
Private Sub ddlPayPeriodReport_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlPayrollPeriodReport.SelectedIndexChanged
...
tblLoading.Style("display") = "none"
End Sub

Dropdown in listview SelectedIndexChanged doesn't work first time but does after

I've searched high and low, I can't seem to find out what's happening with this. I've simplified the code, but I really have taken it back to as basic as this and still have the same problem.
I have a drop down list in a a repeater (in a Web Form with master page):
<asp:DropDownList ID="TicketQuantityDDL" runat="server" CssClass="qtyddl" AutoPostBack="true" OnSelectedIndexChanged="TicketQuantityDDL_SelectedIndexChanged" CausesValidation="false" SelectedIndex='<%# CInt(Eval("Quantity")) - 1%>'>
<asp:ListItem Value="1">1</asp:ListItem>
<asp:ListItem Value="2">2</asp:ListItem>
<asp:ListItem Value="3">3</asp:ListItem>
<asp:ListItem Value="4">4</asp:ListItem>
<asp:ListItem Value="5">5</asp:ListItem>
<asp:ListItem Value="6">6</asp:ListItem>
</asp:DropDownList>
Handler
Protected Sub TicketQuantityDDL_SelectedIndexChanged(sender As Object, e As EventArgs)
myLiteral.text = "Selected Index Changed handled..."
End Sub
The first time the page is loaded if I change the DDL the the page is posted back - the selected index change handler is NOT fired (I've stepped through the code, page.ispostback is true). Every time after the handler works unless the page is full reloaded.
Things I've tried:
Manually adding a handler OnItemCreated
Manually adding a handler OnItemDataBound
Manually registering the control for async postback with scriptmanager
Using OnClientSelectedIndexChanged to trigger postback from the client
Removing AutoPostBack and all of the above again...
I've used Page.Request.Params("__EVENTTARGET") to verify that when the partial postback is fired that the control is the drop down.
Even though viewstate is enabled I've tried specifying this for the control and the page directly.
Disabling validation.
I've tried not binding the value of the drop down and just leaving it
as is with no selected value and then manually setting the initial
selected value - no dice.
Tried removing update panel, same issue.
Things that are DEFINITELY not happening here.
I'm not rebinding on post back if not page.ispostback... databind...
I'm not selecting the same value/first item in the drop down
This isn't an auto ID problem, the controls ID stay the same through postbacks.
I'm not doing anything funky other than binding the repeater to a list of objects.
Why isn't the handler firing the first time? After the first time everything works exactly as intended.
Update
I've replicated the exact same behaviour in a list view. Due to time constraints I've used another approach but I'd really like to know how to fix this or at least know why it doesn't work.
Update 2
I've tested the functionality with a bog standard web form and it functions correctly. Something is up with this being in a contentplaceholder from a masterpage, the script manager or update panel. It's as if the event handler for the dropdown is only registered after the first post back, I've tried registering the handler in DataBound and also in the page LoadComplete events, the same thing still happens.
Update 3
I've since changed it to a list view, I'm having the exact same issue though.
This is on a web form with master page, the master page contains the script manager, the list view is in an update panel, although I've tried removing this and I still have the same issue. I've not included the onselectedindexchanged code, I've made it as simple as changing the text of a literal - doesn't work first post back, does the second.
I had originally specified the list items manually but have changed this to programatically at itemDataBound, still no difference.
As I stated above when I check which control caused the postback it's definitely the ddl, it just doesn't fire selectindexchanged the first time. I've also tried specifying the OnSelectedIndexChange in the control itself, still no dice.
Page load ,bind, list view and on item created code.
Page Load
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Dim _Basket = SessionHandler.getSessionObject(SessionHandler.SessionObjects.Basket)
If _Basket Is Nothing OrElse DirectCast(_Basket, BasketContainer).BasketItemList.Count = 0 Then
BasketSectionContainer.Visible = False
alertLiteral.Text = AlertGenerator.GetAlertHTML("No Items in Basket", "There are no items in your basket, please use the menu above to navigate the site.", AlertGenerator.AlertType.warning)
If _Basket IsNot Nothing Then SessionHandler.removeSessionObject(SessionHandler.SessionObjects.Basket)
Exit Sub
Else
Dim lBasket = DirectCast(_Basket, BasketContainer)
BindBasket(lBasket)
End If
End If
End Sub
Bind
Private Sub BindBasket(lBasket As BasketContainer)
basketListView.DataSource = lBasket.BasketItems
basketListView.DataBind()
bindTotals(lBasket) 'This just sets text of literals on the page outside of the listview
If lBasket.Postage Then
PostageDDL.visible = True 'This is outside of the list view also
End If
End Sub
Item Created
Private Sub basketListView_ItemCreated(sender As Object, e As ListViewItemEventArgs) Handles basketListView.ItemCreated
Dim QtyDDL As DropDownList = DirectCast(e.Item.FindControl("TicketQuantityDDL"), DropDownList)
AddHandler QtyDDL.SelectedIndexChanged, AddressOf TicketQuantityDDL_SelectedIndexChanged
End Sub
_Item Data Bound _
Private Sub basketListView_ItemDataBound(sender As Object, e As ListViewItemEventArgs) Handles basketListView.ItemDataBound
Dim data As BasketItem = DirectCast(e.Item.DataItem, BasketItem)
Dim QtyDDL As DropDownList = DirectCast(e.Item.FindControl("TicketQuantityDDL"), DropDownList)
For i As Integer = 1 To 6
QtyDDL.Items.Add(New ListItem(i.ToString, i.ToString))
Next
QtyDDL.DataTextField = data.BasketItemID.ToString 'no command arg for DDL so using this, I've tested without, doesn't make a difference.
Select Case data.BasketType
Case BasketInfo.BasketItemType.DiscountedTickets, BasketInfo.BasketItemType.Tickets, BasketInfo.BasketItemType.Goods
'tickets and goods...
QtyDDL.Items.FindByValue(data.Quantity.ToString).Selected = True
Case Else
'non ticket or goods type, disable quantity selection
QtyDDL.Items.FindByValue("1").Selected = True
QtyDDL.Enabled = False
End Select
End Sub
_List View _
<asp:ListView ID="basketListView" runat="server">
<LayoutTemplate>
<table class="cart-table responsive-table">
<tr>
<th>Item</th>
<th>Description</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th></th>
</tr>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</table>
<table class="cart-table bottom">
<tr>
<th>
<asp:Button ID="ApplyDiscountCodeButton" runat="server" CssClass="button color pull-right" Text="Apply Code" />
<asp:TextBox ID="DiscountCodeTextBox" runat="server" CssClass="discount-tb pull-right" />
</th>
</tr>
</table>
<div class="clearfix"></div>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<img src="/images/shows/<%# Eval("imageURL")%>.jpg" alt="<%#Eval("BasketItemTitle")%>" class="basketimg" /></td>
<td class="cart-title">
<%#Eval("BasketItemTitle")%>
<br />
<%# String.Format("{0:dddd} {1} {0:MMMM yyyy} | {0:HH:mm}", Eval("PerformanceStarts"), Eval("OrdinalDay"))%>
<br />
<%# Eval("VenueTitle")%>
</td>
<td>
<%#Eval("PriceBandType")%>
<br />
# <%# String.Format("{0:c}", Eval("PriceBandValue"))%>
</td>
<td>
<asp:DropDownList ID="TicketQuantityDDL" runat="server" CssClass="qtyddl" AutoPostBack="true" ClientIDMode="Static" />
</td>
<td class="cart-total"><%#String.Format("{0:c}", Eval("BasketItemTotalValue"))%></td>
<td>
<asp:LinkButton ID="RemoveLinkBtn" runat="server" CssClass="cart-remove" CommandName="RemoveBasketItem" CommandArgument='<%#Eval("BasketItemID")%>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
Since the Dropdown in inside the repeater, you can try the following Option instead.
Add OnItemCommand to the repeater. This should definitely Trigger the Event on selection Change in the Dropdown. Then in the OnItemCommand you Need to cast the Sender to DropDownList to be able get the selected value of the dropdown
You have to register the dropdown list event like this.
protected virtual void OnRepeaterItemCreated(object sender, RepeaterItemEventArgs e)
{
DropDownList dropdown = (DropDownList)e.Item.FindControl("TicketQuantityDDL");
dropdown.SelectedIndexChanged += TicketQuantityDDL_SelectedIndexChanged;
}
And also add this piece of code in your repeater.
OnItemCreated="OnRepeaterItemCreated"
And then you can do the selected index changed event like this.
protected void TicketQuantityDDL_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList mydropdownlist = (DropDownList)sender;
Response.Write(mydropdownlist.SelectedValue);
}
protected void dropdownlist1_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList ddlListFind = (DropDownList)sender;
ListViewItem item1 = (ListViewItem)ddlListFind.NamingContainer;
DropDownList getDDLList = (DropDownList)item1.FindControl("dropdownlist1");
Label lblMessage = (Label)item1.FindControl("lblMsg");
lblMessage.Visible = true; lblMessage.Text = "dropDown text is : " + getDDLList.SelectedItem.Text + " and value is : " + getDDLList.SelectedItem.Value;
}

How to hide an item in datalist

I want to hide an item in datalist according to some condition suing ItemBound, how ?
Wrap a PlaceHolder control around the entire content of the ItemTemplate.
Then in your ItemDataBound event, you could do something like:
Protected Sub myDataList_ItemDataBound(sender As Object, e As System.Web.UI.WebControls.DataListItemEventArgs) Handles myDataList.ItemDataBound
If Not Value = Value2 Then
Ctype(e.Item.FindControl("myPlaceHolder"), PlaceHolder).Visible = False
End If
End Sub
A better approach (however I've not had chance to test it), would be to hide the whole item using e.Item.Visible. This way no HTML table elements would be rendered for the item. It would also mean no PlaceHolder would have to be added.
Protected Sub myDataList_ItemDataBound(sender As Object, e As System.Web.UI.WebControls.DataListItemEventArgs) Handles myDataList.ItemDataBound
If Not Value = Value2 Then
e.Item.Visible = False
End If
End Sub
Alternatively, if the values you are checking are from a database source, you could filter the items out before binding:
WHERE Value=#Value2
A simple solution could be to set the visibility of your Item container by evaluating your desired condition in your ItemTemplate:
<ItemTemplate>
<div id="itemdiv" visible='<%# (Convert.ToInt32(Eval("YourValue")) == 5) %>' runat="server">
<%# Eval("SomeOtherValue") %>
</div>
</ItemTemplate>
My example uses a constant but you could use any variable in scope.
Pitfall!
DataList will insist to create empty rows for hidden items, so you may have to use ListView instead to fully control creating your filtered itemlist.
Update
Using a ListView instead will only create rows for visible items:
<ItemTemplate>
<tr id="itemdiv" visible='<%# (Convert.ToInt32(Eval("YourValue")) == 5) %>' runat="server">
<td><%# Eval("SomeOtherValue") %></td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table border="1">
<tr runat="server" id="itemPlaceholder" />
</table>
</LayoutTemplate>

Dynamically change the number of header cells in listview LayoutTemplate

I'm trying to dynamically create a listview. on reports.aspx user selects a bunch of checkboxes. On the next page, user sees reports.aspx, and should see a table with columns of the checkboxes he selected. My idea was to create a listview, then dynamically change the header row of the LayoutTemplate, then change the select statement depending on which columns selected. This is what I have:
<asp:ListView runat="server" ID="ReportListView" DataSourceID="ReportListViewSDS">
<LayoutTemplate runat="server">
<table runat="server">
<tr runat="server">
<%
' Doesn't work because code blocks (<%%>) aren's allowed inside <LayoutTemplate> blocks
'For Each i As String In Request.Form
'Response.Write("<th>" & Request.Form(i) & "</th>")
'Next
%>
</tr>
</table>
<asp:PlaceHolder runat="server" ID="itemPlaceHolder" />
</LayoutTemplate>
...
Problem is that this doesn't work because i can't put a code block (<%%>) inside the LayoutTemplate. Is there a way in the code behind to edit the LayoutTemplate, or another way to cycle through the Request.Form vars and build the table header row with it?
Thanks for any advice/direction!
-Russ
Try using the ItemTemplate for the binding syntax instead of the layout template. I believe the layout template is strictly for layout.
Also, it looks like you're using classic ASP code blocks. ASP.NET code blocks look like this:
For data binding:
<%# Eval("<COLUMN NAME>")%>
For other cases not involving data binding:
<%= Request.QueryString["Hello"] %>
Since the control is already a server side control, try giving the and id and then modifying the header on pre-render:
<asp:ListView runat="server" ID="ReportListView" DataSourceID="ReportListViewSDS">
<LayoutTemplate runat="server">
<table runat="server">
<tr id='trCustomHeader" runat="server">
Then in your code behind, attach this logic to the listview's pre-render
ReportListView_PreRender(...)
{
TableRow tr = ReportListView.FindControl("trCustomerHeader");
TableCell tempCell = new TableCell();
tempCell.Text = ...
tr.Cells.Add(tempCell);
}
I just created a separate table on the page, outside of the listview, that was the simple way of doing it.
<asp:Table ID="HeaderTable" runat="server">
<asp:TableHeaderRow ID="HeaderTableHeaderRow" runat="server" />
</asp:Table>
<asp:ListView ...>
...
</asp:ListView>
Then in the code behind:
For Each i As String In Request.Form
If i.IndexOf("checkbox_") = 0 Then
Dim c As New TableHeaderCell()
Dim l As New LinkButton()
l.Text = i.Substring(Len("checkbox_"))
c.Controls.Add(l)
c.CssClass = "customreport"
HeaderTableHeaderRow.Cells.Add(c)
End If
Next
Pretty simple. So I didn't have to use a LayoutTemplate at all.

How can I add group heading rows to a data bound GridView

I have a GridView with three databound columns, like this:
<Columns>
<asp:BoundField HeaderText="Type" DataField="Type" />
<asp:BoundField HeaderText="Amenity" DataField="Amenity" />
<asp:BoundField HeaderText="Distance" DataField="Distance" DataFormatString="{0:0.00} Km" />
</Columns>
Records are sorted by type, and I want to remove the Type column, but then insert a header row for each type when the value of type for the next set of rows changes. How can I do this?
This is not the prettiest, but it solves the problem without going outside of the GridView paradigm. Thanks to carlj for Adding (or Inserting) Subheader Rows into a Gridview
Dim _currentAmenityType = String.Empty
Protected Sub amenitiesGrid_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles amenitiesGrid.RowDataBound
If (e.Row.RowType = DataControlRowType.DataRow) Then
Dim drv = e.Row.DataItem
If (drv("Type") <> _currentAmenityType) Then
_currentAmenityType = drv("Type")
Dim parentTable = TryCast(e.Row.Parent, Table)
If Not parentTable Is Nothing Then
Dim row = New GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal)
Dim cell = New TableCell()
cell.ColumnSpan = amenitiesGrid.Columns.Count
cell.Width = Unit.Percentage(100)
cell.Style.Add("font-weight", "bold")
cell.Style.Add("background-color", "#c0c0c0")
cell.Style.Add("color", "white")
Dim span = New HtmlGenericControl("span")
span.InnerHtml = _currentAmenityType
cell.Controls.Add(span)
row.Cells.Add(cell)
parentTable.Rows.AddAt(parentTable.Rows.Count - 1, row)
End If
End If
End If
End Sub
With the standard GridView control, I believe that you cannot dynamically add extra rows once the control has been databound, so you would need to alter the datasource prior to databinding. However, this probably isn't a very good solution to what you need.
I think that using the GridView control might not be your best option in this situation, as the HTML that is rendered would be
<table>
<thead>
<tbody>
So adding extra "header" rows between rows wouldn't actually be Header rows. They would simply be an extra row <tr><td>. Knowing this, I think that you might be better using a Repeater control, building up the HTML for the table within the control, and then have a Placeholder within the Content Template which you can use to mimic adding a new Row.
e.g.
<table>
<asp:Repeater ID="rpt1" runat="server">
<HeaderTemplate>
<thead>
<tr>
<th> </th>
<th>Amenity</th>
<th>Distance</th>
</tr>
</thead>
<tbody>
</HeaderTemplate>
<ItemTemplate>
<asp:PlaceHolder id="phRow" runat="server" />
<tr>
<td> </td>
<td>Amenity Value</td>
<td>Distance Value</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</tbody>
</FooterTemplate>
</asp:Repeater>
</table>
In your code, loop through each Item in the Repeater. If the Type is different, then add a Literal to the Placeholder in that Row.
Literal lt = new Literal() { Text = "<tr><td colspan='3'>Type Value</td></tr>" };

Resources