Dynamically change the number of header cells in listview LayoutTemplate - asp.net

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.

Related

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>

Hiding a table row (the ItemPlaceholderID row) in a ListView

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.

Cannot get inner content of '' because the contents are not literal

I am trying to iterate over the contents of a Repeater containing an html table, and use the contents of each cell. This works fine for standard HTML components, but blows up when I try to use a control. The code below will print the value of the first cell, "Item #1", but will throw an HttpException when trying to access .InnerText of the second cell. The error is as follows:
Cannot get inner content of because the contents are not literal.
I have tried to use RenderControl via this solution found elsewhere, which failed for two reasons; 1) it rendered the entire HTML of the first cell and 2) it still blew up when trying to access the second cell with the following message:
'LinkButton1' of type 'LinkButton' must be placed inside
a form tag with runat=server
Is there an easy way to get the LinkButton text I'm after? Repeater code and C# can be found below.
The repeater code:
<asp:Repeater ID="Rep1" runat="server">
<HeaderTemplate>
<table id="Table1" class="data">
</HeaderTemplate>
<ItemTemplate>
<tr id="Row" runat="server">
<td>Item #1</td>
<td><asp:LinkButton ID="LinkButton1" OnClick="DoSomething"
Text="Item #2" runat="server" /></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
The C#:
foreach (RepeaterItem item in Rep1.Items)
{
HtmlTableRow row = item.Controls[0].FindControl("Row") as HtmlTableRow;
foreach (HtmlTableCell cell in row.Cells)
{
if (cell.InnerText != string.Empty)
{
Console.WriteLine(cell.InnerText);
}
}
}
For anyone else who arrives here:
You cannot get InnerHtml or InnerText on a Control unless its contents are literal, i.e. there are not any server controls or controls with runat="server" inside it that required rendering
Iterate/recurse through any child controls in the Controls collection. When you get to a leaf node (no children), then access that control's InnerText property.
RenderControl C#
StringWriter iSW = new StringWriter();
HtmlTextWriter iHTW = new HtmlTextWriter(iSW);
iDiv.RenderControl(iHTW);
string iS = iSW.GetStringBuilder().ToString();
Include
public override void VerifyRenderingInServerForm(Control control)
{
return;
}
RenderControl VB
Dim iSW As New StringWriter
Dim iHTW As New HtmlTextWriter(iSW)
iDiv.RenderControl(iHTW)
Dim iS As String = iSW.GetStringBuilder().ToString()
Include
Public Overrides Sub VerifyRenderingInServerForm(ByVal control As Control)
Return
End Sub
Also, make sure that you do not have anything such trying to access another view control while another view control is activated in a multi view control.
string value = ((Literal)(cell.Controls[0])).Text

nested repeater pass value in header template

I have a nested repeater and i want to pass value in its header. Here is my code so far..
The main problem is the id of the control in header template is also coming from code behind.
<asp:Repeater ID="RptrProgCategory" runat="server">
<ItemTemplate>
<asp:Repeater ID="RptrPrograms" runat="server">
<HeaderTemplate><input type="hidden" id="<%= questvalue%>"/></HeaderTemplate>
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "cat") %>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
I want value in questvalue from code behind. Any idea how to achieve this?
Edit: I wanted to put this value in a DataTable and bind that value in Repeater bcoz i want output like this may be <%# DataBinder.Eval(Container.DataItem, "questvalue") %> instead of <%= questvalue%>..but in tht case i am not able to find the control
Category1(id of hidden field )
subcat1
subcat2
subcat3
Category2(id of hidden field)
subcat4
subcat5..and so on..
Repeater mainRepeater = this.Page.FindControl("RptrProgCategory") as Repeater;
Repeater nestedRepeater = mainRepeater.FindControl("RptrProgCategory") as Repeater;
You can then do a FindControl in nestedRepeater for questValue.
Add a runat='server' to questvalue so that you can access it in code behind.
I am writing this from memory, syntax might not be correct but it should get you off in the right direction.
set the id of the control in the repeater to something like mycontrolId - and then on OnItemDataBound - (or even OnItemCreated) use findcontrol("mycontrolId") - and then change the id of the control to your questvalue param.

How to pass custom value to asp.net control on aspx page?

I need to generate the following in ASP.NET page. What's the best, easiest way to do that?
Simplified example. So to custom property I need to pass string which would include index (i) to the property of the control. I could do it from codebehind, but it would be simpler and easier if I could keep it in .aspx file.
<table>
<%
for( var i = 0; i < 10; i++ )
{
%><tr>
<td>
<cc1:CustomControl runat="server" CustomProperty="SomeText[<% i %>]"/>
</td>
</tr>
<% } %>
</table>
Essentially I need to pass a custom, not predetermined value to the asp.net control.
This probably won't work how you expect it to. Instead, add a place holder like this:
<table>
<asp:PlaceHolder id="RowPlaceHolder" runat="server" />
</table>
And then in your code behind:
for (int i = 0;i<10;i++)
{
var tr = new HTMLTableRow();
var td = new HTMLTableCell();
var Custom = (CustomControl)LoadControl("MyControl.ascx");
Custom.id="Custom" + i.ToString();
Custom.CustomProperty = "SomeText[" + i.ToString() + "]";
td.Controls.Add(Custom);
tr.Controls.Add(td);
RowPlaceHolder.Controls.Add(tr);
}
Going deeper, if the number 10 really is hard-coded, you'll find things much easier to deal with in the long run if you just go ahead and copy 10 entries for your control into the aspx markup manually. Dynamic controls in ASP.Net webforms are rife with pitfalls and gotchas.
If the number comes from some legitimate datasource, then you're likely better off actually using that datasource and binding it to a data control like a repeater.
<%= i %>
Should work for you.
You could rock out with a repeater and use the Container.ItemIndex,
<asp:Repeater ID="rpt" runat="server">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<cc1:CustomControl runat="server" CustomProperty="SomeText[<%# Container.ItemIndex %>]"/>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>

Resources