So I have tried and searched far and wide but cannot get my nested repeater to work properly.
<asp:Repeater ID="rptrParent" runat="server">
<ItemTemplate>
<dl class="StudentTasksList">
<dt><%# Eval("ClassCode") %></dt>
<asp:Repeater ID="rptrChild" runat="server">
<ItemTemplate>
<dd><%# Eval("Title") %></dd>
</ItemTemplate>
</asp:Repeater>
</dl>
</ItemTemplate>
</asp:Repeater>
C# Code
private void ListTasks()
{
using (StudentContext syndb = new StudentContext())
{
using (TaskContext dbdb = new TaskContext())
{
// Iterate through each active Class for the current Student finding any tasks not archived for this year
var ClassTasks = (from c in syndb.GetClasses()
where c.Students.Select(y => y.StudentID).Contains(LoggedInUserID)
join t in dbdb.Tasks on c.ClassCode equals t.ClassCode
where !t.Archive.HasValue || t.Archive.HasValue && t.Archive.Value && t.DueDate.Value.Year == DateTime.Now.Year
group c by c.ClassCode);
rptrParent.DataSource = ClassTasks;
rptrParent.DataBind();
}
}
}
So ClassCode comes from the GetClasses() method for the selected student, and Title is the name of any Titles found in the Tasks join for the current Class. The Result I need is a list of the classes for a student with any related task titles under each of the ClassCode. I have tried this several different ways so this may not be the best sample of what I am trying to do. I would get great if someone could show me a LINQ C# example of a joined linq query that populates a nested repeater as I cannot find anything decent enough to work this one out.
I would also like to know how to populate an N/A in place if the class doesn't have any tasks, but I wont push my luck.
To populate the inner repeater, you must handle the ItemDataBound event of the parent repeater like this:
<asp:Repeater ID="rptrParent" runat="server" OnItemDataBound="rptrParent_ItemDataBound">
And in the code behind
protected void rptrParent_ItemDataBound(object sender, RepeaterItemEventArgs e) {
RepeaterItem item = e.Item;
if ((item.ItemType == ListItemType.Item) || (item.ItemType == ListItemType.AlternatingItem)) {
Repeater rptrChild = (Repeater)item.FindControl("rptrChild");
rptrChild.DataSource = DataBinder.Eval(item.DataItem, "Group");
rptrChild.DataBind();
}
}
I guess that the property of the data item you need to use as data source of the inner repeater is Group as you are grouping in your LINQ statement, but maybe you need to change that...
As far as I know LINQ dose not play any role in data binding. The LINQ query you have mentioned looks find and may populate data as you needed. But You have nasted Repeater so "rptrParent.DataBind();" will bind the outer most repeater only. I think you have to write ItemDataBound event for "rptrParent" and find child repeater control assign it with group you got from LINQ query.
Hope this will help you.
Related
I have the following code behind:
List<Articles> articles = (from em in db.Articles orderby em.ReceivedDate descending select em).ToList();
gvArticles.DataSource = articles;
gvArticles.DataBind();
Where gvArticles is a GridView. The following is the asp code within that grid:
<p style='font-weight:bold;font-size:17px;color:black;'>
<asp:Label ID="lblDate" runat="server" Text='<%#Eval("datePrinted").ToString() %>'></asp:Label>
</p>
<asp:Label ID="lblBody" runat="server" Text= '<%#Eval("Content")%>'></asp:Label>
... that will clearly show from the DB the fields 'datePrinted' and 'Content'
Is there any way that I can show other value in 'datePrinted' based on a condition? For example, if 'datePrinted' is before 1/1/1990 then put "N/A" instead of the field 'datePrinted' itself?
Or a bit more complex, put "-" if the day is the same as the previous record (time can be different, just day/month/year is the same)?
Of course I could calculate all of that and store it in the DB, so I will pull other fields from the DB, but that sounds extremely inefficient, for any future condition create a new field in the DB. So are there any other ways to achieve the same results?
Ideally a condition, formula, or function that translates values from the DB before binding them to the grid is the most desirable solution.
Any idea?
Create a method in your code behind. Call that method parsing your value instead of using eval.
E.g
public static string ConvertDate(DateTime date)
{
if (date < new DateTime(1990, 1,1) )
{
return "N/A";
}
else
{
return date.ToString("dd/MM/yyyy");
}
}
Place this method where you placed your Eval("datePrinted").
<asp:Label ID="lblDate" runat="server" Text='<%# ConvertDate(Convert.ToDateTime(Eval("datePrinted")) ) %>'></asp:Label>
After days trying to figure out what to do, I found one answer just after posting this question. Basically I realized that you can process the List before assign it to the binding. So my second sample, I did this after loading the list and before assign it to the grid datasource:
string prevDate = "";
foreach(Article myArticle in articles) {
if (((DateTime)myArticle.ReceivedDate).ToShortDateString() != prevDate) {
prevDate = ((DateTime)myMail.ReceivedDate).ToShortDateString();
myArticle.ReceivedDate = prevDate;
}
else myArticle.ReceivedDate = "-";
}
Now, I am not a LINQ expert, if anyone can do the same code in the line where the DB is loaded in the List, I would appreciate it.
I am using Asp.net 4.5, C#.
I have a reapter that has some DataSource Bind to it:
<asp:Repeater ItemType="Product" ID="ProductsArea" runat="server">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
...
</ItemTemplate>
<FooterTemplate></FooterTemplate>
</asp:Repeater>
Inside this repeater, I would like a refrence to the current Iterated Item.
I know I can use <%#Item%> and that I can Use <%#Container.DataItem%>. If I want to get to a field, I can use <%#Item.fieldName%> or Eval it.
But I want to make a condition on a field, How can I get the refrence to the #Item in order to do something like this:
<% if (#Item.field>3)%>, <%if (#Container.DataItem.field<4)%>
I would acautley would like to have a refrence like this
<%var item = #Item%> and than to use it whenever I need.
Ofcourse the syntax above is invalid, How to achieve this properley?
I'd use ItemDataBound instead. That makes the code much more readable, maintainable and robust(compile time type safety).
protected void Product_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
// presuming the source of the repeater is a DataTable:
DataRowView rv = (DataRowView) e.Item.DataItem;
string field4 = rv.Row.Field<string>(3); // presuming the type of it is string
// ...
}
}
Cast e.Item.DataItem to the actual type. If you need to find a control in the ItemTemplate use e.Item.FindControl and cast it appropriately. Of course you have to add the event-handler:
<asp:Repeater OnItemDataBound="Product_ItemDataBound" ItemType="Product" ID="ProductsArea" runat="server">
I have a repeater inside a repeater, and I trying to bind the child repeater with a list of Aka's from session.
My codebehind looks like:
var results = HttpContext.Current.Session["completeRecord"];
var rep = (Repeater)e.Item.FindControl("rptAkas");
var akaList = new List<string>();
foreach (Entity list in (IEnumerable<Entity>) results)
{
akaList.AddRange(list.Aka);
}
rep.DataSource = akaList;
rep.DataBind();
In my child repeater I have the following:
<asp:Repeater runat="server" ID="rptAkas" OnItemDataBound="repeater_OnItemDataBound">
<ItemTemplate>
<p><strong>Aka</strong><asp:Literal runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "akaList") %>'></asp:Literal></p>
</ItemTemplate>
</asp:Repeater>
this throws the following message:
Additional information: DataBinding: 'System.String' does not contain a property with the name 'akaList'.
How can I bind this list to the repeater?
EDIT
Container.DataItem took care of it.
Container.DataItem is the current item your Repeater is iterating over. It's not the list of strings, it's each string individually.
Think of what you're doing in a more pseudocode form:
foreach ( var a_single_string in Repeater.DataSource )
{
var some_var = a_single_string.akaList;
}
When you reference Container.DataItem, it's the same as addressing a_single_string in the example above.
Not practically applicable code by a long shot, but I hope it explains why you are getting the error. You are actually asking for the property "akaList" of a string, hence the error.
I'm getting started with Sitecore and am having a hard time getting images stored in a TreeList to display properly in an aspnet repeater.
In the markup, I have a repeater that is meant to list the images using an sc:Image item.
<asp:Repeater ID="rptLogos" runat="server" OnItemDataBound="rptLogos_ItemDataBound" >
<ItemTemplate>
<a title="Logo" href="#">
<sc:Image runat="server" ID="img" field="image logos" />
</a>
</ItemTemplate>
</asp:Repeater>
In the code behind, I'm retrieving a MultilistField and setting the repeater's data source with the TargetIDs like so
Sitecore.Data.Fields.MultilistField mlLogos = Footer.Fields["Logos"];
if (mlPartnerLogos != null)
{
rptLogos.DataSource = mlLogos.TargetIDs;
rptLogos.DataBind();
}
In the ItemDataBound event I am then converting each ID to an item, grabbing the image control from the repeater and setting the Item field for the Sitecore.Web.UI.WebControls.Image item.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
ID targetId = e.Item.DataItem as ID;
if (!targetId.IsNull)
{
MediaItem targetItem = Sitecore.Context.Database.GetItem(targetId);
if (targetItem != null)
{
var phImage = e.Item.FindControl("img") as Sitecore.Web.UI.WebControls.Image;
phImage.Item = targetItem;
}
}
}
If I debug the code, it seems to be retrieving the data without any issues. However, in the markup, instead of having the translated to an field, I'm just getting blank text.
The same "idea" of grabbing a Sitecore Item and assigning it to a Sitecore.Web.UI.WebControls.Image item is working fine elsewhere where I have a single item, although I did notice that if I change the sc:Image tag's field to anything other than the name in lowercase of the field name in sitecore it won't work. If this is the issue though, I'm a bit confused as to what the Field name should be for the images within the TreeList though.
I also have a strange feeling that the way I'm retrieving the items and trying to assign them may be a bit too "bloated", and there may be a simpler way of doing it.
Any help would be greatly appreciated. Thank you.
Edit:
Fixed as suggested by replacing the sc:image with an asp:Image and changing the ItemDataBound to
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
ID targetId = e.Item.DataItem as ID;
if (!targetId.IsNull)
{
MediaItem targetItem = Sitecore.Context.Database.GetItem(targetId);
if (targetItem != null)
{
var phImage = e.Item.FindControl("img") as System.Web.UI.WebControls.Image;
phImage.ImageUrl = Sitecore.Resources.Media.MediaManager.GetMediaUrl(targetItem);
}
}
}
The sc:image control is to be used to display fields of an item. I think it's a better approach to just use an asp:image control and fill the image url. If you use the MediaManager you can get the image url from your image item.
Martijn is right. I'd like to add that skipping the OnItemDataBound is better. You should use databinding, much easier. Replace mlLogos.TargetIDs with the items and you are good to go!
ASPX : Code
<asp:repeater id="repeater" runat="server">
<headerTemplate></headerTemplate>
<itemtemplate></itemtemplate>
<footerTemplate> <asp:literal id=findme runate=server> </footerTeplate>
</asp:repeater>
What i am looking for is source code to be able to find the control within the footer of a data repeater. Im familiar with the basic "FindControl" when i do a databind or look for control within the page itself, but how can i find the control within a footer template of a data repeater?
Is this even possible? and if so how can i please get some assistance,
thanks again to all!!!
[update]
i need to be able to do this after the databind
Protected Sub Repeater1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles Repeater1.ItemDataBound
If e.Item.ItemType = ListItemType.Footer Then
Dim Lit As Literal = CType(e.Item.FindControl("findme"), Literal)
End If
End Sub
There are a number of ways that you can do it, the exact way depends on when you want to get access to the control.
If you want it during DataBind, simply do the following inside the item Databound.
if(e.Item.ItemType == ItemType.Footer)
{
Literal findMe = (Literal)e.Item.FindControl("findMe");
//Your code here
}
If you want to find it at another point in time, access the repeater's Item collection, then find the "Footer" item, and from that item, you can find the control.
Update
Based on your added note, you can do this by enumerating the item collection, below is an example with a repeater that has an id of myRepeater.
foreach (RepeaterItem item in myRepeater.Items)
{
if (item.ItemType == ListItemType.Footer)
{
Literal findMe = (Literal)item.FindControl("findMe");
//Do your stuff
}
}
I think you have to check the ListItemType in an ItemDataBound event handler. You can check for Header or Footer and then use the FindControl method to access the control.
Foreach (RepeaterItem item in myRepeater.Controls)
This will work better as Items collection doesn't contain header and footer
If you need to get the Footer after DataBind (which is what the OP appears to want) then you can use the following:
RepeaterItem item= (RepeaterItem)myRepeater.Controls[myRepeater.Controls.Count - 1];
if (item.ItemType == ListItemType.Footer) {
Literal findMe = (Literal)item.FindControl("findMe");
}