Binding Gridview to IList<BusinessObject> that contains an IList<BusinessObject> - asp.net

I'm having trouble figuring out how to bind a custom IList to a gridview. The IList contains another custom IList. I need to bind a property from this IList to the gridview.
public class Seminar : BusinessObject
{
private string _description = String.Empty;
private List<User> _attendees;
public string Description {get {return _description;} set {_description = value;}}
public List<User> Attendees {get {return _attendees;} set {_attendees = value;}}
}
public class User : BusinessObject
{
private string _name = String.Empty;
public string Name { get { return _name; } set { _name = value; } }
}
Backend page.aspx.cs:
List<Seminar> seminarList = SeminarManager.Get(id);
gridSeminars.DataSource = seminarList;
gridSeminars.DataBind();
Frontend page.aspx:
<asp:GridView ID="gridSeminars" runat="server">
<Columns>
<asp:BoundField DataField="Id" />
<asp:BoundField DataField="Description" />
<asp:BoundField DataField="Name" />
</Columns>
</asp:GridView>
The problem is with populating the "Name" field in the gridview. All suggestions are welcome.

Have you tried,
Attendees.Name
Edit:
Attendees is an IEnumerable itself. The above suggestion only works with Attendee.Name
If you wan´t to display all the attendees you need to make a templatefield and maybe use a repeater or something...something like:
<asp:TemplateField>
<ItemTemplate>
<asp:Repeater runat="server" DataSource='<%# Eval("Attendees") %>'>
<ItemTemplate>
<tr>
<td>
Name
</td>
<td>
<%# Eval("Name")%>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:TemplateField>

Johan's answer is a good one, specifically the part about how you are trying to bind a collection to a column that is expecting something that can be converted to a string.
Another option would be to put a TemplateField that contains the bindable control (Repeater, another GridView, etc.) you choose to use and binding the DataSource property to the Attendees property of your business object.
Also, I don't if you're married to the idea of using a GridView, but in cases like these where you need more control of the layout, I would suggest the new ListView (.NET 3.5) control, or lately I've just been using nested repeaters so I can have more refined control of the layout I am trying to generate.
Hope this helps.

I'm assuming you want to flatten the hierarchy and display one row for every user. If you have access to Linq, you can use this:
List<Seminar> seminarList = SeminarManager.Get(id);
gridSeminars.DataSource = from seminar in seminarList // loop through all of the seminars in the list
from user in seminar.Attendees // loop through all of the users in the current seminar
select new // create a new, flattened object to bind to.
{
seminar.Id,
seminar.Description,
user.Name
};
gridSeminars.DataBind();
http://weblogs.asp.net/zeeshanhirani/archive/2008/03/26/select-many-operator-part-1.aspx

Related

EntityDataSource accessing a collection from another entity in GridView

I'm not sure if my title accurately describes what I'm trying to do.
I have two tables in my Entity Framework, person and user. I am trying to display a grid of users, so I set up this EntityDatasource:
<asp:EntityDataSource ID="peopleQuery" runat="server"
ConnectionString="name=myEntities" DefaultContainerName="myEntities"
EnableFlattening="False" EntitySetName="people" Include="location"
Where="it.active = 1" OrderBy="it.lastname, it.firstname">
</asp:EntityDataSource>
Now in my GridView, I can bind to fields like lastname, firstname, emailaddress, and fields from the location table like address and city.
Now, in my system, a user record can be attached to one or more person records. So it's a 1-to-many relationship. In the gridview, I would like to display the users associated with each person. I can do it with a RowDataBound event, but that requires an extra DB lookup for every row in the database.
Is there some efficient way to navigate from the "many" end of a "1-to-many" foreign key relationship?
Actually, it doesn't even have to be 1-to-many. It could be 1-to-1, but you're on the other end of the foreign key relationship. So, in this example, the EntityDataSource uses the "person" table, but in the user table, I have a personid field. So the foreign key is user.personid => person.personid. Since the GridView is driven from the user person table, how can I navigate to the user table to display user.username in my GridView?
You can nest either a gridview or repeater inside your Gridview and use the relationship to populate the data by setting the datasource to the relationship:
<asp:EntityDataSource ID="eds" runat="server"
ContextTypeName="people"
EnableFlattening="False"
EntitySetName="people"
Include="users" >
</asp:EntityDataSource>
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="False" DataKeyNames="PeopleId" DataSourceID="eds">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First name" />
<asp:BoundField DataField="LastName" HeaderText="Last name" />
<asp:TemplateField HeaderText="People">
<ItemTemplate>
<asp:Repeater ID="rptUsers" runat="server" DataSource='<%# Eval("users") %>'>
<ItemTemplate>
<%# Eval("username") %>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
This assumes you have a relationship setup in your data model, eg:
public class person(){
public int PersonId{get;set;}
public string FirstName{ get;set;}
public string LastName{ get;set;}
pubic virtual ICollection<user> users { get; set; }
}
public class user(){
public int UserId{ get;set;}
public string username{ get;set; }
}

Is it possible to have the datasource of a asp.net repeater be a list<string> that is a property of a class?

Here is the aspx for my repeater:
<asp:Repeater ID="rpt_Files" runat="server">
<HeaderTemplate>
<th><asp:Label runat="server" ID="lbl_FileNameHeader" Text="File Name" /></th>
</HeaderTemplate>
<ItemTemplate>
<asp:Label runat="server" ID="lbl_FileName" Text='<%# Eval("JobFileNames") %>' />
</ItemTemplate>
</asp:Repeater>
Here is the code for binding the data in C#:
rpt_Files.DataSource = CurrentQuote;
rpt_Files.DataBind();
And here is the class definition of CurrentQuote:
public class CurrentQuote
{
// Properties
private List<string> _jobfilenames;
public List<string> JobFileNames
{
get
{
if (_jobfilenames != null)
return _jobfilenames;
else
{
_jobfilenames = new List<string>();
return _jobfilenames;
}
}
set { _jobfilenames = value; }
}
Here is the error I receive:
An invalid data source is being used for rpt_Files. A valid data source must implement either IListSource or IEnumerable.
If I change the repeaters datasource to CurrentQuote.JobFileNames I receive an error stating that string has no property called "JobFileNames".
If you have one quote with multiple files your DataSource should look something like this:
CurrentQuote cq = new CurrentQuote();
string[] filenames = new string[] { "file1", "file2", "file3" };
cq.JobFileNames = filenames.ToList();
rpt_Files.DataSource = cq.JobFileNames;
rpt_Files.DataBind();
Then in the markup you can use:
<asp:Label runat="server" ID="lbl_FileName" Text='<%# Container.DataItem %>' />
So yes "It is possible to have the datasource of a asp.net repeater be a list that is a property of a class" but you should instantiate the class first and from markup you have to access directly the DataItem since it is a string.
You should assign DataSource to a List of objects like this:
List<CurrentQuote> myCurrentQuoteList = new List<CurrentQuote>();
CurrentQuote currentQuoteObj = new CurrentQuote();
currentQuoteObj.JobFileNames.Add("test");
myCurrentQuoteList.Add(myCurrentQuoteList );
rpt_Files.DataSource = myCurrentQuoteList;
rpt_Files.DataBind();
You cannot directly assign a class Object, you should implement IListSource or IEnumerable to be used as your data source.

How do I bind a GridView to a custom object?

If I have the following ASP.NET code (it's not complete - obviously there's a lot missing, but none of it matters):
<asp:GridView>
<Columns>
<asp:TemplateField>
<ItemTemplate>
My Label: <asp:Label />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
My Text Box: <asp:TextBox />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And if I have something like this in the CodeBehind:
Private MyListOfObjects As List(Of MyObject)
...
Public Class MyObject
Public MyTextBoxString as String
Public MyLabelString as String
End Class
How can I bind the GridView so that one row is equivalent to one item in my MyListOfObjects list, and so that the data will populate and persist across page loads or postbacks? I've never done custom databinding like this before, so a full explanation would be very helpful. All the tutorials I've come across so far only talk about using GridViews directly with Database query results, and that's not what I need.
Thanks!
Just set the datasource of the gridview to your object.
MyGridView.DataSource = myList
MyGridView.DataBind()
Here's a very similiar post:
Binding a method that returns List<employee> to a gridview
Looks like you are using a list in vb.net. Remember lists can hold integers, strings, dates, objects (these include user defined types (your object)).
So you can bind a gridview to a list object by setting the datasource property to your list.
In the above example, myList, might hold a ton of employee objects, etc. So assign it to the datasource and .DataBind() and voila a gridview with each row containing your object.
You can do something like
My Label: <asp:Label id="myLabel" runat="server" Text='<%# Eval("MyTextBoxString") %>' />
in the markup and similar stuff for your textbox.
GridView1.DataSource = MyListOfObjects
GridView1.DataBind()
First remember any binding controls like GridView, DropdownList e.t.c bind to public properties, so first make your public members to public properties.
Then create objects of MyObject class and add them to your List<MyObject> object
Finally you can persist this list object by saving it in Session or ViewState to maintain it after postbacks.
I hope you can do it now!!! you can ask for more help if neccessary.

ASP.NET: Bind a value to a custom user control inside a repeater

I have an ASP.NET control that binds data to a repeater. Inside that repeater, I have another custom user control. I want to pass a value to this second control based on the current binding item.
<asp:Repeater runat="server" ID="ProductList">
<ItemTemplate>
<p>Product ID: <%# Eval("ProductID") %></p>
<myControl:MyCoolUserControl runat="server" ProductID='<%# Eval("ProductID") %>' />
</ItemTemplate>
</asp:Repeater>
The repeater item template correctly prints out my Product ID with the Eval statement, but when I do the same thing to pass the Product ID to MyCoolUserControl, it doesn't work (if ProductID on MyCoolUserControl is a Nullable Int32 - I can debug it and it's always null).
Any ideas how I can do this?
I did a small test and I got it working if the ProductID is a string. After I changed it to and int in the usercontrol I got kind of the same problems.
I did a int.Parse in the datasource to the repeater and got it working again.
Check to see that the ProductId that you pass into the repeaters datasource is of type int.
Mytest app.
string[] values = new string[]{ "12", "13" };
MyRepeater.DataSource = from v in values
select new
{
ProdId = int.Parse(v)
};
MyRepeater.DataBind();
<asp:Repeater runat="server" ID="MyRepeater">
<ItemTemplate>
<My:control runat="server" ProductId='<%# Eval("ProdId") %>' />
</ItemTemplate>
</asp:Repeater>
and in the usercontrol:
public int? ProductId
{
set { MyLabel.Text = value.Value.ToString(); }
}

CheckBoxList with multiple values bound to a single flag enum

I have a FormView (bound to an ObjectDataSource) that contains a CheckBoxList that I'd like to bind to a single property of the underlying object that is an Enum with the FlagsAttribute applied to it. Binding to the SelectedValue property always gives me just the FIRST selected value from the list as the property's value. Anyone know how to get around this without overriding the Inserting or Updating methods and manually getting the values of the checkbox list and stuffing it into the parameters of the datasource? Sample code below of what I'm trying to do...
<asp:FormView runat="server" ID="MyFormView" DataSourceID="MyDataSource">
<InsertItemTempate>
<asp:CheckBoxList runat="server" ID="MyCbl" SelectedValue='<%# Bind("MyProperty") %>'>
<asp:ListItem Text="Choice 1" Value="ChoiceOne"></asp:ListItem>
<asp:ListItem Text="Choice 2" Value="ChoiceTwo"></asp:ListItem>
</asp:CheckBoxList>
</InsertItemTemplate>
</asp:FormView>
<asp:ObjectDataSource runat="server" ID="MyDataSource" TypeName="MyClass" ...></asp:ObjectDataSource>
behind the scenes, my object is declared like this...
public class MyClass
{
public MyEnum MyProperty { get; set; }
}
[Flags()]
public Enum MyEnum
{
ChoiceOne = 1,
ChoiceTwo = 2
}
You will have to iterate through the Items collections and build up the enum values from there.
A search on Google for FlaggedEnumTypeConverter should also be helpful.

Resources