What is the shortest way to convert a DataTable into a string (formatted in HTML)?
Programmatically binding to a UI control and rendering to an ASP.NET page is not acceptable. Can't depend on the ASP.NET page lifecycle.
The purpose of this shouldn't matter, but to satisfy curiosity: This is for logging/debugging/dump purposes in algorithms that do a lot of DataTable processing.
Thanks!
You can use the ASP.net controls such as GridView, DataGrid and point them render into StringBuilder using StringWriter, No need to use ASP.net page for this, this is a simple example in Console
class Program
{
static void Main(string[] args)
{
IList<Person> persons = new List<Person>()
{
new Person{Id = 1, Name="Test Name 1"},
new Person{Id = 2, Name="Test Name 2"}
};
GridView gridView = new GridView();
StringBuilder result = new StringBuilder();
StringWriter writer = new StringWriter(result);
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
gridView.DataSource = persons;
gridView.DataBind();
gridView.RenderControl(htmlWriter);
Console.WriteLine(result);
}
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
I use this function through my application. It's pretty straightforward
static public string ConvertDataTableToHTMLString(System.Data.DataTable dt, string filter, string sort, string fontsize, string border, bool headers, bool useCaptionForHeaders)
{
StringBuilder sb = new StringBuilder();
sb.Append("<table border='" + border + "'b>");
if (headers)
{
//write column headings
sb.Append("<tr>");
foreach (System.Data.DataColumn dc in dt.Columns)
{
if (useCaptionForHeaders)
sb.Append("<td><b><font face=Arial size=2>" + dc.Caption + "</font></b></td>");
else
sb.Append("<td><b><font face=Arial size=2>" + dc.ColumnName + "</font></b></td>");
}
sb.Append("</tr>");
}
//write table data
foreach (System.Data.DataRow dr in dt.Select(filter,sort))
{
sb.Append("<tr>");
foreach (System.Data.DataColumn dc in dt.Columns)
{
sb.Append("<td><font face=Arial size=" + fontsize + ">" + dr[dc].ToString() + "</font></td>");
}
sb.Append("</tr>");
}
sb.Append("</table>");
return sb.ToString();
}
If this is just for purposes of logging, might it not make more sense to log them out as XML - easier to manipulate if you need to. You just need to call the WriteXml method.
Create the control, create an HTML Writer, set any settings or databind the control, then call the render method, using the HTML Writer.
You can then get the string out of the writer.
Edit: I initially misread the question and thought you wanted to render a datagrid.
A Datatable can easily be rendered to its XML.
you asked for HTML.
here is a console app code that will render a datatable using a datagrid control.
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Column1");
dt.Columns.Add("Column2");
dt.Rows.Add("RowValue1", "Field2RowValue1");
dt.Rows.Add("RowValue2", "Field2RowValue2");
DataGrid dg = new DataGrid();
dg.DataSource = dt;
dg.DataBind();
StringWriter sw = new StringWriter();
HtmlTextWriter w = new HtmlTextWriter(sw);
dg.RenderControl(w);
Console.Write(sw.ToString());
Console.ReadLine();
}
}
Related
I've used EPPlus to download my datatable from my website / database to an Excel sheet and the first picture is the result I get. The second picture is what I would like it to be.
Questions:
How do I change the format of my Timestamp to "time"?
Obviously title would still be a string format.
How do I make the width of the columns to match the longest word inside?
So that 80% of the message isn't hidden and you have to drag the column out to read the entire message.
Edit: Completely forgot to add my code...
public ActionResult ExportData()
{
DataTable dataTable = GetData();
using (ExcelPackage package = new ExcelPackage())
{
var ws = package.Workbook.Worksheets.Add("My Sheet");
//true generates headers
ws.Cells["1:1"].Style.Font.Bold = true;
ws.Cells["A1"].LoadFromDataTable(dataTable, true);
ws.Cells[ws.Dimension.Address].AutoFitColumns();
var stream = new MemoryStream();
package.SaveAs(stream);
string fileName = "Log.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
stream.Position = 0;
return File(stream, contentType, fileName);
}
}
public DataTable GetData()
{
DataTable dt = new DataTable();
if (ModelState.IsValid)
{
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ConnectionString))
{
using (SqlCommand comm = conn.CreateCommand())
{
comm.Parameters.AddWithValue("#val1", Session["myID"]);
comm.Parameters.AddWithValue("#val2", "%" + Session["mySearchString"] + "%");
comm.CommandText = "SELECT * FROM dbo.Log WHERE CustomerId = #val1 AND Message LIKE #val2";
try
{
conn.Open();
dt.Load(comm.ExecuteReader());
}
catch (SqlException e)
{
throw new Exception(e.ToString());
}
}
}
}
return dt;
}
Just need to set the Numberformat.Format string. Like this:
ws.Column(2).Style.Numberformat.Format = "hh:mm:ss";
If you want to customize the actual just there are plenty of resource online like http://www.ozgrid.com/Excel/excel-custom-number-formats.htm. Or you can just open it in excel, set the format to Custom and experiment with the string.
I have been given some c# code and have been asked to create a markup (.aspx) file that would go along with it.
I am not asking for help to write the code, but instead, how to go about it.
Here is the code:
public partial class search : Page
{
protected override void OnLoad(EventArgs e)
{
int defaultCategory;
try
{
defaultCategory = Int32.Parse(Request.QueryString["CategoryId"]);
}
catch (Exception ex)
{
defaultCategory = -1;
}
Results.DataSource = GetResults(defaultCategory);
Results.DataBind();
if (!Page.IsPostBack)
{
CategoryList.DataSource = GetCategories();
CategoryList.DataTextField = "Name";
CategoryList.DataValueField = "Id";
CategoryList.DataBind();
CategoryList.Items.Insert(0, new ListItem("All", "-1"));
CategoryList.SelectedIndex = CategoryList.Items.IndexOf(CategoryList.Items.FindByValue(defaultCategory.ToString()));
base.OnLoad(e);
}
}
private void Search_Click(object sender, EventArgs e)
{
Results.DataSource = GetResults(Convert.ToInt32(CategoryList.SelectedValue));
Results.DataBind();
}
private DataTable GetCategories()
{
if (Cache["AllCategories"] != null)
{
return (DataTable) Cache["AllCategories"];
}
SqlConnection connection = new SqlConnection("Data Source=DB;Initial Catalog=Store;User Id=User;Password=PW;");
string sql = string.Format("SELECT * From Categories");
SqlCommand command = new SqlCommand(sql, connection);
SqlDataAdapter da = new SqlDataAdapter(command);
DataTable dt = new DataTable();
da.Fill(dt);
Cache.Insert("AllCategories", dt, null, DateTime.Now.AddHours(1), System.Web.Caching.Cache.NoSlidingExpiration);
connection.Dispose();
return dt;
}
private DataTable GetResults(int categoryId)
{
SqlConnection connection = new SqlConnection("Data Source=DB;Initial Catalog=Store;User Id=User;Password=PW;");
string sql = string.Format("SELECT * FROM Products P INNER JOIN Categories C on P.CategoryId = C.Id WHERE C.Id = {0} OR {0} = -1", categoryId);
SqlCommand command = new SqlCommand(sql, connection);
SqlDataAdapter da = new SqlDataAdapter(command);
DataTable dt = new DataTable();
da.Fill(dt);
connection.Dispose();
return dt;
}
}
EDIT
In the above code, what is the Results object and is the CategoryList just a listbox?
As Nilesh said this seems like a search page, You can possibly try creating the a Webform using Visual studio which is just drag and drop controls into canvas and that will create the mark up for the controls in the code window.
This code behind seems to be doing the following,
On page load at Get request (when its !Page.IsPostBack) page is going to get categories using GetCategories() and fill the drop down list "CategoryList" with all category names (default selected one being the defaultcategory ID from query string).
The search button takes the dropdown's selected value and calls the GetResults() to get data table to fill the grid view "Results". So you need 3 controls (Dropdown list, Button, Gridview) in the webform with these names..
I have DataReader that holds the results from a stored procedure caal. The results consist of two fields ...
UserID
UserName
Normally I bind these results to an ASP.NET dropdownlist control ...
ddlUserList.DataSource = rdr // rdr is the DataReader
ddlUserList.DataTextField = "UserName"
ddlUserList.DataValueField = "UserID"
ddlUserList.DataBind()
However I am now trying to accomplish the same thing using jQuery AJAX. What I am stuck on is how to manually convert the dataset held in the DataReader to JSON. How are multiples values separated? Does this look correct?
{{"UserID":1, "UserName":"Bob"}, {"UserID":2, "UserName":"Sally"},{"UserID":3, "UserName":"Fred"}}
I realize there are libraries out there such as JSON.NET to handle the serialization but I am in the learning stage now and want to make sure I understand everything from the bottom up.
Was wondering if you have tried using System.Web.Script.Serialization.JavaScriptSerializer library?
You can look at Rick Stahl's blog on this:
http://www.west-wind.com/weblog/posts/737584.aspx
Or you could also do something like create a method that will pull out data from the datareader and place it in a list of objects. (See code below). These list of object will be serialized using the JavaScriptSerializer library.
Hope this helps!
public class User
{
public int UserId { get; set; }
public string UserName { get; set;}
}
public class DataLayer
{
public string GetUsers(string connString)
{
string result = null;
List<User> users = null;
// get data using SqlReader
using(var conn = new SqlConnection(connString))
{
using(var cmd = new SqlCommand{ Connection = conn, CommandText = "SELECT * FROM Users", CommandType = CommandType.Text })
{
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if(!reader.HasRows)
return null;
//convert data reader to a list of user objects
users = (List<User>)ConvertToList<User>(ref reader);
conn.Close();
}
}
//convert list of objects in list to json objects
var jsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
result = jsonSerializer.Serialize(users);
return result;
}
public static IList<T> ConvertToList<T>(ref SqlDataReader reader)
{
IList<T> result = null;
if (reader.IsClosed)
return result;
result = new List<T>();
T item = default(T);
while (reader.Read())
{
//create item instance
item = (T)Activator.CreateInstance<T>();
//get class property members
var propertyItems = item.GetType().GetProperties();
//populate class property members with data from data reader
for (int ctr = 0; ctr < reader.FieldCount; ctr++)
{
if(reader.GetName(ctr) == propertyItems[ctr].Name)
propertyItems[ctr].SetValue(item, UtilsHelper.GetValue<string>(reader[ctr]), null);
}
//add item to list
result.Add(item);
}
reader.Close();
reader.Dispose();
return result;
}
}
http://www.dsebd.org/latest_PE_all2_08.php
i work on asp.net C# web.Above url contain some information ,i need to save them in my database and also need to save then in specified location as xml format.This url contain a table.I want to get this table value but how to retrieve value from this html table.
HtmlWeb htmlWeb = new HtmlWeb();
// Creates an HtmlDocument object from an URL
HtmlAgilityPack.HtmlDocument document = htmlWeb.Load("http://www.dsebd.org/latest_PE_all2_08.php");
I need help to get table information from this document .How to save this table value.Show me some syntax
public partial class WebForm3 : System.Web.UI.Page
{
private byte[] aRequestHTML;
private string myString = null;
protected System.Web.UI.WebControls.Label Label1;
private ArrayList a = new ArrayList();
protected void Page_Load(object sender, EventArgs e)
{
WebClient objWebClient = new WebClient();
aRequestHTML = objWebClient.DownloadData("http://www.dsebd.org/latest_PE_all2_08.php");
// creates UTf8 encoding object
UTF8Encoding utf8 = new UTF8Encoding();
// gets the UTF8 encoding of all the html we got in aRequestHTML
myString = utf8.GetString(aRequestHTML);
string html = #"
<html><head></head>
<body><div>
<table border=1>
<tr><td>sno</td><td>sname</td></tr>
<tr><td>111</td><td>abcde</td></tr>
<tr><td>213</td><td>ejkll</td></tr>
</table>
<table border=1>
<tr><td>adress</td><td>phoneno</td><td>note</td></tr>
<tr><td>asdlkj</td><td>121510</td><td>none</td></tr>
<tr><td>asdlkj</td><td>214545</td><td>none</td></tr>
</table>
</div></body>
</html>";
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(myString);
DataTable addressAndPhones;
foreach (var table in ParseAllTables(htmlDoc))
{
if (table.Columns.Contains("Trading Code") && table.Columns.Contains("Close Price"))
{
// You found the address and phone number table
addressAndPhones = table;
}
}
}
private static DataTable[] ParseAllTables(HtmlDocument doc)
{
var result = new List<DataTable>();
foreach (var table in doc.DocumentNode.Descendants("table"))
{
result.Add(ParseTable(table));
}
return result.ToArray();
}
private static DataTable ParseTable(HtmlNode table)
{
var result = new DataTable();
var rows = table.Descendants("tr");
var header = rows.Take(1).First();
foreach (var column in header.Descendants("td"))
{
result.Columns.Add(new DataColumn(column.InnerText, typeof(string)));
}
foreach (var row in rows.Skip(1))
{
var data = new List<string>();
foreach (var column in row.Descendants("td"))
{
data.Add(column.InnerText);
}
result.Rows.Add(data.ToArray());
}
return result;
}
}
In this way u can easily create a DataTable and then can save is DataBase.
I've decided to start another thread based on the responses I got in this thread:
Asp.Net: Returning a Reader from a Class
I was returning a reader, but members have suggested I'd be better off returning a Dataset instead and also try to seperate the data access tier from the presentation tier.
This is what I have so far:
//my class methods
public DataSet GetSuppliers()
{
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("con_spSuppliersList", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", HttpContext.Current.Request.QueryString["p"]);
return FillDataSet(cmd, "SuppliersList");
}
//my FillDataSet method
private DataSet FillDataSet(SqlCommand cmd, string tableName)
{
SqlConnection conn = new SqlConnection(connectionString);
cmd.Connection = conn;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds, tableName);
}
finally
{
conn.Close();
}
return ds;
}
// on my ascx page I call the method like so:
protected void Page_Load(object sender, EventArgs e)
{
//instantiate our class
MyClass DB = new MyClass();
// grab the table of data
DataTable dt = DB.GetSuppliers().Tables["SuppliersList"];
//loop through the results
foreach (DataRow row in dt.Rows)
{
this.supplierslist.InnerHtml += Server.HtmlEncode(row["Address"].ToString()) + "<br/>";
this.supplierslist.InnerHtml += "<b>Tel: </b>" + Server.HtmlEncode(row["Telephone"].ToString()) + "<p/>";
}
}
}
Would anyone like to suggest improvements?
Is my loop 'data tier' or 'presentation tier', should the loop be inside the class and I just return a formatted string instaed of a dataset?
Thanks for all the great advice
I also would use a Typed DataSet or create your own class that holds the properties so you are not dealing with strings like row["Address"] you would say object.Address and get compile time checking.
DataSets have a lot of built in functionality that is nice but also caries with it a lot of overhead that might not be needed in something simple. Creating a simple class with properties and passing that out of your data access layer is probably the simplest way to implement what you want.
Something like this on the DAL (Data Access Layer) side:
//Also pass in the blogID dont have the DAL get the value from the UI layer..
//make the UI layer pass it in.
public IList<Supplier> GetSuppliers(string connectionString, int blogID)
{
IList<Supplier> suppliers = new List<Supplier>();
//wrap with the using statements
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("con_spSuppliersList", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", blogID);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
suppliers.Add(new Supplier
{
Address = reader.GetString(0),
Telephone = reader.GetString(1)
});
}
}
}
return suppliers;
}
}
public class Supplier
{
//I would have Address an object....but you have as string
//public Address Address { get; set; }
public string Address { get; set; }
public string Telephone { get; set; }
}
//Example if you went with Address class...
public class Address
{
//Whatever you want in the address
public string StreetName { get; set; }
public string Country { get; set; }
public string Region { get; set; }
public string City { get; set; }
}
One thing you should get in the habit of doing is calling Dispose() on your SqlConnection. The best pattern to do this is to use the using statement, which will automatically dispose of it for you. It looks like this:
private DataSet FillDataSet(SqlCommand cmd, string tableName)
{
using(SqlConnection conn = new SqlConnection(connectionString))
{
cmd.Connection = conn;
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
try
{
conn.Open();
adapter.Fill(ds, tableName);
}
finally
{
conn.Close();
}
return ds;
}
}
What you have in the foreach loop in Page_Load, is presentation logic (layout), and IMO this should not be in the code-behind of your page, but in the markup.
I'd suggest that instead of using a foreach loop to construct the HTML output, you should use a databound control (such as a asp:Repeater, DataList or GridView). Then you can bind the repeater to your dataset or datatable and have all the markup where it belongs (in the ASCX file). See this page for an example.
As a general note: you can find lots of tutorials on www.asp.net, e.g. about data access: http://www.asp.net/%28S%28pdfrohu0ajmwt445fanvj2r3%29%29/learn/data-access/