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.
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 simply trying to create a list and add elements to it from the code behind. Each list element must be connected to a function in the code behind so I am using the Asp:LinkButton to do this. In the Default.aspx page I have:
<asp:ListView ID="ulNumTenants" runat="server">
<ItemTemplate>
<li>
<%# DataBinder.Eval(Container.DataItem, "XXX" ) %>
</li>
</ItemTemplate>
</asp:ListView>
And in the code behind I have the following:
var listItems = new List<LinkButton>();
int numberOfTenantsPossible = Space.MaxNumberOfTenants - (Space.MaleHousemates + Space.FemaleHousemates);
for (int itemCount = 0; itemCount < numberOfTenantsPossible; itemCount++ )
{
LinkButton currentItem = new LinkButton();
currentItem.CommandArgument = (itemCount + 1).ToString();
currentItem.CommandName = "Tenant_OnClick";
currentItem.Text = (itemCount + 1).ToString() + " tenants";
listItems.Add(currentItem);
}
ulNumTenants.DataSource = listItems;
ulNumTenants.DataBind();
The issue I am having is in the default.aspx code since I do not know what the expression field( "XXX" ) should be set to when I am not getting the entries from a database. Any suggestions are greatly appreciated.
Try this:
<%# Container.DataItem %>
I doubt it will work, since I think it will just take the string representation of a LinkButton instead of the HTML markup. However, why create the LinkButton dynamically in code? Try this instead:
Code Behind:
public class TenantViewModel
{
public string ID {get; set;}
public string Name {get; set;}
}
int numberOfTenantsPossible = Space.MaxNumberOfTenants - (Space.MaleHousemates + Space.FemaleHousemates);
var vms = new List<TenantViewModel>();
for (int itemCount = 0; itemCount < numberOfTenantsPossible; itemCount++ )
{
var vm = new TenantViewModel { ID = (itemCount + 1).ToString(), Name = (itemCount + 1).ToString() + " tenants"};
vms.Add(vm);
}
ulNumTenants.DataSource = vms;
ulNumTenants.DataBind();
ASPX:
<asp:ListView ID="ulNumTenants" runat="server">
<ItemTemplate>
<li>
<asp:LinkButton runat="server" CommandName="Tenant_OnClick" CommandArgument='<%# (Container.DataItem as TenantViewModel).ID' Text='<%# (Container.DataItem as TenantViewModel).Name' />
</li>
</ItemTemplate>
</asp:ListView>
That allows you to keep UI element declaration in your ASPX markup, and instead of creating all the buttons in your code behind, you just create a view model to bind it to. Container.DataItem will be an object, so we use the as syntax to convert it to the correct type TenantViewModel so we can access the properties. This results in much cleaner code. Instead of a ListView, you might also consider binding to a Repeater. ListViews are typically for two way binding directly to a database, but we're binding to a custom IEnumerable.
Also, if you do find that this markup is significantly cleaner, you might consider looking into ASP.NET MVC. The markup gets even cleaner there with Razor syntax, because you won't have to worry about casting to the correct type. Instead of using a repeater, you'd just use a foreach loop.
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.
I'm not sure how to correct the following problem. I have dropdown list that has a object data source. Then in the code there is a method like this
void InitPageData()
{
MembershipUser user = Membership.GetUser();
DataSetTableAdapters.MemberInfoTableAdapter da = new DataSetTableAdapters.MemberInfoTableAdapter();
DataSet.MemberInfoDataTable dt = da.GetMember((Guid)user.ProviderUserKey);
if (dt.Rows.Count == 1)
{
DataSet.MemberInfoRow mr = dt[0];
//rank.Text = mr.rank;
//position.Text = mr.position;
UserName.Text = user.UserName;
...
}
This method populates form fields on the page. What I'm trying to do is to have the rank dropdown list populated from the ods but use this method above to populate the selected item of the rank dropwon list with the line rank.Text = mr.rank. In this example the the line of code that throws the error is commented out otherwise it throws this: "'rank' has a SelectedValue which is invalid because it does not exist in the list of items.
Parameter name: value".
I've chaned the code to rank.DataTextFiled = mr.rank and rank.DataValueField = mr.rankid.ToString() but this threw another error: "DataBinding: 'System.Data.DataRowView' does not contain a property with the name 'Star'." "Star" is the value of the mr.rank.
Here is what the dropdown list and the ods look like:
<asp:DropDownList runat="server" ID="rank" CssClass="txtfield" DataSourceID="ODCRanks"
DataTextField="Rank" DataValueField="ID" AppendDataBoundItems="True">
<asp:ListItem Text="--- Select a Rank ---" Value="-1" />
<asp:ObjectDataSource ID="ODCRanks" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetRanks"
TypeName="RanksTableAdapters.RankTableAdapter"></asp:ObjectDataSource>
You should try adding data columns to your data tables (with ID and Rank being the column name) so that the data can be binded to control.
The Text property sets it by value.
See http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.listcontrol.text.aspx
You seem to have what will be the text associated with the value and you want to set that as the selected item. I guess this mostly because your value collection is bound to something called ID and I figure a rank of Star isn't an ID.
If Star is what will show up as something in DataTextField, use the items collection FindByText method to select it.
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.listitemcollection.findbytext.aspx
Example
ListItem li = DropDownList1.Items.FindByText("one");
if (li != null) li.Selected = true;
If Star is indeed an ID, then check that the collection is fully loaded before trying to select anything in it.
try something like
rank.SelectedIndex = rank.Items.IndexOf(rank.Items.FindByText(mr.rank));
I am new to Repeater and DataBinding and I need help using it.
In PageLoad, I have
var photos = from p in MyDataContext.Photos
select new {
p,
Url = p.GetImageUrl()
};
repeater1.DataSource = photos;
repeater1.DataBind();
In the Repeater control, I have
<ItemTemplate>
<% Photo p = (Photo) Eval("p"); %> <!-- Apparently I can't do this -->
...
<asp:TextBox runat="server" ID="txtTime" Text='<%= p.Time == null ? "" : ((DateTime)p.Time).ToString("dd/MM/yyyy HH:mm:ss") %>' />
...
</ItemTemplate>
But that is wrong.
What I need is to get the Photo object in ItemTemplate so I can do things with it (eg. to display the time as in the second line in ItemTemplate above). Is it even possible to do this in a Repeater?
Could someone point me to the right direction?
Thank you in advance!
Try something like this In the onDatabound event
if (e.Item.ItemType = ListItemType.Item)
{
photo p = (photo)e.DataItem;
Textbox txtTime = (Textbox)e.Item.FindControl("txtTime");
txtTime.text = (p.Time == null ? "" : ((DateTime)p.Time).ToString("dd/MM/yyyy HH:mm:ss"));
}
Edit -
Sorry, I didn't see the extra Url there. I looks like you might have to create a small class or struct.
See this Stackoverflow link for a hack workaround.
Paul Suart's post in that thread made a valid point.
Have you tried just:
<%# Eval("p") %>
instead of
<% Photo p = (Photo) Eval("p"); %>
I use an alternative method. In my "Register" I import the object class:
<%# Import Namespace="Test.Test.TO" %>
With this It's possible use your object...
Next, I created an object the same type I want to bound in my codebehind, global variable...
public Test test;
In my Repeater inside ItemTemplete:
<span style="display: none;"> <%# test = (Test)Container.DataItem %> </span>
Now, you can use all object's properties, inclusive ToString to format with culture...
Sorry for my english.