What is an easy way to bind a gridview with dynamic columns? - asp.net

I have a variable defined as follows:
Dim iRows As List(Of String())
I've also been converting that to a list of lists just to make it easier to work with.
Dim iRows As List(Of IList(Of String))
I would like to bind that list to a GridView using the contents of the nested array/list to dynamically define the columns. I don't know ahead of time how many columns there will be, but I do know that they are all the same throughout the list.
I'm just not real sure how to go about it. Thoughts?

Seems to me, that without knowing how your datasource looks like / how many columns there will be, there is no way to put each column into a different gridview column.
That means, fixed markup is not possible => You need to set the AutoGenerateColumns property to true.
You can set the HeaderText of each column by accessing the HeaderCollecion of the GridView, if you want to use a different
Another solution you might be interested in, would be not to use the GridView at all, if you are only interested in displaying data (meaning no edit or delete buttons) You could render your data into a html table with columns and rows, by using Reflection, like aleafonso suggested.

I have done something like this with c# which could possibly help you.
I will have a list of destinations which will be populated to a GridView.
The destination object must be serializable and cannot have nullable values. This is my example:
[Serializable]
public class destination
{
private int idDestination;
public int IDDestination { get; set; }
private string name;
public string Name { get; set; }
private string type;
public string Type { get; set; }
private string ringingTime;
public string RingingTime { get; set; }
private int priority;
public int Priority { get; set; }
private int huntBusy;
public int HuntBusy { get; set; }
public destination() { }
}
Every time you want to populate the GridView you will need to do the following:
GridViewDestination.DataSource = ConvertArrayListToDataTable(listSelectedDestinations);
GridViewDestination.DataBind();
where ConvertArrayListToDataTable is the following:
public static DataTable ConvertArrayListToDataTable(ArrayList arrayList)
{
DataTable dt = new DataTable();
if (arrayList.Count != 0)
{
dt = ConvertObjectToDataTableSchema(arrayList[0]);
FillData(arrayList, dt);
}
return dt;
}
public static DataTable ConvertObjectToDataTableSchema(Object o)
{
DataTable dt = new DataTable();
PropertyInfo[] properties = o.GetType().GetProperties();
if (o.GetType() == typeof(destination))
{
foreach (PropertyInfo property in properties)
{
DataColumn dc = new DataColumn(property.Name);
dc.DataType = property.PropertyType; dt.Columns.Add(dc);
}
}
return dt;
}
private static void FillData(ArrayList arrayList, DataTable dt)
{
foreach (Object o in arrayList)
{
DataRow dr = dt.NewRow();
PropertyInfo[] properties = o.GetType().GetProperties();
if (o.GetType() == typeof(destination))
{
foreach (PropertyInfo property in properties)
{
dr[property.Name] = property.GetValue(o, null);
}
}
dt.Rows.Add(dr);
}
}
As far as I know, this is using reflection: using the arraylist of destinations to bind it to a gridview.
On the other hand, your GridView should be defined like this:
<asp:GridView ID="GridViewDestination" runat="server" Visible="False" Width="98%" CssClass="GridView" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="idNonAnsweredCreating" runat="server" Text='<%# bind("idDestination") %>' Visible="false"></asp:Label>
<asp:Label Visible="true" runat="server" ID="destinationLabelCreating" Text='<%# bind("name") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Type">
<ItemTemplate>
<asp:Label Visible="true" runat="server" ID="destinationTypeLabelCreating" Text='<%# bind("type") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
As you can see, you will bind as many destinations properties as needed in each column of the GridView.
Hope this helps.

Related

ASP.NET Error when binding a GridView to a List of Objects?

Error Message:
The data source for GridView with id 'FormProprietari' did not have
any properties or attributes from which to generate columns. Ensure
that your data source has content.
I have a ASP.NEt application in which I try to bind a GridView to a List<T> objects which from what I can tell on the net it should be possible.
This is my GridView:
<asp:GridView ID="FormProprietari" runat="server">
<Columns>
<asp:TemplateField >
<ItemTemplate>
<%#((Lab_TAP_web.Proprietar)Container.DataItem).NumeProprietar%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField >
<ItemTemplate>
<%#((Lab_TAP_web.Proprietar)Container.DataItem).PrenumeProprietar%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField >
<ItemTemplate>
<%#((Lab_TAP_web.Proprietar)Container.DataItem).ProprietarID%>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And this is the function in which I add a new object of the Proprietar class I defined to the database then rebind the gridview to show it, I should mention that intially the database is empty and so is the gridview i,e it doesn't show up.
protected void Button2_Click(object sender, EventArgs e)
{
var Nume = TBNumeProprietar.Text;
var Prenume = TBPreNumeProprietar.Text;
MyCars db = DBSilo.db;
Proprietar newOwner = new Proprietar();
newOwner.NumeProprietar = Nume;
newOwner.PrenumeProprietar = Prenume;
newOwner.ProprietarID = (db.Proprietari.Count() + 1);
//newOwner.ProprietarID = 1;
db.Proprietari.InsertOnSubmit(newOwner);
db.SubmitChanges();
try
{
FormProprietari.DataSource = db.Proprietari.ToList();
}
catch (Exception)
{
throw;
}
FormProprietari.DataBind();
}
The problem being that I checked the List<Proprietar> which is the data source with breakpoints and the list always contains a Proprietar object.
Does anyone have a idea what I did wrong?
Moved from comment to Answer.
Please make sure you have getter and setter for each property of Proprietar class like this -
public class Proprietar
{
public string NumeProprietar { get; set; }
public string PrenumeProprietar { get; set; }
public int ProprietarID { get; set; }
}

How to populate asp:GridView from code?

I have a List<string[]> items list filled with arrays of strings in my code. On the ASPX page, I have added a new grid view control:
<asp:GridView ID="ProductList" runat="server" AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="ProductID" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier" ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}" HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" />
</Columns>
</asp:GridView>
I know that I should specify the DataSourceID attribute for the grid view in a fashion similar to this:
<asp:GridView ... `DataSourceID="ObjectDataSource1" ... >
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
But, I don't know what do OldValuesParameterFormatString, SelectMethod and TypeName attributes represent. Also, I don't have the database to bind to, I just have the list of string arrays named items. Can you help me populate the grid view? It is not necessary to do it through the binding at all. Thanks!
You don't even need an ObjectDataSource. At Page_Load in the codebehind, you can parse through the string array list and create a DataTable on the fly.
Make sure to wrap this around a Not Page.IsPostback so that it doesn't rebind on postback.
List<string[]> stringList = null;
DataTable dt = new DataTable();
DataRow dr = null;
dt.Columns.Add("ProductName", System.Type.GetType("System.String"));
dt.Columns.Add("CategoryName", System.Type.GetType("System.String"));
dt.Columns.Add("SupplierName", System.Type.GetType("System.String"));
dt.Columns.Add("UnitPrice", System.Type.GetType("System.Double"));
dt.Columns.Add("Discontinued", System.Type.GetType("System.String"));
foreach (string[] s in stringList) {
foreach (string str in s) {
dr = dt.NewRow();
dr["ProductName"] = s[0];
dr["CategoryName"] = s[1];
dr["SupplierName"] = s[2];
dr["UnitPrice"] = s[3];
dr["Discontinued"] = s[4];
dt.Rows.Add(dr);
}
}
dt.AcceptChanges();
ProductList.DataSource = dt;
ProductList.DataBind();
Edit: You'll have to change some of this code to match your needs. I'm making assumptions on the data types stored in your string[] based on the GridView code you provided.
You'll want to look into creating a class to use for your items, instead of a string[]. Maybe something like:
public class MyObject
{
public string ProductName { get; set; }
public string CategoryName { get; set; }
public string SupplierName { get; set; }
public decimal UnitPrice { get; set; }
public bool Discontinued { get; set; }
}
You'd then need something to translate the string[] into MyObject:
private MyObject ConvertToMyObject(string[] values)
{
var myObject = new MyObject();
myObject.ProductName = values[0];
myObject.CategoryName = values[1];
myObject.SupplierName = values[2];
decimal unitPrice;
if (decimal.TryParse(values[3], out unitPrice))
{
myObject.UnitPrice = unitPrice;
}
bool discontinued;
if (boo.TryParse(values[4], out discontinued))
{
myObject.Discontinued = values[4];
}
}
Then you could use that method in your code and then bind:
protected void Page_Load(parameters here)
{
if (IsPostback)
{
var myObjects = new List<MyObject>();
foreach (string[] values in items)
{
myObjects.Add(ConvertToMyObject(values));
}
ProductList.DataSource = myObjects;
ProductList.DataBind();
}
}
Like I said in the comment, make sure you have access to items in the Page_Load event handler. Also, I typed this from memory, forgot the parameters in Page_Load. You most likely already have a Page_Load handler in your code behind though.
You can try this
List<string[]> items = List<string[]>();
//add items to List
GridView1.DataSource = items;
GridView1.DataBind();
Your GridView
<asp:GridView runat="server" ID="GridView1">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="stringLabel" Text="<%# Container.DataItem %>" runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

Summing a Child Data-Source data-column in a Grid Item Template Column

I am working with three tables in a db. The first table is labeled 'Accounts' and has a one-many relationship with the second, 'AccountItems'.The AccountItems table has a size column. The third table, somewhat tangential to this question, is 'Customer' which has many accounts.
I am Creating a RadGrid that is bound to the Account table. It should look something like this:
Customer | Location | Account# | Total Size
___________________________________________
Location and Account # are easy, as was Customer because the navigation property worked in an item template.
My problem however, is the Size column. It should total the Size from each AccountItem. Example, if there are four AccountItems with a size of '50' each, then the total should be 200. It does not seem like I can simply navigate the child relationship in an Eval() like I did for the Customer. Is it possible to do this in the column code, or do I need to do a server side event handler for OnItemCreated?
Edit
I figured it out for a temp fix.
<ItemTemplate>
<asp:Label ID="lblSizeSum" runat="server"Text='<%# CalculateSizeTotal((MS_Accounts)Container.DataItem)%>'>
<ItemTemplate>
And then the code behind:
Protected string CalculateSizeTotal(MS_Accounts dataItem)
{
return dataItem.MS_AccountLoads.Sum(s => s.Size).ToString();
}
The only thing I don't like about this is the clear violation of MVC by accessing my ORM directly for the casting.
Please check below code snippet.
Let me know if i am missing any thing or not understand your requirement.
.aspx
<telerik:RadGrid ID="RadGrid2" runat="server" AllowPaging="True" AllowSorting="True"
OnNeedDataSource="RadGrid2_NeedDataSource" AutoGenerateColumns="False"
onitemdatabound="RadGrid2_ItemDataBound">
<MasterTableView>
<Columns>
<telerik:GridBoundColumn DataField="CustomerID" HeaderText="CustomerID" UniqueName="CustomerID">
</telerik:GridBoundColumn>
<telerik:GridTemplateColumn>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" ></asp:Label>
</ItemTemplate>
</telerik:GridTemplateColumn>
</Columns>
</MasterTableView>
</telerik:RadGrid>
.aspx.cs
public partial class forumpage : System.Web.UI.Page
{
protected void RadGrid2_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
RadGrid2.DataSource = getDatarofGrid();
}
protected List<Customer> getDatarofGrid()
{
List<Customer> lstCustomer = new List<Customer>();
Customer customer;
for (int i = 0; i < 3; i++)
{
customer = new Customer();
customer.CustomerID = i;
List<Sizes> lstSize = new List<Sizes>();
Sizes sizes;
for (int j = 0; j < 3; j++)
{
sizes = new Sizes();
sizes.size = i + 100;
lstSize.Add(sizes);
}
customer.sizes = lstSize;
lstCustomer.Add(customer);
}
return lstCustomer;
}
protected void RadGrid2_ItemDataBound(object sender, GridItemEventArgs e)
{
if (e.Item is GridDataItem)
{
GridDataItem item = e.Item as GridDataItem;
Label Label1 = item.FindControl("Label1") as Label;
Customer objcustomer = item.DataItem as Customer;
if (objcustomer != null && objcustomer.sizes.Count > 0)
{
Label1.Text = objcustomer.sizes.Sum(i => i.size).ToString();
}
}
}
}
public class Customer
{
public int CustomerID { get; set; }
public List sizes { get; set; }
}
public class Sizes
{
public int size { get; set; }
}

How to Combine TemplateFields in ASP.Net GridView?

Everyone.
I have a question when combining cells in GridView. I konw how to combine BoundField cells, but I do not know how to combine TemplateField cells in asp.net GridView.
EDIT:
Perhaps I did not make my question clearly and I am sorry about that. My question is that I use GridView to bind data from db and There is a field named UserName, one user has several records in the db, so I want to combine UserName in one cell(i can combine it correctly). In the same way, I want to do some operation to this user such as Add, Delete. So i put these operations into TemplateField, but i do not konw how to combine TemplateField like BoundField. I have a low reputation, so i can not post images
Any good ideas?
Sorry for my poor english! Thanks in advance.
There isn't a way to merge/combine fields. I think you misunderstood the BoundFields/TemplateField. However you can use Eval() and Bind() to show/bind one or more expression into single cell//column..
Read MSDN articles:
BoundField
TemplateField
EDIT:
#loren : There is a field named UserName, one user has several records
in the db.
I guess you need to use "nested" grid or you may also use any data control (formview, detail view or ListView).
Here is demo that shows how to bind nested data controls.
I've define two classes - Contact, Info
public class Contact
{
public string Address{get;set;}
public string Phone {get;set;}
}
public class Info
{
public string UserName {get;set;}
private List<Contact> _addr=new List<Contact>();
public List<Contact> Address
{
get { return _addr; }
set { _addr = value; }
}
}
In .aspx page (Markup),
<asp:GridView
ID="GridView1"
runat="server"
AutoGenerateColumns="false"
onrowdatabound="GridView1_RowDataBound"
>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<p>
Username :
<asp:Literal
ID="UserName"
runat="server"
Text='<%#Eval("UserName") %>'
>
</asp:Literal>
</p>
<asp:GridView
ID="GridView2"
runat="server"
AutoGenerateColumns="false"
>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<p>Phone :
<asp:TextBox
runat="server"
ID="txtDetail"
Text='<%#Bind("Phone") %>'
></asp:TextBox>
</p>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and in code-behind (aspx.cs),
List<Info> info;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
info = new List<Info>()
{
new Info()
{
UserName="User1",
Address =
{
new Contact() { Phone="2929927", Address="Address1"},
new Contact() { Phone="2929928", Address="Address2"},
}
},
new Info()
{
UserName="User2",
Address =
{
new Contact() { Phone="2929929", Address="Address3"},
new Contact() { Phone="2929930", Address="Address4"},
}
},
};
GridView1.DataSource = info;
GridView1.DataBind();
}
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
Literal username=(Literal)e.Row.FindControl("UserName");
GridView view=(GridView)e.Row.FindControl("GridView2");
if (view != null)
{
var result = from ele in info
from add in ele.Address
where ele.UserName == username.Text
select add;
view.DataSource = result;
view.DataBind();
}
}
If possible, you can update the source type.
For example, if you an object data source and a model like this :
[DataObject]
public class MyOds{
[DataObjectMethod(DataObjectMethodType.Select)]
public ICollection<MyModel> GetMyModels()
{
var result = new ListMyModel();
Populate(result); // load model with any method of you choice
return result;
}
}
public class MyModel{
public string Address { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string FullAddress {
get
{
return string.Format("{0} - {1} - {2}", Address, City, Country);
}
}
}
Note the FullAddress property. The idea is to build, for the view, properties easy to exploit. The ASPX can looks like this :
<asp:TemplateField HeaderText="Header">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text="Label">
<%# Eval("FullAddress")%>
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
you can use Eval as bellow
<asp:TemplateField HeaderText="Header">
<ItemTemplate>
<asp:TextBox runat="server" ID="txt1" Text='<%#Bind("Phone") %>'></asp:TextBox>
<asp:TextBox runat="server" ID="txt2" Text='<%#Bind("Address") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>

Getting data from a checkbox inside a template column of asp.net gridview

This seems like something simple, but I can't seem to figure it out! I'm trying to get 2-way data-binding to work on an ASP.net page with a check box as one of the columns. How do I get the updated values (from check boxes) back from the gridview ?????
Here is my data type:
[Serializable]
public class UserRequirements
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserId { get; set; }
public string Email { get; set; }
public bool ThingRequired { get; set; }
}
My markup looks something like this:
<form id="form1" method="post" runat="server" >
<asp:GridView ID="UserTable" runat="server" AutoGenerateColumns="false" >
<Columns>
...
<asp:TemplateField HeaderText="Required ?">
<ItemTemplate>
<asp:CheckBox id="chkBox1" runat="server" on
Text ="Required"
checked='<%# DataBinder.Eval(Container.DataItem,"ThingRequired") %>'>
</asp:CheckBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button id="thebutton" Text="Save Changes" OnClick="UpdateRequirements" runat="server" CausesValidation=false />
</form>
My code behind looks something like this:
List<UserRequirements > _userList = new List<UserRequirements >();
protected void Page_Load(object sender, EventArgs e)
{
_userList = data_layer.GetUserRequirments();
this.UserTable.DataSource = _userList;
this.UserTable.DataBind();
}
Eventually, I will call something like this, but I don't know where this should go or how to get the values back from the gridview:
void UpdateRequirements(object sender, EventArgs e)
{
_userList = ???????????? // How do I get the data?
data_layer.UpdateUserRequirements( _userList );
}
foreach (GridViewRow di in GridView1.Rows)
{
HtmlInputCheckBox chkBx = (HtmlInputCheckBox)di.FindControl("chkBox1");
if (chkBx != null && chkBx.Checked)
{
/// put your code here
}
}
try something like this to get the value on change:
protected void OnCheckedChanged(object sender, EventArgs e)
{
CheckBox c = (CheckBox)sender as CheckBox;
string checkBoxId = c.ID;
bool checkBoxValue = c.Checked;
//update database
}
[EDIT]
If you want to get all the values from the rows in the grid in one go, you will need to bind the checkboxes using the Id for the row or item in your list of UserRequirements, so in your grid do something like this:
<asp:CheckBox ID="<%# Eval('Id') %>" />
then on postback, iterate through the items in the UserRequirements list matching the object/item Id with the Ids of the checkboxes in the grid .. something like this:
foreach (UserRequirement item in Requirements)
{
Control c = grid.FindControl(item.Id);
CheckBox cbx = c as CheckBox;
if (cbx != null)
{
bool value = cbx.Checked;
//update db
}
}
Note: you may need to use FindControl recursively to search child controls, or do a foreach on each GridViewRow object in the grid to pickup the checkbox you are looking for.

Resources