ASP.NET - Getting the object inside Repeater ItemTemplate with/without Eval - c#

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.

Related

In ASP.NET can I change the values of some fields from a DB bound to a grid in the binding process?

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.

Bind a List to a ListView

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.

Save a Eval as bool without code behind

I only need this one variable in the front end of the page. I am trying to achieve something like this:
<% bool YesNo = Eval("isParent") == "True" ? true : false %>
Data binding doesnt allow me to do this
Does anyone know a way around this?
Thank you for all your answers, Instead of trying to find bool value, I made the If return as text true or false and worked my way around that.(with control properties and css classes)
My Solution
Visible='<%#(string)Eval("isParent") == "True" ? false : true %>'
class="<%#(string)Eval("isParent") == "True" ? "LegendHeader" : "" %>"
the reason I did it this way is because the data is bound to the specific fields, as Vladimir Sachek mentioned, if you do it the way I wanted to you'll have to loop the data and set variable accordingly
use DataBinder.Eval instead and specify the model you are taking the data from
<% bool YesNo = DataBinder.Eval(new{isParent = "True"}, "isParent") == "True" ? true : false; %>
<%= YesNo %>
try
var YesNo = <%= Eval("isParent").ToString() %> == "True"
Try something like
<% bool YesNo(Eval("isParent").toString()) %>
Where YesNo is a method in your code behind. Like following
public bool YesNo(string sIsParent)
{
if(sIsParent.equals("1")){ return true; } else { return false; }
}
Jack said:
it gives me error Databinding methods such as Eval(), XPath(), and
Bind() can only be used in the context of a databound control.
You can use Eval only inside Repeater, GridView and similar controls. This is because Eval
uses the current data item to retrieve the requested property (the 'IsParent')

Bind a repeater based on Collection Position

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.

Simple nested repeater example using LINQ in C#

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.

Categories

Resources