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.
Related
As the title suggests, I want to add something to a table (inside aspx page) depending on some infos I get from a database (so if I have an item x in db I will make a bool type variable in cs).
My question is, is there any method to send that bool to aspx so I can use it inside an if statement (in order to add the respective columns to my table).
You don't need a session variable here, you can simply declare a class level property and use it in your ASPX page:-
public bool variable { get; set; }
and then directly use it in your aspx page:-
<%=variable %>
You can store the value of bool in session and display it in aspx, something like that
bool val;
// Your code
Session["value"] = val;
and use it in aspx such as
<%= Session["value"]%>
You can use a div and make your own HTML code in the .cs code behind file.
Or, you could simply use some labels and give information like that.
html:
<div>
<table>
<tr>
<td><asp:Label id="somelabel" runat="server" /></td>
</tr>
</table>
.cs
somelabel.Text = "yourdata";
Or you could add all your html code to a single string. Then, if your div is called divTable:
divTable.InnerHtml = liststring;
An example:
liststring = liststring +
"<img src=" + SQLdata.Tables[0].Rows[x]["imageSource"].ToString() +
"<ul style='list-style-type: none;'>" +
"<li>" + SQLdata.Tables[0].Rows[x]["Name"].ToString() + "</li>" +
"<li><b>Adress: </b>" + SQLdata.Tables[0].Rows[x]["Adress"].ToString() + "</li>" +
"<li><b>Website: </b><a href='http://" + SQLdata.Tables[0].Rows[x]["Website"].ToString() + "'>" + SQLdata.Tables[0].Rows[x]["Website"].ToString() + "</a></li>" + "</ul></div>";
divTable.InnerHtml = liststring;
Ofcourse you have to css a bit to get the correct display, but this will get you going I guess...
On default.aspx I have the following hidden fields:
<asp:HiddenField runat="server" ID="icon1" />
<asp:HiddenField runat="server" ID="icon2" />
<asp:HiddenField runat="server" ID="icon3" />
As you can see, the name of the field is the same each time but increments by 1 up to 3.
In my code behind I have been doing this (if statements and other code removed for brevity - this is the meat of it):
icon1.Value = "Bonus1";
icon2.Value = "Bonus2";
icon3.Value = "Bonus3";
Must I assign the iconX.Value individually every time like that? Can I do it all in one shot in a loop (also with everything else removed for brevity)?
for (int i = 1; i <=3; i++)
{
icon(i).Value = "Bonus" + i.ToString();
}
Everything I have read leads me to believe this is not possible in C#. Let's pretend I have 50 iconX.Value = whatever to assign. A loop makes the most logical sense. Possible?
A loop makes the most logical sense. Possible?
Yes. Use the FindControl method of the page to look up a control by its ID:
for (int i = 1; i <= 3; i++)
{
HiddenField field = (HiddenField)this.FindControl("icon" + i);
field.Value = "Bonus" + i.ToString();
}
Note: Because the return type of FindControl is Control, you must cast the result in order to access properties specific to HiddenField.
You have to create a asp:panel in the HTML section.
Then, in the c# code, make a loop and create elements like you describe.
When the element is fully configured, add it to the panel.
.ASPX file
<asp:Panel ID="panel_controls" runat="server"> </asp:Panel>
C# code
for (int i = 1; i <= 3; i++) {
HiddenField myField = new HiddenField();
myField.ID = "icon" + i;
myField.Value = "Bonus" + i;
panel_controls.Controls.Add(myField);
}
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 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.
I’m using c#.net.
I have been looking round the web and can’t find anything that helps me out.
I have a list of contractors, daily hours, daily slots (three different tables).
First row - contains the contractors
names.
Second row - unlimited - contains the
slots.
For example
I thought I could use a ListView however I am having trouble working out where I would place the code.
<asp:ListView ID="contractorListView" runat="server">
<LayoutTemplate>
<table runat="server">
<tr>
<td>Times</td>
// Contractors names pulled from another
<th><asp:PlaceHolder id="itemPlaceholder" runat="server" /></th>
</tr>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>Times pulled from one database table</td>
<td align="left" style="width: 200px;">
// Customers name - attached to correct time
<asp:Label runat="server" Text='<%#Eval("person_name")%>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
Uses Linq model so can connect to customers 'slotted time'
ObjectDataSource contractorDataSource = new ObjectDataSource();
contractorDataSource.SelectMethod = "GetContractorByDateCategory";
contractorDataSource.TypeName = "contractBook.classes.contractorRepository";
contractorListView.DataSource = contractorDataSource;
contractorDataSource.DataBind();
contractorListView.DataBind();
Anyone got any ideas / example?
Thanks in advance for any help.
Clare
Here's how I tend to solve problems like this:
manually pull our the data you want to show (calling the repository methods, not using ObjectDataSource to do it). For efficiency, it often makes sense to make one big query which returns denormalized records, each containing all the columns you'll need (e.g. SELECT TimeSlot, CustomerName, ContractorName FROM (joins go here) ...)
create a custom collection class which puts that data into a format which is easy to data-bind. "easy to data bind" usually means that the data is organized in the same order that you're going to display it. You also might need to do hacks like, for example, making all your rows the same length in order to databind the same number of table cells per row.
in your data-binding expressions, cast Container.DataItem to whatever type you need, in order to pull out properties of that type. This also has the added advantage of making databinding expressions with typos fail at compile time rather than waiting until runtime to find bugs.
for nesting, set the DataSource property of a nested template control (e.g. a repeater) to a property of the parent Container.DataItem
for header rows, here's a trick: put header code directly into the ItemTemplate, and use Container.ItemIndex==0 to know when to show the header line or not. Since only Repeater (but not ListView) supports the ItemIndex property, I tend to use Repeater instead of ListView for most read-only data-binding tasks. That's why I changed your ListView in my code sample below to use Repeater. The same thing can be done by adding an Index or RowNumber property to your custom data-binding classes described above, but that's harder.
The general idea is that you want to push as much intelligence as possible out of your databinding code and into actual methods in your page (or code-behind) code, which is easier to write and debug.
Here's a working sample (with your classes and repository classes mocked up) to give you an idea what I'm talking about. You should be able to adapt this to your situation.
<%# Page Language="C#"%>
<%# Import Namespace="System.Collections.Generic" %>
<%# Import Namespace="System.Linq" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
public class Person // replace with your class
{
public string person_name {get; set;}
}
public class Repository // replace with your class
{
public static IEnumerable<Record> GetCustomerByDateCategory()
{
// fake some data
return new Record[]
{
new Record { Time = new DateTime(2000, 1, 1, 8, 0, 0), Contractor = new Person {person_name = "Joe the Plumber"}, Customer = new Person {person_name = "Joe Smith"} },
new Record { Time = new DateTime(2000, 1, 1, 8, 30, 0), Contractor = new Person {person_name = "Bob Vila"}, Customer = new Person {person_name = "Frank Johnson"} },
new Record { Time = new DateTime(2000, 1, 1, 8, 30, 0), Contractor = new Person {person_name = "Mr. Clean"}, Customer = new Person {person_name = "Elliott P. Ness"} },
};
}
public class Record // replace this class with your record's class
{
public DateTime Time {get; set;}
public Person Contractor { get; set; }
public Person Customer { get; set; }
}
}
// key = time, value = ordered (by contractor) list of customers in that time slot
public class CustomersByTime : SortedDictionary<DateTime, List<Person>>
{
public List<Person> Contractors { get; set; }
public CustomersByTime (IEnumerable <Repository.Record> records)
{
Contractors = new List<Person>();
foreach (Repository.Record record in records)
{
int contractorIndex = Contractors.FindIndex(p => p.person_name == record.Contractor.person_name);
if (contractorIndex == -1)
{
Contractors.Add(record.Contractor);
contractorIndex = Contractors.Count - 1;
}
List<Person> customerList;
if (!this.TryGetValue(record.Time, out customerList))
{
customerList = new List<Person>();
this.Add(record.Time, customerList);
}
while (customerList.Count < contractorIndex)
customerList.Add (null); // fill in blanks if needed
customerList.Add (record.Customer); // fill in blanks if needed
}
MakeSameLength();
}
// extend each list to match the longest one. makes databinding easier.
public void MakeSameLength()
{
int max = 0;
foreach (var value in this.Values)
{
if (value.Count > max)
max = value.Count;
}
foreach (var value in this.Values)
{
while (value.Count < max)
value.Add(null);
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
CustomersByTime Customers = new CustomersByTime(Repository.GetCustomerByDateCategory());
CustomerListView.DataSource = Customers;
CustomerListView.DataBind();
}
</script>
<html>
<head>
<style type="text/css">
td, th, table { border:solid 1px black; border-collapse:collapse;}
</style>
</head>
<body>
<asp:Repeater ID="CustomerListView" runat="server">
<HeaderTemplate><table cellpadding="2" cellspacing="2"></HeaderTemplate>
<ItemTemplate>
<asp:Repeater runat="server" visible="<%#Container.ItemIndex==0 %>"
DataSource="<%#((CustomersByTime)(CustomerListView.DataSource)).Contractors %>" >
<HeaderTemplate>
<tr>
<th>Times</th>
</HeaderTemplate>
<ItemTemplate>
<th><%#((Person)Container.DataItem).person_name %></th>
</ItemTemplate>
<FooterTemplate>
</tr>
</FooterTemplate>
</asp:Repeater>
<tr>
<td><%#((KeyValuePair<DateTime, List<Person>>)(Container.DataItem)).Key.ToShortTimeString() %></td>
<asp:Repeater ID="Repeater1" runat="server" DataSource="<%# ((KeyValuePair<DateTime, List<Person>>)(Container.DataItem)).Value %>">
<ItemTemplate>
<td align="left" style="width: 200px;">
<%#Container.DataItem == null ? "" : ((Person)(Container.DataItem)).person_name%>
</td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
<FooterTemplate></table></FooterTemplate>
</asp:Repeater>
</body>
</html>
BTW, if you're building a brand-new app and have some time for learning, I definitely suggest looking at ASP.NET MVC, which has a non-trivial learning curve but makes a lot of things easier... in partiuclar this kind of complex data rendering.
Is this not just a cross-tab situation? Look to my solution here:
Cross Tab - Storing different dates (Meeting1, Meeting2, Meeting 3 etc) in the same column
Why not create a single query which extracts all your information?
You can perform a JOIN operation over multiple tables.
If the join gets really complex, you can create a VIEW, which creates a new virtual table to consolidate your information.