Databinder inside a "for" loop in an ASPX page - asp.net

I'm still trying to understand how I can take advantage from DataBinder ( Is there a way to use a DataBinder.Eval statement as an index of a specific array in an ASPX page? ).
I'm currently building tables with the help of repeater and I would like to use a loop defining the Item label dynamically, to allow more interactions.
Currently, this test code is working:
<asp:Repeater id="Fish" runat="server">
<table>
<ItemTemplate>
<tr>
<td><%# Container.DataItem("ITEM")%></td>
<td><%# Container.DataItem("AGG")%></td>
</tr>
</ItemTemplate>
</table>
</asp:Repeater>
But as you can imagine, this type of structure doesn't allow to choose dynamically the columns that are displayed from the columns to be ignored.
I was thinking that by using a "for" loop structure, I would be able to choose dynamically which column could be displayed. And I tried this as a test:
Public Test_id() As String
Public Test_idp As String
<% Test_id = New String() {"id", "Agg"} %>
<asp:Repeater id="Fish" runat="server">
<table>
<ItemTemplate>
<tr>
<% For Each Test_idp as String In Test_id%>
<td><%# Container.DataItem(Test_idp)%></td>
<% Next Test_idp%>
</tr>
</ItemTemplate>
</table>
</asp:Repeater>
which is not working... and is granted by the following error message:
Overload resolution failed because no Public 'Item' is most specific for these arguments:
'Public Overrides ReadOnly Property Item(name As String) As System.Object': Not most specific.
'Public Overrides ReadOnly Property Item(i As Integer) As System.Object': Not most specific.
Any idea?
Edit:
to answer Mike C's question, I have tried DataBinder.Eval(Container.DataItem, Test_idp) instead of Container.DataItem(Test_idp). It still does not work, but the error is different:
System.ArgumentNullException: value cannot be null

Test_Idp is an Object (since it wasn't declared otherwise).
Therefore, the compiler cannot figure out which of those overloads to call.
You need to explicitly declare it As String.

You can use a nested repeater for the columns.
<asp:Repeater id="Fish" runat="server">
<table>
<ItemTemplate>
<tr>
<asp:Repeater id="columns" runat="server">
<ItemTemplate>
<td><%# ((RepeaterItem)Container.Parent.Parent).DataItem("ITEM")%></td>
<td><%# ((RepeaterItem)Container.Parent.Parent).DataItem("AGG")%></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</table>
</asp:Repeater>

Related

Allocate number of columns to repeater when the page is loaded

I am binding a dataset to asp repeater.
<asp:Repeater id="rptDataset" runat="server">
<table>
<HeaderTemplate>
<tr>
<th>Col1</th>
<th>Col2</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Eval("c1") %></td>
<td><%# Eval("c2") %></td>
</tr>
</ItemTemplate>
</asp:Repeater>
Now I have varying number of columns, that won't be known until the page is loaded. So I'm modifying the header like this
<HeaderTemplate>
<tr>
<th>Col1</th>
<th>Col2</th>
<% for (int i=0; i<NoOfColumn; i++){
Response.Write("<th>ColN</th>")
} % >
</tr>
</HeaderTemplate>
Which correctly showed all column headers.
What should I modify <ItemTemplate> so it could show all the column data?
I've tried something like
<% Response.Write("<td><%# Eval("c1") %></td>") %>
which would result an error due to the nested <% %> sign.
Also tried
<% Response.Write(DataBinder.Eval(Container.DataItem, "c1")) %>
but it said Container cannot be resolved.
So you have two options, quick hack and proper pretty solution.
Quick hack
Create all necessary html in a string and output it in Literal control. Make sure to use PassThrough mode not to have html encoded:
<ItemTemplate>
<%# GetDataRow(Container.DataItem) %>
</ItemTemplate>
protected string GetDataRow(object dataItem)
{
StringBuilder output = new StringBuilder();
output.Append("<tr>");
for (int i=0; i<NoOfColumn; i++)
{
output.Append("<td>");
output.Append(DataBinder.Eval(dataItem, "c"+i));
output.Append("</td>");
}
output.Append("</tr>");
return output.ToString();
}
Pretty solution
Define your own custom template implementing ITemplate. A complete example of how this is done can be found here. It will take more work, but, as it's usually the case with pretty solutions, result will be much more readable, maintainable and reusable.
Finally I have it solved by applying nested Repeater.
<asp:Repeater runat="server" ID="rptRow">
<ItemTemplate>
<tr>
<td><%# Eval("c1") %></td>
<td><%# Eval("c2") %></td>
<asp:Repeater runat="server" ID="rptCol">
<ItemTemplate>
<td><%# Eval("cn") %></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>

ListView in ASP.NET and DataKeyNames

I have a ListView, which is bound with a list of 'A', which looks like this:
Class A
Property Id as Integer
Property TestStringA as String
Property B as B
End Class
Class B
Property Id as Integer
Property TestStringB as String
End Class
In the ListView i can refer to 'link property' values (whats the correct term for this?):
<asp:ListView runat="server" ID="lwTest" ItemType="A">
<LayoutTemplate>
<tr runat="server" id="itemPlaceHolder"></tr>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><%# Item.TestStringA %></td>
<td><%# Item.B.TestStringB %></td> (this is what I mean)
</tr>
</ItemTemplate>
</asp:ListView>
This is also working when using the Eval-method (if unable to use 'ItemType'):
<%# Eval(B.TestStringB) %>
I want to loop the ListView´s items and use the values from the container, without saving them in hidden fields (isn't that the purpose of 'DataKeyNames'?). The issue is, that I can not refer to a property of another object by it's link-attribute/property (in the example B.Id) in DataKeyNames. When I do like this, I get an error telling me that there´s no property called 'B.Id'):
<asp:ListView runat="server" ID="lwTest" ItemType="A" DataKeyNames"Id, B.Id">
<LayoutTemplate>
<tr runat="server" id="itemPlaceHolder"></tr>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><%# Item.TestStringA %></td>
<td><%# Item.B.TestStringB %></td> (this is what I mean)
</tr>
</ItemTemplate>
</asp:ListView>
Does anyone know if it is possible to do this? I know that I can add readonly properties returning the value directly to the bound object (but I would like to avoid this if possible):
Class A
Property Id as Integer
Property TestStringA as String
Property B as B
ReadOnly Property BId as Integer
Get
Return B.Id
End Get
End Property
End Class
Class B
Property Id as Integer
Property TestStringB as String
End Class
Thank you in advance!
First of all, you can only use direct properties of a class as your DataKeyNames. With the Eval instruction you can go deeper(but I believe only one level).
What you can do is change what is shown in your list view through the code-behind.
To do this, add an ItemDataBound event.
The data for the item that is being created can be found in e.Item.DataItem end will be of type A (do cast it before using).
You should be able to access your controls directly through e.Item (don't know which properties to use by heart on this one) and once you've found the correct cell, you can set any text to it you want and since you have the bound instance of A itself you can call any property or sub property you want without restrictions.
This function gets called for every entry in your ListView, so they should only provide you data of the current entry.
Hope this helps you solve your issue.

How can I use a FOR loop inside a REPEATER to create <TD>'s in a <TABLE>?

I have a repeater that is bound to a List(of T) object collection. It's a list of Inventory objects. Each Inventory object also contains a List(of T) which is a list of Date / Inventory Count pairs. When the repeater is creating a table, I need to create a TD for each of the Date / Inventory Counts. Since the number of Date / Inventory Counts is not set until runtime (using NumWeeks variable), I need to vary the number of TD's in my repeater. This is essentially what I want:
<asp:Repeater ID="rptReport" runat="server">
<ItemTemplate>
<tr>
<td><%#DataBinder.Eval(Container.DataItem, "Department")%></td>
<td><%#DataBinder.Eval(Container.DataItem, "Description")%></td>
<% For x = 0 To NumWeeks%>
<td><%#DataBinder.Eval(Container.DataItem, "Values")(x).Value()%></td>
<% Next%>
</tr>
</ItemTemplate>
</asp:Repeater>
You need to place another repeater inside this repeater and assign the datasource to that inner repeater in the "ItemDataBound" event of the parent repeater. This should solve your issue.
Hope this is helpful!!
Another option would be that you create a UserControl containing a Repeater for the inner loop. You can assign the "Values" as a property of the UserControl. Something like this:
<asp:Repeater ID="rptReport" runat="server">
<ItemTemplate>
<tr>
<td><%# Eval("Department") %></td>
<td><%# Eval("Description") %></td>
<uc:WeekControl NumWeeks="<%#NumWeeks %>" Values='<%# EVal(Values)%> />
</tr>
</ItemTemplate>
</asp:Repeater>
I needed that for loop I tried another approach and it worked.
in my .cs file I created a public string and used it inside the repeater. It works fine.
public string somethingloop
for (int i = 0; i < dolar; i++)
{
somethingloop += "<i class='fa fa-dollar icon highlighted'></i>";
}
and in repeater inside anywhere in the itemtemplate
<asp:Repeater ID="rptReport" runat="server">
<ItemTemplate>
<%= somethingloop %>
<tr>
<td><%# Eval("Department") %></td>
<td><%# Eval("Description") %></td>
<uc:WeekControl NumWeeks="<%#NumWeeks %>" Values='<%# EVal(Values)%> />
</tr>
</ItemTemplate>
</asp:Repeater>

How to dynamically assign datasource to listview

I am having a problem with dynamically assigning datasource to listview.
For example I have list of receivedBonuses(Bonus), receivedLeaves(Leave) and I want listview to display those list items depending on what link button user clicked.
Researching internet and stackoverflow.com i found 3 solutions:
Using repeater inside the listview. But in my case, I could not apply it to my case and i got totally confused
Using nested listviews. I tried to do like this:
<asp:ListView ID = "bonuses" runat="server" DataSource ='<%# Eval("received_bonuses") %>' >
<ItemTemplate>
<tr>
<td><%# Eval("bonus_desc")%></td>
<td><%# Eval("bonus_type")%></td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table>
<tr>
<th>Bonus Description</th>
<th>Bonus Received Date</th>
</tr>
<tr ID="itemPlaceholder" runat="server" />
</table>
</LayoutTemplate>
<table>
<tr>
<th>Bonus Description</th>
<th>Bonus Received Date</th>
</tr>
<tr ID="itemPlaceholder" runat="server" />
</table>
</LayoutTemplate>
</asp:ListView>
<br />
and on back code I tried to write like this:
protected void dataBound(object sender, ListViewItemEventArgs e)
{
this.DataBindChildren();
}
It didn't give any errors it just didn't work.
Using data pager
I have no idea how to apply it to my case.
Any help is appreciated.
Thanks a lot.
All you have to do on the server side is change the DataSource or DataSourceID property and call DataBind on the ListView.
You have to make sure when using <%# Eval("") %> syntax that the objects you are binding to have those properties that are named in the Eval. So you may have a problem with with switching datasources when your properties are prepended with the typename and underscore.
That being said. There are 2 options you have an changing a data source. In the click event of the button or whatever switching mechanism you are using you can just write something like.
Not using a DataSource in the markup:
List<Bonus> bonusList = GetBonuses();
MyListView.DataSource = bonusList;
MyListView.DataBind();
Using a DataSource in the markup:
//where bonus list would be the id of the datasource in the markup
MyListView.DataSourceID= "BonusList";
MyListView.DataBind();
Do you need to do this dynamically? If you only have "bonuse" and "leave" can you not create two listviews and then just do display logic to visible=true/false the listview based upon the link button clicked?

Seeking Advice: Updating a FormView Based on DropdownList Value

Greetings!
I'm looking for some advice regarding an approach to displaying data in a FormView based on a selection of a DropDownList within that FormView control. For example, I have a UserControl with the following:
<asp:XmlDataSource ID="xdsMyXmlData" runat="server" EnableCaching="false" XPath="Root/Membership" />
<asp:FormView ID="fvwMyFormView" runat="server" DataSourceID="xdsMyXmlData">
<ItemTemplate>
<div>
<h2><%# XPath("Title") %></h2>
<fieldset>
<asp:DropDownList ID="ddlMemberTypes" runat="server" DataSource='<%# XPathSelect("MenuItems/*") %>'></asp:DropDownList>
</fieldset>
<table>
<thead>
<tr>
<th><%# XPath("Columns/Name") %></th>
<th><%# XPath("Columns/Age") %></th>
<th><%# XPath("Columns/DateJoined")%></th>
</tr>
</thead>
<tbody>
<asp:Repeater ID="rptMembershipInfo" runat="server" DataSource='<%# XPathSelect("Members/*") %>'>
<ItemTemplate>
<tr>
<th><%# XPath("Data/Name") %></th>
<td><%# XPath("Data/Age") %></td>
<td><%# XPath("Data/DateJoined") %></td>
</tr>
</ItemTemplate>
</asp:Repeater>
</tbody>
</table>
</div>
</ItemTemplate>
</asp:FormView>
The UserControl's OnLoad() looks like this so far:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string l_XmlData = MyControllerClass.GetMembershipTableXml(0);
xdsMyXmlData.Data = l_XmlData;
}
I would like to be able to pass the value of the DropDownList's selected item into GetMembershipTableXml() in order to retrieve the corresponding XML and then use it to populate the values of the FormView. What would the best way be to do this? Doing a Response.Redirect back to the current page using the selected DropDownList value as a query string variable? I'm hoping there's a better approach. What do you think?
You can create an event for OnSelectedItemChanged on your DropDownList; when this occurs, you can grab the selected item, and call your GetMembershipTableXml function.
Finally, dont forget to call DataBind on your FormView control to update the values :)
I think that's what you're after, hopefully it helps!

Resources