Up until now, I had a utility class that contained a function called "getTable", which took a string query and returned a datatable. Now I'm trying to get these queries to be returned via excel. Simply copying the contents of the datatable to excel works fine - however there's no header row on the spreadsheet to explain what each column is.
Below is my attempt:
public static DataTable getTable(String sql, bool includeHeaderRow)
{
DataTable table = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(sql, getConn()))
{
adpt.Fill(table);
if (includeHeaderRow)
{
DataRow headerRow = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
headerRow[i] = table.Columns[i].ColumnName;
}
table.Rows.InsertAt(headerRow, 0);
}
}
return table;
}
The above almost works, but an exception is thrown when I try and write a column name (obviously a string) into a non-string column of the datatable.
Any ideas for an easy way to achieve this?
Of course the code may give error, because as you told you are assigning none compatible datatypes into one column with specific datatype, columns at Datatable accepts specific datatypes now if you try to change the column datatype to object by creating a copy of table(cloning it) I think the problem will fix:
public static DataTable getTable(String sql, bool includeHeaderRow)
{
DataTable table = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(sql, getConn()))
{
adpt.Fill(table);
if (includeHeaderRow)
{
DataTable dt = table.Clone();
for (int i = 0; i < table.Columns.Count; i++)
{
dt.Columns[i].DataType = typeof(Object);
}
foreach (DataRow row in table.Rows)
{
dt.ImportRow(row);
}
DataRow headerRow = dt.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
headerRow[i] = table.Columns[i].ColumnName;
}
dt.Rows.InsertAt(headerRow, 0);
}
}
return dt;
}
Related
I have a DataTable called dt which is filled with values from excel.
On line 6 (row) i have my header information
Code i have now
string connString = #"Provider=Microsoft.ACE.OLEDB.16.0;Data Source=C:\Users\thoje\Desktop\stack\forslag.xlsx;" +
#"Extended Properties=""Excel 12.0;HDR=No;""";
DataTable dt = new DataTable();
string sql = #"SELECT * from [Ark1$]";
using (OleDbConnection conn = new OleDbConnection(connString))
{
conn.Open();
using (OleDbCommand cmd = new OleDbCommand(sql, conn))
{
using (OleDbDataReader rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
}
}
}
DataTable dt2 = dt.Clone();
//foreach (DataRow dr in dt.Rows)
//{
for (int i = 5; i <= dt.Rows.Count-1; i++)
{
if (i == 6)
{
DataRow dr = dt2.NewRow();
dr.ItemArray = dt.Rows[i].ItemArray;
dt2.Rows.Add(dr);
}
else if(i >=8)
{
DataRow dr = dt2.NewRow();
dr.ItemArray = dt.Rows[i].ItemArray;
dt2.Rows.Add(dr);
}
}
What i need is:
I want all my data on row 6 to become headers in a new DataTable
All data after line 7 i need to append to this new Datatable with the new headers.
What my code shows:
My code now shows how to do it on a DataTable which is cloned, so therefore i have the wrong headernames.
You don't need to iterate through all rows if you only need 1 of them.
DataTable dtSource = new DataTable();
DataTable dtTarget = new DataTable();
// Get row at index == 6 (7th line as this is a 0-based-index)
var headerRow = dtSource.Rows[6];
// Iterate through all values
foreach(var value in headerRow.ItemArray)
{
// For each value create a column
// and add it to your new dataTable
DataColumn dc = new DataColumn(value.ToString());
dc.Caption = value.ToString();
dtTarget.Columns.Add(dc);
}
using a linq approach might be good:
a pseudo way:
//linq query to select row
var query = from myRow in
myDataTable.AsEnumerable() where myRow.Field<int>("RowNo") == 6
select myRow;
// Create a table from the query DataTable newTable =
query.CopyToDataTable<DataRow>();
Your question is pretty confusing to read, and keeps talking about rows where I'd expect columns etc, but I'll make the following assumptions:
You have an excel file with 5 blank rows and then a row of headers, and then data rows
You've successfully imported this data into a Datatable called dt
You want to end up with a datatable that has the values in row 6 as the column names, and the blank rows removed
Code:
for(int i = 5; i >= 0; i--){
if(i == 5)
{
for(int j = 0; j < dt.Columns.Count; j++)
dt.Columns[j].ColumnName = dt.Rows[i][j].ToString();
}
dt.Rows.RemoveAt(i);
}
You don't need to clone the datatable. Watch out for strings that make for poor column names (containing chars like '[' ) making your life miserable further down the line ..
I have been trying to convert GRIDVIEW to DataTable but it doesn't give me data, it only gives me HEADERS of GridView but not the data inside it. Why ? I am using template fields in gridview
Calling it from event:
DataTable dt = GetDataTable(GridView DenominationsWiseTransactions);
Conversion function:
DataTable GetDataTable(GridView dtg)
{
DataTable dt = new DataTable();
// add the columns to the datatable
if (dtg.HeaderRow != null)
{
for (int i = 0; i < dtg.HeaderRow.Cells.Count; i++)
{
dt.Columns.Add(dtg.HeaderRow.Cells[i].Text);
}
}
// add each of the data rows to the table
foreach (GridViewRow row in dtg.Rows)
{
DataRow dr;
dr = dt.NewRow();
for (int i = 0; i < row.Cells.Count; i++)
{
dr[i] = row.Cells[i].Text.Replace(" ", "");
}
dt.Rows.Add(dr);
}
return dt;
}
So, when you are looping through the rows in the datagrid, it will include headers, the rows and the footer.
To see just the data rows you need to do something like:
if (e.Row.RowType == DataControlRowType.DataRow)
{ //convert that row }
Your conversion line might need some work, but at least you'll only need to concern yourself with datarows now.
I have a application where I save data from a datagridView with 3 columns to the xml file the application has save and load buttions where when save is pressed it saves to the xml file problem is it does not load the changes made in the datagridview... Only loads the headers for it.
DATAGRIDVIEW COLUMNS : (column 1= text, column 2 =checkbox , column 3 = dropdown w) Note: The dropdown has two values... so its obvoiusly 0 and 1
Code for save :
public void Save(DataGridView dgv)
{
DataTable dt = new DataTable();
for (int i = 1; i < dgv.Columns.Count + 1; i++)
{
DataColumn column = new DataColumn(dgv.Columns[i - 1].HeaderText);
dt.Columns.Add(column);
}
int ColumnCount = dgv.Columns.Count;
foreach (DataGridViewRow dr in dgv.Rows)
{
DataRow dataRow = dt.NewRow();
for (int i = 0; i < ColumnCount; i++)
{
dataRow[i] = dr.Cells[i];
}
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
XmlTextWriter newXml = new XmlTextWriter(#"c:/older/DGVXML.xml", Encoding.UTF8);
ds.WriteXmlSchema(newXml);
}
So far this is all I have for the load:
public void Load(DataGridView dgv)
{
XmlReader xmlFile = XmlReader.Create(#"c:/older/DGVXML.xml", new XmlReaderSettings());
DataSet dataSet = new DataSet();
dataSet.ReadXml(xmlFile,XmlReadMode.ReadSchema);
//LOADS!!! YAY, but only the headers with no data... check save?
dgv.DataSource = dataSet.Tables[0];
xmlFile.Close();
}
Problem 1
You have to call dt.Rows.Add(dataRow) to add row.
http://msdn.microsoft.com/en-us/library/5ycd1034.aspx
Problem 2
DataSet.WriteXmlSchema method doesn't output current data, only structure.
http://msdn.microsoft.com/en-us/library/kas5y1ky(v=vs.110).aspx
Use DataSet.WriteXml method instead.
http://msdn.microsoft.com/en-us/library/ws09ehyc(v=vs.110).aspx
Code being used is as follows.. made changes to it (edited by question asker)
DataTable dt = new DataTable();
for (int i = 1; i < dgv.Columns.Count + 1; i++)
{
DataColumn column = new DataColumn(dgv.Columns[i - 1].HeaderText);
dt.Columns.Add(column);
}
int columnCount = dgv.Columns.Count;
foreach (DataGridViewRow dr in dgv.Rows)
{
DataRow dataRow = dt.NewRow();
for (int i = 0; i < columnCount; i++)
{
//returns checkboxes and dropdowns as string with .value..... nearly got it
dataRow[i] = dr.Cells[i].Value;
}
dt.Rows.Add(dataRow);
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
XmlTextWriter xmlSave = new XmlTextWriter(#"c:/older/DGVXML.xml", Encoding.UTF8);
ds.WriteXml(xmlSave);
xmlSave.Close();
You first mistake was to choose ListDictionary as a holder for data. Second, to serialize data as array of string.
You could have a small type presenter class to hold your data (or use Tuple).
public class MyParameter
{
public string Name {get; set;}
public bool Enabled {get; set;} // notice type
public SomeType Direction {get; set;} // not sure in type
}
Then you can easily serialize rows as a list or array
var list = dgv.Rows.Select(o => new MyParameter()
{
Name = o.Cells["name"],
Enabled = o.Cells["ENABLED"],
Direction = o.Cells["DIRECTION"]
}).ToList();
And restoring data will looks as simple as
foreach(var parameter in app.Properties.Settings.Default.DeviceSettings)
{
dvg.Rows.Add(parameter.Name, parameter.Enabled, parameter.Direction);
}
Code may not compile (from head), but should give an idea.
I forgot to mention, what I never used Properties to store application settings (simply because they are crap). I rely on the fact, what you was able to store Dictionary in it, then my implementation should works as well.
Much better way to save configuration (configuration is a set of application properties) is to use, to example, XmlSerializer to save data in xml format (which is human-friendly to view/edit). The approach will still be the same to store list of settings: get them as a list, serialize (or if they are just a part of other configuration settings, assign to property and serialize whole configuration).
Scenario: App contains DataGridViews, I am populating the DataGridViews from a Database.
All the data in the Database is encrypted so after I fill my DataTable I need to cycle
through every entry in the DataTable through the decryption methods and place back
in the same spot in the DataTable. How would I do such a task?
Or is there a way I can decrypt the data as it is entering the dataTable?
SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(query, conn);
SQLiteCommandBuilder commandBuilder = new SQLiteCommandBuilder(dataAdapter);
DataTable dataTable = new DataTable();
dataTable.Locale = System.Globalization.CultureInfo.Invaria…
dataAdapter.Fill(dataTable);
//Decrypt cells
int i;
foreach (DataRow row in dataTable.Rows)
{
i = 0;
foreach (var item in row.ItemArray)
{
//This doesn't work
row.ItemArray[i] = Crypto.Decrypt(item.ToString());
i++;
}
}
return dataTable;
for (int i = 0; i < dataTable.Rows.Count; i++)
{
for (int j = 0; j < dataTable.Columns.Count; j++)
{
dataTable.Rows[i][j] = Crypto.Decrypt(dataTable.Rows[i][j].ToString());
}
}
I wrote a method for splitting a DataTable into multiple small data tables; however I am getting exception. How do I correct it? Please share the code.
Exception message:
This row already belongs to another table.
Framework: .Net 3.0
private static List<DataTable> SplitDataTable(DataTable dt, int size)
{
List<DataTable> split = new List<DataTable>();
DataTable current = dt.Clone();
int iterator = 0;
foreach (DataRow dr in dt.Rows)
{
iterator = iterator + 1;
if (iterator == size)
{
current = dt.Clone();
split.Add(current);
iterator = 0;
}
current.Rows.Add(dr);
//Exception: This row already belongs to another table.
}
return split;
}
Client:
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("TEST", typeof(int));
dt.Columns.Add("VAL", typeof(string));
dt.Rows.Add(0,"a");
dt.Rows.Add(1,"b");
dt.Rows.Add(2,"c");
dt.Rows.Add(3,"d");
List<DataTable> split = SplitDataTable(dt, 2);
}
Use dt.Copy(); instead of dt.Clone();
Before you add a DataRow to your cloned datatable, you need to remove it from the original source datatable:
foreach (DataRow dr in dt.Rows)
{
iterator = iterator + 1;
if (iterator == size)
{
current = dt.Clone();
split.Add(current);
iterator = 0;
}
dt.Rows.Remove(dr); // remove it from the source FIRST, then add it to the cloned DataTable
current.Rows.Add(dr);
}
Use current.ImportRow(dr); instead of current.Rows.Add(dr);
You can either remove the DataRow from the source DataTable or create a new DataRow and add it to the new DataTable.
You just need to change the line where you add the data row to current data table. Use the overload which takes an object array to create a new row. This way you are not cloning or copying any rows, instead creating a new row.
current.Rows.Add(dr.ItemArray);
I think this function will not work properly try this
I make some modification on you code it's working fine
private static List<DataTable> SplitDataTable(DataTable dt, int size)
{
List<DataTable> split = new List<DataTable>();
DataTable current = dt.Clone();
int iterator1 = 0;
foreach (DataRow dr in dt.Rows)
{
if (current.Rows.Count < size)
{
current.Rows.Add(dr.ItemArray);
}
if (current.Rows.Count == size)
{
iterator1= iterator1+size;
split.Add(current);
current = dt.Clone();
}
}
if (iterator1 < dt.Rows.Count) { split.Add(current); }
return split;
}
happy codding