Here is my Code Structure.
Repeater Markup:
<asp:Repeater runat="server" ID="RPMenu" DataSource='<%# Menues.GetAllMainMenu() %>'>
<ItemTemplate>
<%# Eval("MenuName") %><br />
<asp:Repeater runat="server" ID="RPMenuUnder" DataSource='<%# Menues.GetAllMainMenu(Convert.ToInt32(Eval("MenuID"))) %>'>
<ItemTemplate>
<%# Eval("MenuName") %><br />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Menu Class :
public static List<Menu> GetAllMainMenu(int parrentID = 0)
{
using (Scooterfrøen_Entities db = new Scooterfrøen_Entities())
{
return db.Menu.Where(i => i.ParentMenuID == parrentID).ToList();
}
}
Database table:
MenuID | MenuName | MenuDescription | ParrentMenuID | MenuUrl
I have several rows where ParentMenuID IS 0 and NOT NULL.
But for some reason Repeater control does not list anything on the site.
What could be the reason why Repeater control don't show anything?
I think page still requires to bind even though you have provided datasource in markup.
Write Page.DataBind(); in your page load event:
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
How to Use nested Repeater:It seems you have to handle ParentRepeater_ItemDataBound event to bind child repeater control.
Checkout this article available here.
You have to set values and bind repeater
RPMenu.DataSource=<value you want to set>;
RPMenu.DataBind();
A combination of the two existing answers is closest to true.
Rather than run Page.DataBind(), which may have unintended side-effects, run RPMenu.DataBind() in the Page_Load event handler. It will use the DataSource which is set in code-front.
The inner Repeater should automatically be bound. You only need to explicitly databind the outermost Repeater.
Related
I'm using an asp repeater to display a list of names and I want to display the current letter as a type of grouping header, like in an index page.
The data is sorted alphabetically before binding, but i'm finding it difficult to insert the 'A' and 'B' before the names are displayed.
Add a Panel control to your ItemTemplate with visibility set to False. When you are binding the repeater (assuming you are subscribing to the ItemDataBound event), run a check to see if the first letter has changed. If it has, set the panel's visibility to true and print out the letter.
Let me know if you need some example code.
EDIT: EXAMPLE CODE
For clarity sake, "AlphaHeaders" is what we will call the "A", "B", "C" letters that we want to display
aspx code
The Repeater will look like so:
<table>
<asp:Repeater id="rptRepeater" runat="server" OnItemDataBound="rptNames_OnItemDataBound">
<ItemTemplate>
<asp:Panel id="pnlAlphaHeader" runat="server" visible="False">
<tr><td><asp:Label id="lblAlphaHeader" runat="server" /></td></tr>
</asp:Panel>
<tr><td><asp:Label id="lblName" runat="server" /></td></tr>
</ItemTemplate>
</asp:Repeater>
</table>
aspx.cs code
First, you need a variable that holds the current AlphaHeader:
private string _AlphaHeaderCurrent = String.Empty;
Then you will do your work on the repeater's OnItemDataBound event:
protected void rptNames_OnItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
if ((e.ItemType==ListItemType.Item) || (e.ItemType==ListItemType.AlternatingItem)) {
string name = e.Item.DataItem("Name").ToString();
//check if the first letter of the current name is new. If it is new, we print out the header
if(!name.StartsWith(this._AlphaHeaderCurrent)) {
this._AlphaHeaderCurrent = name.SubString(1);
((Panel)e.ItemFindControl("pnlAlphaHeader")).Visible = true;
((Label)e.Item.FindControl("lblAlphaHeader")).Text = the._AlphaHeader;
}
((Label)e.Item.FindControl("lblName")).Text = name;
}
}
You sort before you bind.
That is, bind the sorted result set.
Without seeing the values you have, however, it is not possible to tell exactly how to do so.
Update - from your comment, I would say you need to change your binding source to something like Dictionary<string,IList<string>>.
With such a structure, you could bind by key (sorted) and sublist (secondary sort).
You need to sort your data before bind it to the repeater.
You can use nested repeaters (repeater inside repeater). Like category and subcategory.
In first repeater you can list all your names and make a condition starts with A. Then in sub repeater you can show all names. You will also use itemdatabound event to bind second repeater.
<asp:Repeater id="rptFirstLetters" runat="server" OnItemDataBound="rptChars_OnItemDataBound">
<ItemTemplate>
<div>"<%#Eval("letters") %>"
<asp:Repeater id="rptNames" runat="server">
<ItemTemplate>
<%#Eval("names") %>
</ItemTemplate>
</asp:Repeater>
</div> // bind all letters
</ItemTemplate>
Not really a nice way of doing this to be honest, repeaters generally result in ugly code I've found. The hierarchical approach from kad1r is probably the nicest if you can set it up, but there are alternatives, depending on your implementation details; I kind of prefer this in some ways as it keeps the markup very clean, and as I have a non-programmer design guy that is a plus for me.
ASPX:
<%# Page language="C#" Src="test.CODE.cs" Inherits="test_Page" %>
<asp:Repeater ID="TestRepeater" runat="server">
<ItemTemplate>
<asp:PlaceHolder Visible='<%# Eval("IsFirstInGroup") %>' runat="server">
<strong><%# Eval("Initial") %></strong><br/>
</asp:PlaceHolder>
<%# Eval("Name") %><br/>
</ItemTemplate>
</asp:Repeater>
CODE BEHIND:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public class test_Page : Page
{
protected Repeater TestRepeater;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
String[] names = new String[]
{
"Alpha, John",
"Altman, Mary",
"Asher, Cyril",
"Bachman, Turner",
"Beta, Rob",
"Bexman, Norah",
"Clark, Freddy"
};
List<_DispItem> l = new List<_DispItem>();
for (int i = 0; i < names.Length; i++)
l.Add(new _DispItem() { Name = names[i], IsFirstInGroup = (i == 0 || names[i - 1][0] != names[i][0]) });
TestRepeater.DataSource = l;
TestRepeater.DataBind();
}
private class _DispItem
{
public String Name { get; set; }
public String Initial { get { return Name.Substring(0, 1); } }
public bool IsFirstInGroup { get; set; }
}
}
I have a list of files (stored in a database) that I would like the user to be able to upload in asynchronous callbacks.
I have got as far as displaying AJAX Control Toolkit's AsyncFileUpload controls next to the name of each file I'm expecting:
<asp:Repeater ID="SourceTables" runat="server">
<ItemTemplate>
<tr>
<td>
<%#DataBinder.Eval(Container.DataItem, "LongName")%>
</td>
<td>
<ajax:AsyncFileUpload runat="server" ClientIDMode="AutoID" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
All of these async upload controls will end up calling the same method in code behind, which is fine, but when I come to save the file I need some way to identify which control is causing the postback (i.e. which of the several files has just been uploaded). However, I have been unable to set the ID dynamically inside the repeater (I believe it is only possible from code behind).
Clearly this doesn't work:
<ajax:AsyncFileUpload ID=<%#DataBinder.Eval(Container.DataItem, "ShortName")%> runat="server" ClientIDMode="AutoID" />
Is there another way I can set the ID of each of the upload controls dynamically inside the repeater (or otherwise) or another approach I can take?
You can add controls dynamically in "ItemDatabound" event of the repeater.
How are you handling the postback events?
If you are using the UploadedComplete event of the AsyncFileUpload control, you get the uploaded file through the AsyncFileUploadEventArgs object.
You could also cast sender to your AsyncFileUpload control and then get the file through the PostedFile property.
protected void AsyncFileUpload1_UploadedComplete(object sender, AsyncFileUploadEventArgs e)
{
string savePath = MapPath("~/Uploads/" + Path.GetFileName(e.filename));
}
Something like this
<ajax:AsyncFileUpload runat="server" id="afuMyUpload" ClientIDMode="AutoID" OnClientUploadComplete="MyFunction" />
foreach (Control c in SourceTables.Items)
{
var myUpload = c.FindControl("afuMyUpload") as AsyncFileUpload;
//Do stuff with your control
}
You can't bind the ID property of controls. You will need to create the control in code behind and add it to the repeater.
Example:
protected void GridDataBind(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow) {
IEnumerable list = PremiumUnitFees.PremiumAmenities.ObtainFeesForProject(IntranetUser.Current.ProjectId);
foreach (PremiumUnitFees.PremiumAmenities feature in list) {
e.Row.Cells(3).Controls.Add(new CheckBox {
ID = feature.Id.ToString(),
Text = feature.NickName,
Checked = PremiumUnitFees.PremiumUnitView.IsUnitPremium(feature.Id, Convert.ToInt64(DataBinder.Eval(e.Row.DataItem, "Id")))
});
}
}
}
I created LinkButton that located inside of Repeater Control.
CategoryID is a variable in LinkButton Control that have to get value after Repeater Control was bound to data. But CategoryID always get zero.
I have the following ASP and C# code:
<asp:Repeater ID="rpt1" runat="server"
OnItemDataBound="rpt1_ItemDataBound"
OnItemCommand="rpt1_ItemCommand">
<ItemTemplate>
<div>
<%# Eval("Name") %>-<%# Eval("CollectionType")%>
<asp:LinkButton ID="LinkButton1" runat="server" Text="[edit item]"
PostBackUrl='AddItem.aspx?CategoryID=<%# Eval("CollectionID")%>' />
</div>
</ItemTemplate>
</asp:Repeater>
Code behind:
public void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
List<GlassesCollection> gc = BL.GetDataBL.GetCollection();
rpt1.DataSource = gc;
rpt1.DataBind();
}
}
Any idea why CategoryID variable doesn't get any value and how can I fix the problem?
A server control parameter cannot contain a mixture of literal text and evaluated expressions.
The code you have will literally be posting back to AddItem.aspx?CategoryID=<%# Eval("CollectionID")%> and it will not be evaluating the code within the angle brackets.
You need to change your parameter like so
PostBackUrl='<%# "AddItem.aspx?CategoryID=" + Eval("CollectionID")%>' />
How can i get current MainNavigationMenu hyprelink in code behind and check if is current menu clicked then i will change him default CSS.
I try with this code but is always null
HyperLink mainNavigationMenu = siteMapAsBulletedList.FindControl("MainNavigationMenu") as HyperLink;
full repeater code:
<asp:Repeater runat="server" ID="siteMapAsBulletedList" DataSourceID="smdsMenu">
<HeaderTemplate>
<li><asp:HyperLink ID="MainNavigationMenu" runat="server" NavigateUrl='<%#SiteMap.RootNode.Url%>'
Text='<%#SiteMap.RootNode.Title%>'></asp:HyperLink></li>
</HeaderTemplate>
<ItemTemplate>
<li><asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%#Eval("Url")%>' Text='<%#Eval("Title")%>'></asp:HyperLink></li>
</ItemTemplate>
</asp:Repeater>
Are you verifying which type of repeater item you are looking at ?
Attach an ItemDataBound to your repeater and do something like this :
private void rptPanier_ItemDataBound(Object sender , RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Header)
{
var myItem = (Hyperlink)e.Item.FindControl("YourControlName");
}
}
Then you will have a reference on it and you can do whatever you want !
Beware tho, the ItemData item, which can be found in the repeateritemeventargs, is always null when the repeater is creating the header.
Hope this helps !
The repeater has a collection of Items. Each Item is a RepeaterItem, which has an ItemType property. For header items, this value will be "ListItemType.Header". Therefore, you want to perform .FindControl() upon that particular repeater item, not the entire Repeater itself.
I have a class that contains hierarchical data. I want to present this data in my ASP.net webapp using nested repeaters. How do I do this? I've only ever done one level of nesting, how do I do say five levels?
Each item can have zero or many sub items. I'm basically just indenting at each subleveling using some css stuff. I do not want to use the treeview control, I want to strictly stick with a repeater.
Update:
My data comes from a database. I have an item datatable with some basic properties.
Item
{
ID,
Name,
Description,
...
}
Then I have a many to many table with:
Parent
{
ParentID,
ChildID
}
I'm iterating through each item and displaying its children; and its children's children. I assume this would best be accomplished with nested repeaters, but I could be wrong.
I've found that the simplest way to do nested repeaters without worrying about databinding events is to just set the DataSource using <%# %> syntax.
For example:
<asp:Repeater runat="server" id="Departments">
<ItemTemplate>
Name: <%# Eval("DeptName") %>
Employees:
<asp:Repeater runat="server" DataSource='<%# Eval("Employees") %>'>
<ItemTemplate><%# Eval("Name") %></ItemTemplate>
<SeparatorTemplate>,</SeparatorTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
This is presuming that your Departments class has an Employees property - eg:
public class Department {
public string DeptName {get; set;}
public IEnumerable<Employee> Employees {get; set;}
}
public class Employee {
public string Name {get; set;}
}
If your outer-repeater object doesn't have a property corresponding to the inner-repeater object you can still use this trick, by adding a method in your code-behind that does the calculation. So your inner repeater might become:
<asp:Repeater runat="server" DataSource='<%# GetEmployees(Container.DataItem) %>'>
and then GetEmployees might look something like:
protected IEnumerable<Employee> GetEmployees(object item) {
var dept = (Department) item;
// then do whatever is necessary to get the employees from dept
return employees;
}
It's always cleaner to deal with the datasource than messing about with ItemDataBound, but this is even more the case when nesting Repeaters:
<asp:Repeater DataSource="<%#ColOfCol%>" runat="server">
<ItemTemplate>
<tr>
<asp:Repeater DataSource="<%#Container.DataItem%>" runat="server">
<ItemTemplate>
<td><%#SomeExtractingMethodLikeEval()%></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>
The inner datasource could also be an evaluated property, or a call to a method that returns the enumeration wanted. Just be aware that it will be called with an object. I prefer to write the specific version, and then overload:
protected IEnumerable<string> GetNames(Family fam)
{
foreach(Person p in fam.Members)
yield return p.FirstName + " " + p.Surname;
}
protected IEnumerable<string> GetNames(object famObj)
{
return GetNames((Family)famObj);
}
One thing to be aware of is that if you want to get the current object in the parent repeater than you have to obtain it with:
((RepeaterItem)Container.Parent.Parent).DataItem
You can nest repeaters without a problem. More then 2 levels deep gets nasty though. Here's how:
The html looks something like this:
<asp:Repeater ID="r1" runat="server" OnItemDataBound="r1_ItemDataBound">
<ItemTemplate>
<!-- top level repeater element template here -->
<asp:Repeater ID="r2" runat="server" onitemdatabound="r2_ItemDataBound">
<ItemTemplate>
<!-- child repeater element template here -->
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
The codebehind looks like this:
protected void r1_ItemDataBound(object sender, RepeaterItemEventArgs e) {
Repeater r2 = (Repeater)e.Item.FindControl("r2");
r2.DataSource = yourDataSourceHere; // you'll have to query for appropriate data
r2.DataBind();
}
protected void r2_ItemDataBound(object sender, RepeaterItemEventArgs e) {
// do the same thing here for the 3rd nested repeater if you have a third, and so on
}
<asp:Repeater ID="R1" runat="server">
<ItemTemplate>
<asp:Repeater ID="R2" runat="server">
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
R1.ItemDataBound += (s, e) =>
{
var r2 = e.Item.FindControl("R2") as Repeater;
r2.DataSource = something;
r2.DataBind();
};
Be aware that FindControl is not recursive, it will only get the children.