I am trying to get some values from a List and then create a html table with this data but I can't get it to work properly.
I have:
HtmlTable table = new HtmlTable();
HtmlTableRow row;
HtmlTableCell cell;
foreach(var item in Name)
{
row = new HtmlTableRow();
foreach(var familyName in item.familyName)
{
cell = new HtmlTableCell();
cell.InnerText = item.familyName.ToString();
row.Cells.Add(cell);
}
foreach (var givenName in item.givenName)
{
cell = new HtmlTableCell();
cell.InnerText = item.givenName.ToString();
row.Cells.Add(cell);
}
table.Rows.Add(row);
}
this.Controls.Add(table);
When I step through the debugger I can see that row.Cells.Add(cell) contains the family name in the first loop and given name in the second loop but then something seems to be wrong and I can't get the table to show up on the page with this data.
When I check the table.rows.add(row) it says that
base {System.SystemException} = {"'HtmlTableRow' does not support the InnerText property."}
What am I doing wrong here?
I've stepped through your code and I can't replicate the error you mention.
It's difficult to say for sure without seeing your data structure Name but a couple of observations:
I. If familyName is a string, your inner foreach will execute once for each character in the string. This may not be what you want as it'll output a surname x number of times where x = surname.length.
This will result in unequal numbers of table cells per row unless all your surnames are the same length.
So I would say get rid of the
foreach(var familyName in item.familyName){...}
loop and just leave the code inside so it'll output surname just once.
II. I'm guessing that item.givenName is an array or collection e.g. List<> of strings? If so you could just use
cell.InnerText = givenName;
Note that this is will still give you uneven numbers of table cells per row because people have different numbers of forenames ;-)
Having said that you really ought to use the built in controls for doing this kind of thing - the Repeater is probably the way to go.
E.g.
Markup
<asp:Repeater runat="server" id="rptNames" onItemDataBound="rptName_ItemDataBound" >
<HeaderTemplate>
<table>
<tr>
<td>Given Name(s)</td>
<td>Family Name</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Eval("FamilyName") %></td>
<td>
<asp:Label runat="server" id="lGivenNames" />
</td>
</tr>
<ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
CodeBehind
Probably triggered by Page_Load - just bind your repeater to your Name collection:
rptNames.DataSource = Name;
rptNames.DataBind();
To output the GivenNames you use the ItemDataBound event which gets called for each row of the repeater:
protected void rptNames_ItemDataBound(object sender, RepeaterItemEventArgs e){
//Not interested the Header and Footer rows
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem){
Label l = ((Label)e.Item.FindControl("lGivenNames"));
string[] arrGivenNames = ((FullName)e.Item.DataItem).GivenNames;
foreach (string n in arrGivenNames){//could use a StringBuilder for a performance boost.
l.Text += n + " "; //Use a regular space if using it for Winforms
}
//For even slicker code, replace the Label in your repeater with another repeater and bind to that. Google `nested repeater` for a how to.
}
}
HTH.
Full Code
<h2>Doing it by hand - manually building up an HTML Table</h2>
<asp:Panel runat="server" ID="pnl1">
</asp:Panel>
<h2>With a Repeater</h2>
<asp:Repeater runat="server" id="rptNames" onItemDataBound="rptName_ItemDataBound" >
<HeaderTemplate>
<table border="1" style="border-color:Red;">
<tr>
<td>Given Name(s)</td>
<td>Family Name</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Eval("FamilyName") %></td>
<td>
<asp:Label runat="server" id="lGivenNames" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace Testbed.WebControls
{
internal class FullName{
public string FamilyName{get;set;}
public string[] GivenNames{get;set;}
public FullName(){
}
public FullName(string[] _givenNames, string _familyName)
{
FamilyName = _familyName;
GivenNames = _givenNames;
}
}
public partial class HTMLTables : System.Web.UI.Page
{
List<FullName> Name;
protected void Page_Load(object sender, EventArgs e)
{
this.Name = new List<FullName>();
Name.Add(new FullName(new string[]{"Kylie"},"Minogue"));
Name.Add(new FullName(new string[]{"Angelina", "Kate", "Very-Lovely"}, "Jolie"));
Name.Add(new FullName(new string[]{"Audrey", "Veronica"},"Hepburn"));
HtmlTable table = new HtmlTable();
table.Border = 1;
HtmlTableRow row;
HtmlTableCell cell;
row = new HtmlTableRow();
cell = new HtmlTableCell();
cell.InnerText = "Given Name";
row.Cells.Add(cell);
cell = new HtmlTableCell();
cell.InnerText = "Family Name";
row.Cells.Add(cell);
foreach (var item in Name)
{
row = new HtmlTableRow();
//foreach (var familyName in item.FamilyName){
cell = new HtmlTableCell();
cell.InnerText = item.FamilyName.ToString();
row.Cells.Add(cell);
//}
foreach (string givenName in item.GivenNames)
{
cell = new HtmlTableCell();
cell.InnerText = givenName.ToString();
row.Cells.Add(cell);
}
table.Rows.Add(row);
}
this.pnl1.Controls.Add(table);
//Or do it with a repeater
rptNames.DataSource = Name;
rptNames.DataBind();
}
//This gets called everytime a data object gets bound to a repeater row
protected void rptName_ItemDataBound(object sender, RepeaterItemEventArgs e){
switch(e.Item.ItemType){
case ListItemType.Item:
case ListItemType.AlternatingItem:
string[] arrGivenNames = ((FullName)e.Item.DataItem).GivenNames;
foreach(string n in arrGivenNames){
((Label)e.Item.FindControl("lGivenNames")).Text += n + #" ";
}
break;
default:
break;
}
}
}
}
Related
I have a gridview ,gridview data is from database.I have a last column which is asp:FileUpload for user to insert new attachment if they want to,thing i wanted to do here is ,my backend c# will check if row asp file upload is not empty,then will upload new revision to that form.if empty,dont upload new revision to that form.
<asp:TemplateField HeaderText="Upload New Form" HeaderStyle-BackColor="DarkBlue" HeaderStyle-Font-Bold="false" HeaderStyle-ForeColor="White" ItemStyle-Width="5%">
<ItemTemplate>
<asp:FileUpload ID="newdoc" runat="server"/>
</ItemTemplate>
</asp:TemplateField>
this is the column i created for user to insert new file if they want.
protected void Button11_Click(object sender, EventArgs e)
{
//System.Diagnostics.Debugger.Launch();
string data = "";
foreach (GridViewRow row in GridView3.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
FileUpload chkRow = (row.Cells[7].FindControl("newdoc") as FileUpload);
if (FileUploadControl.HasFile)
{
string id = row.Cells[2].Text + '-'+row.Cells[3].Text;
data = data + id + ",";
}
}
}
string[] alldata = data.Split(',');
foreach (string dataid in alldata )
{
if (dataid.Equals(""))
continue;
ClientScript.RegisterStartupScript(GetType(), "alert", "alert('" + dataid + "');", true);
}
}
my c# logic is like this,i will check each row,if the uploadcontrol is not empty then dont add the id into data list,after collect all the row that needed to update new revision i will split the list update it 1 by 1,i try to alert out the id but value i get is empty.any ideas where i did wrong??
i suspeect something wrong in this line " string id = row.Cells[2].Text + '-' + row.Cells[3].Text;"
use BoundField instead of TemplateField..
string test = row.Cells[2].Text;
I want the checkbox control to be added dynamically with different id's in different th tags generating in a loop
<table border="1">
<thead>
<%string j = " Check"; %>
<%for (int i = 0; i < 10;i++ )
{%>
<th style="padding:2px; width:500px;">Table Head<br /><br />
<%
CheckBox chk = new CheckBox();
chk.ID = i + j;
chk.Text = "I am " + i + j;
%>
//I want this checkbox to be added dynamically here with different id's in different th tags generating in a loop
<asp:CheckBox runat="server" ID="<%=i+j%>"/>
</th>
<%} %>
</thead>
</table>
the way to do this is to create yourself a server-control with all the parameters you need, creating the controls in the OnInit, and rendering html in the RenderControl, and accessing the controls from public props like this:
public class DynamicCbs : Control
{
public int CtrlsCount { get; set; }
public List<CheckBox> lstCheckBoxs;
/// decleration of controls must be in the OnInit since the next stage of the page life cycle is to connect whatever came back from the client to the server
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
lstCheckBoxs = new List<CheckBox>();
for (int i = 0; i < CtrlsCount; i++)
{
string id = "DynamicCbs" + i;
CheckBox cbx = new CheckBox()
{
ID = id,
Text = "i am " + id
};
lstCheckBoxs.Add(cbx);
//add controls to control tree
this.Controls.Add(cbx);
}
}
/// here you must build ur html
public override void RenderControl(HtmlTextWriter writer)
{
writer.RenderBeginTag(HtmlTextWriterTag.Table);
writer.RenderBeginTag(HtmlTextWriterTag.Thead);
foreach (var cbx in lstCheckBoxs)
{
writer.RenderBeginTag(HtmlTextWriterTag.Th);
cbx.RenderControl(writer);
writer.RenderEndTag();
}
writer.RenderEndTag();//thead
writer.RenderEndTag();//table
}
}
full example
ok I found the solution. I have use asp:Table control to solve this problem
My aspx page code is :
<asp:Table ID="ObjectwiseTable2" runat="server"
CssClass="AccessTable" BorderColor="Black" width="100%">
</asp:Table>
My .cs page code to Add content and dynamic content in the table is :
TableHeaderRow thead = new TableHeaderRow();
TableHeaderCell th = new TableHeaderCell();
th.Controls.Add(new LiteralControl("Object Wise Detail(s)"));
th.RowSpan = 2;
thead.Cells.Add(th);
int totalUsers = accesswiseDt.Rows.Count;
for (int User = 0; User < totalUsers; User++)
{
TableHeaderCell th2 = new TableHeaderCell();
th2.Controls.Add(new LiteralControl(accesswiseDt.Rows[User]["users"].ToString()));
IsReviewPending = view_access.IsWaitingForViewAccess(ApplicationTree.SelectedNode.Value, Session["empCode"].ToString(), accesswiseDt.Rows[User]["empcode"].ToString());
if (IsReviewPending)
{
th2.Controls.Add(new LiteralControl("<br />"));
CanReviewAccess = true;
//Code for Adding Dynamic control in specific cell of the table
CheckBox chk = new CheckBox();
chk.ID = ApplicationTree.SelectedNode.Value + "_" + accesswiseDt.Rows[User]["empcode"].ToString();
chk.Text = "Access Reviewed";
th2.Controls.Add(chk);
}
thead.Cells.Add(th2);
}
I've just started playing with developing in SP2010, so I'm complete begginer .
I've created simple console application with code like this :
using (SPSite currentSite = new SPSite("http://win-e9840laitme"))
{
using (SPWeb currentWeb = currentSite.OpenWeb())
{
SPList myList = currentWeb.Lists["List1"];
string currentUserName = currentWeb.CurrentUser.Name;
SPQuery queryInformationAboutCurrentUser = new SPQuery();
queryInformationAboutCurrentUser.Query = "<Where>" +
"<Eq><FieldRef Name='EmployeeName'/><Value Type='Text'>" + currentUserName + "</Value></Eq>" +
"</Where>";
List<EmployeeInfo> listEmployeeInfo = new List<EmployeeInfo>();
SPListItemCollection collectionEmployee = myList.GetItems(queryInformationAboutCurrentUser);
foreach (SPListItem info in collectionEmployee)
{
EmployeeInfo eInfo = new EmployeeInfo();
eInfo.Deparment = info["Office"].ToString();
listEmployeeInfo.Add(eInfo);
}
foreach (EmployeeInfo eI in listEmployeeInfo)
{
SPQuery querySameOffice = new SPQuery();
querySameOffice.Query = "<Where>" +
"<Eq><FieldRef Name='Office'/><Value Type='Choice'>" + eI.Deparment + "</Value></Eq>" +
"</Where>";
SPListItemCollection collectionEmployeeDisplay = myList.GetItems(querySameOffice);
foreach (SPListItem item in collectionEmployeeDisplay)
{
Console.WriteLine(item["EmployeeName"].ToString() + " " + item["PhoneNumber"].ToString() + "\n");
}
}
}
}
Now I want to use that code inside sharepoint project , and instead of putting result of SPQuery on Console , I want to populate HTML table with the result staticly(without JS or Jquery , if it is possible).
I've created sp project and added ApplicationPage.
My idea was to use StringBuilder and something like this :
StringBuilder html = new StringBuilder();
html.Append("<table border = '1'>");
//Building the Header row.
html.Append("<tr>");
html.Append("<th>");
html.Append("EmployeeName");
html.Append("</th>");
html.Append("<th>");
html.Append("PhoneNumber");
html.Append("</th>");
html.Append("</tr>");
and in the last foreach loop this code :
foreach (SPListItem item in collectionEmployeeDisplay)
{
html.Append("<tr>");
html.Append("<td>");
html.Append(item["EmployeeName"].ToString());
html.Append("</td>");
html.Append("<td>");
html.Append(item["PhoneBook"].ToString());
html.Append("</td>");
html.Append("</tr>");
}
//Table end.
html.Append("</table>");
//Append the HTML string to Placeholder.
PlaceHolder1.Controls.Add(new Literal { Text = html.ToString() });
I beleive there are more elegant solutions to this problem, and if you now it please give me idea .Thank you.
I've found a solution.First we need to add Repeater into PlaceHolderMain :
<asp:Repeater ID="rptEmployees" runat="server">
<HeaderTemplate>
<table border="1" style="width:70%">
<tr>
<td>Employee Name</td>
<td>Phone Number</td>
<td>Position</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Eval("EmployeeName") %></td>
<td><%# Eval("PhoneNumber") %></td>
<td><%# Eval("Position") %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
than create another class and add this code into final loop:
foreach (SPListItem item in collectionEmployeeDisplay)
{
DisplayEmployee dE = new DisplayEmployee();
dE.EmployeeName = item[Sol.PB1.Fields.EmployeeName].ToString();
dE.PhoneNumber = item[Sol.PB1.Fields.PhoneNumber].ToString();
dE.Position = item[Sol.PB1.Fields.Position].ToString();
display.Add(dE);
}
rptEmployees.DataSource = display;
rptEmployees.DataBind();
}
You cant use XSLT transformation to convert your SPQuery results to an HTML table. see example of Applying an XSLT Transform to a DataSet
I have some settings stored in web.config like this:
<add key="Answers" value="radiobutton1,radiobutton2,radiobutton3"/>
Radiobutton1, radiobutton2 and radiobutton3 are radiobutton label values.
In settings.cs I have a function to retrieve value from web.config:
public static string Answers
{
get
{
return System.Configuration.ConfigurationManager.AppSettings["Answers"];
}
}
.ascx file:
<table runat="server" OnPreRender="Radio_PreRender" id="table1" name="table1">
</table>
My ascx.cs file contains this function:
protected void Radio_PreRender(object sender, EventArgs e)
{
if (Settings.Answers != "")
{
int counter = 0;
string a = Settings.Answers;
string[] words = a.Split(',');
StringWriter stringwriter = new StringWriter();
HtmlTextWriter writer = new HtmlTextWriter(stringwriter);
foreach (string word in words)
{
writer.WriteBeginTag("tr");
writer.WriteBeginTag("td");
writer.Write("abc123");
RadioButton rdb1 = new RadioButton();
rdb1.Checked = true;
rdb1.GroupName = "rdbgroup";
rdb1.ID = "radiobutton" + counter;
rdb1.Text = word;
table1.Controls.Add(rdb1 );
writer.WriteEndTag("td");
writer.WriteBeginTag("tr");
table1.Render(writer);
counter++;
}
}
}
In other words, I want to generate a dynamic number of this code inside table1:
<tr>
<td>
// input type="radiobutton" and label go here.
</td>
</tr>
At the moment radiobuttons are not generated, because they can't be direct child elements to a table. If I specify a div instead, radiobuttons are generated, but everything I try to write with HtmlTextWriter is not. I understand that my html has to be rendered by using table1.Render(writer); or something similar, but I can't figure it out.
You can try creating a table and add it to the page you are working on, using the exmaple below you can replace the textbox with a radiobutton
Here is an example:
//Creat the Table and Add it to the Page
Table table = new Table();
table.ID = "Table1";
Page.Form.Controls.Add(table);
// Now iterate through the table and add your controls
for (int i = 0; i < rowsCount; i++)
{
TableRow row = new TableRow();
for (int j = 0; j < colsCount; j++)
{
TableCell cell = new TableCell();
TextBox tb = new TextBox();
// Set a unique ID for each TextBox added
tb.ID = "TextBoxRow_" + i + "Col_" + j;
// Add the control to the TableCell
cell.Controls.Add(tb);
// Add the TableCell to the TableRow
row.Cells.Add(cell);
}
// Add the TableRow to the Table
table.Rows.Add(row);
}
I do this quite often by running the user control through its full asp.net lifetime and getting it as a string:
http://www.diaryofaninja.com/blog/2009/09/14/a-simple-solution-to-viewing-a-preview-of-any-page-in-your-site
This way you can use ASP.Net as a templating engine for anything
Page tempPage = new Page();
UserControl myUserControl = new MyUserControl();
tempPage.Controls.Add(myUserControl);
StringWriter sw = new StringWriter();
HttpContext.Current.Server.Execute(tempPage, sw, false);
if (!String.IsNullOrEmpty(sw.ToString()))
{
return sw.ToString();
}
Ok so I need to query a live website to get data from a table, put this HTML table into a DataTable and then use this data. I have so far managed to use Html Agility Pack and XPath to get to each row in the table I need but I know there must be a way to parse it into a DataTable. (C#) The code I am currently using is:
string htmlCode = "";
using (WebClient client = new WebClient())
{
htmlCode = client.DownloadString("http://www.website.com");
}
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(htmlCode);
//My attempt at LINQ to solve the issue (not sure where to go from here)
var myTable = doc.DocumentNode
.Descendants("table")
.Where(t =>t.Attributes["summary"].Value == "Table One")
.FirstOrDefault();
//Finds all the odd rows (which are the ones I actually need but would prefer a
//DataTable containing all the rows!
foreach (HtmlNode cell in doc.DocumentNode.SelectNodes("//tr[#class='odd']/td"))
{
string test = cell.InnerText;
//Have not gone further than this yet!
}
The HTML table on the website I am querying looks like this:
<table summary="Table One">
<tbody>
<tr class="odd">
<td>Some Text</td>
<td>Some Value</td>
</tr>
<tr class="even">
<td>Some Text1</td>
<td>Some Value1</td>
</tr>
<tr class="odd">
<td>Some Text2</td>
<td>Some Value2</td>
</tr>
<tr class="even">
<td>Some Text3</td>
<td>Some Value3</td>
</tr>
<tr class="odd">
<td>Some Text4</td>
<td>Some Value4</td>
</tr>
</tbody>
</table>
I'm not sure whether it is better/easier to use LINQ + HAP or XPath + HAP to get the desired result, I tried both with limited success as you can probably see. This is the first time I have ever made a program to query a website or even interact with a website in any way so I am very unsure at the moment! Thanks for any help in advance :)
Using some of Jack Eker's code above and some code from Mark Gravell (see post here) , I managed to come with a solution.
This code snippet is used to obtain the public holidays for the year of 2012 in South Africa as of writing this article
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Web;
using System.Net;
using HtmlAgilityPack;
namespace WindowsFormsApplication
{
public partial class Form1 : Form
{
private DataTable dt;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string htmlCode = "";
using (WebClient client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.UserAgent, "AvoidError");
htmlCode = client.DownloadString("http://www.info.gov.za/aboutsa/holidays.htm");
}
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(htmlCode);
dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Value", typeof(string));
int count = 0;
foreach (HtmlNode table in doc.DocumentNode.SelectNodes("//table"))
{
foreach (HtmlNode row in table.SelectNodes("tr"))
{
if (table.Id == "table2")
{
DataRow dr = dt.NewRow();
foreach (var cell in row.SelectNodes("td"))
{
if ((count % 2 == 0))
{
dr["Name"] = cell.InnerText.Replace(" ", " ");
}
else
{
dr["Value"] = cell.InnerText.Replace(" ", " ");
dt.Rows.Add(dr);
}
count++;
}
}
}
dataGridView1.DataSource = dt;
}
}
}
}
There's no such method out of the box from the HTML Agility Pack, but it shouldn't be too hard to create one. There's samples out there that do XML to Datatable from Linq-to-XML. These can be re-worked into what you need.
If needed I can help out creating the whole method, but not today :).
See also:
HTML Agility pack - parsing tables
parsing html with HTMLAGILITYPACK and loading into datatable C#
This is my solution. May be a bit messy but it is working perfectly at the moment :D
string htmlCode = "";
using (WebClient client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.UserAgent, "AvoidError");
htmlCode = client.DownloadString("http://www.website.com");
}
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(htmlCode);
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Value", typeof(decimal));
int count = 0;
decimal rowValue = 0;
bool isDecimal = false;
foreach (var row in doc.DocumentNode.SelectNodes("//table[#summary='Table Name']/tbody/tr"))
{
DataRow dr = dt.NewRow();
foreach (var cell in row.SelectNodes("td"))
{
if ((count % 2 == 0))
{
dr["Name"] = cell.InnerText.Replace(" ", " ");
}
else
{
isDecimal = decimal.TryParse((cell.InnerText.Replace(".", "")).Replace(",", "."), out rowValue);
if (isDecimal)
{
dr["Value"] = rowValue;
}
dt.Rows.Add(dr);
}
count++;
}
}
Simple logic to convert a htmltable to datatable :
//Define your webtable
public static HtmlTable table
{
get
{
HtmlTable var = new HtmlTable(parent);
var.SearchProperties.Add("id", "searchId");
return var;
}
}
//Convert a webtable to datatable
public static DataTable getTable
{
get
{
DataTable dtTable= new DataTable("TableName");
UITestControlCollection rows = table.Rows;
UITestControlCollection headers = rows[0].GetChildren();
foreach (HtmlHeaderCell header in headers)
{
if (header.InnerText != null)
dtTable.Columns.Add(header.InnerText);
}
for (int i = 1; i < rows.Count; i++)
{
UITestControlCollection cells = rows[i].GetChildren();
string[] data = new string[cells.Count];
int counter = 0;
foreach (HtmlCell cell in cells)
{
if (cell.InnerText != null)
data[counter] = cell.InnerText;
counter++;
}
dtTable.Rows.Add(data);
}
return dtTable;
}
}
You can try
DataTable.Rows[i].Cells[j].InnerText;
Where DataTable is the id of your table, i is the row and j is the cells.