I have a DataTable that is populated with only strings at the moment.
I want to get from the DataTable Columns the DataType and insert the DataType.
DataTable example, all row names can be random.
And I want to have from the example Column "age" as int, and the rest still string.
At the moment the Age is a string, can I try to Parse the whole column? Or would this be a bad solution.
Is there a simple way to do this?
You can not change the data type once the table is loaded. Clone the current DataTable from the original table, find the age column, change data type from string to int then import rows.
Important: The above assumes that, in this case the age column can represent an int on each row, if not you need to perform proper assertion before using ImportRow.
Here is a conceptual example
private static void ChangeColumnType()
{
DataTable table = new DataTable();
table.Columns.Add("Seq", typeof(string));
table.Columns.Add("age", typeof(string));
table.Columns.Add("name", typeof(string));
table.Rows.Add("1", "22", "Smith");
table.Rows.Add("2", "46", "Jones");
DataTable cloned = table.Clone();
bool found = false;
for (int index = 0; index < table.Columns.Count; index++)
{
if (string.Equals(table.Columns[index].ColumnName, "age",
StringComparison.CurrentCultureIgnoreCase))
{
cloned.Columns["age"]!.DataType = typeof(int);
found = true;
}
}
if (!found) return;
foreach (DataRow row in table.Rows)
{
cloned.ImportRow(row);
}
foreach (DataColumn column in cloned.Columns)
{
Console.WriteLine($"{column.ColumnName}\t{column.DataType}");
}
}
Edit: One possible way to avoid issues when age can not be converted to an int.
if (!found) return;
foreach (DataRow row in table.Rows)
{
if (int.TryParse(row.Field<string>("age"), out _))
{
cloned.ImportRow(row);
}
else
{
Console.WriteLine($"Failed: {string.Join(",", row.ItemArray)}");
}
}
Sorry if I do not understand your question but I will try to answer what I think you're asking below:
If you're just trying to determine the data type then I would suggest taking the first value in said column (as you don't need to check them all obviously.) And do a tryparse to determine if it is compatible with int for example.
If you used getType it would likely return a String.
If you're trying to SET the whole column as a data type then you should probably be doing this at the stage you're generating the table via a constructor or programatically as shown in the first example
// Create second column.
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "ParentItem";
column.AutoIncrement = false;
column.Caption = "ParentItem";
column.ReadOnly = false;
column.Unique = false;
// Add the column to the table.
table.Columns.Add(column);
Full Example from Microsoft
Related
I have the following datagridview.
I need to filter and save rows separately that match Valid and Invalid property of the status column.
It do not have a datasource.So i'm creating a DataTable,filtering it and saving the results.But the filtering is not working as intended and contains results that does not match the expression
DataTable dt = new DataTable();
//Populating Virtual Table
foreach (DataGridViewColumn col in dataGridView4.Columns)
{
dt.Columns.Add(col.Name);
}
foreach (DataGridViewRow row in dataGridView4.Rows)
{
DataRow dRow = dt.NewRow();
foreach (DataGridViewCell cell in row.Cells)
{
dRow[cell.ColumnIndex] = cell.Value;
}
dt.Rows.Add(dRow);
}
Now creating a Filtered Table containing results where column named Status equals "Valid"
filtered = dt.Copy();
DataTable filteredResults = new DataTable();
DataTable filteredResults2 = new DataTable();
// filtered.Columns.Remove("Status");
var expression = string.Format("Status LIKE '%{0}%'", "Valid");
if (filtered.Select(expression).Any())
{
filteredResults = filtered.Select(expression).CopyToDataTable();
}
But the filtered data table contains elements which has the Status column value "Invalid".What i'm i doing wrong ? Please advice.
You are using LIKE in your expression. LIKE checks to see if the value contains the specified value. Use = instead.
I'm new to this but I'm using C# and am trying to make a Windows forms application where I can display only specific rows from a data table. I'm using a disconnected application (loading the SQL database into a dataset) and I am trying to show only rows where the value of a specific column is equal to a number that is chosen by the user.
What is the best way to display this information?
How do I only show those specific rows?
Create BindingSource
, bind to your DataTable and use Filter property
You can use DataTable Select method.
DataTable.Select Method (String)
Gets an array of all DataRow objects that match the filter criteria.
DataTable.Select Method (String, String)
Gets an array of all DataRow objects that match the filter criteria,
in the specified sort order.
DataTable.Select Method (String, String, DataViewRowState)
Gets an array of all DataRow objects that match the filter in the
order of the sort that match the specified state.
Or you can create a DataView (msdn).
Examples of using those method you can find under the links that I gave you above.
Binding to ListBox:
DataTable.Select:
DataTable myTable = new DataTable();
myTable.Columns.Add("Id", typeof(int));
myTable.Columns.Add("Name", typeof(string));
DataRow myNewRow = null;
for (int i = 0; i < 10; i++)
{
myNewRow = myTable.NewRow();
myNewRow["Id"] = i;
myNewRow["Name"] = "Name " + i.ToString();
myTable.Rows.Add(myNewRow);
}
DataRow[] Array = myTable.Select("Id > 5");
DataTable newTable = myTable.Clone();
foreach (var item in Array)
{
newTable.ImportRow(item);
}
listBox1.DataSource = newTable;
listBox1.ValueMember = "Id";
listBox1.DisplayMember = "Name";
DataView:
DataView custDV = new DataView(myTable,
"Id > 5",
"Name",
DataViewRowState.CurrentRows);
listBox1.DataSource = custDV;
listBox1.ValueMember = "Id";
listBox1.DisplayMember = "Name";
Hi. I have a DataTable one of the column is set to AutoIncrement is true. Basically I am adding some text box values to the DataTable and then binding it to the Grid View. What I am trying to achieve is if I delete a row from the grid view the row in the DataTable is also need to be deleted and also decrement the primary key column.
DataTable is declared like this private DataTable table = new DataTable(); and code is:
DataColumn promoDetailsID = new DataColumn();
promoDetailsID.ColumnName = "promoDetailsID";
promoDetailsID.DataType = System.Type.GetType("System.Int32");
promoDetailsID.AutoIncrement = true;
promoDetailsID.AutoIncrementSeed = 1;
promoDetailsID.AutoIncrementStep = 1;
table.Columns.Add(promoDetailsID);
table.Columns.Add("StartRange", typeof(string));
table.Columns.Add("EndRange", typeof(string));
table.Columns.Add("Amount", typeof(string));
table.Columns.Add("AllocationCases", typeof(string));
table.Columns.Add("AllocationUnits", typeof(string));
if (ViewState["dtTable"] != null)
{
table = (DataTable)ViewState["dtTable"];
}
table.Rows.Add(null,TxtStartRange.Text.Trim(), TxtEndRange.Text.Trim(), TxtAllocationAmount.Text.Trim(), TxtAllocationCases.Text.Trim(), TxtAllocationUnits.Text.Trim());
grdPromotions.DataSource = table;
grdPromotions.DataBind();
ViewState["dtTable"] = table;
This is the code when I am trying to delete row from grid.
protected void grdPromotions_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
if (ViewState["dtTable"] != null)
{
table = (DataTable)ViewState["dtTable"];
int rowIndex = Convert.ToInt32(e.RowIndex);
table.Rows[e.RowIndex].Delete();
}
table.AcceptChanges();
grdPromotions.DataSource = table;
grdPromotions.DataBind();
ViewState["dtTable"] = table;
}
There is no error I am getting but the DataTable is not updating after delete.
Since you don't use a real database it makes no sense to use DataRow.Delete which just sets it's RowState to Deleted. What you want to do is to remove the row from the DataTable.
table.Rows.RemoveAt(e.RowIndex);
If you also want to decrement the primary key column, you have to make the column writable:
table.Columns[0].ReadOnly = false;
Then you need to update the value manually:
int counter = 0;
foreach(DataRow row in table.Rows)
{
row[0] = ++counter;
}
table.Columns[0].ReadOnly = true;
Side-note: don't store a DataTable in ViewState, if you need to persist it between postbacks use the Session instead. Session lives in memory whereas ViewState will be serialized and stored in the rendered html, so it will also be transferred to the client.
I may well be looking at this problem backwards but I am curious none the less. Is there a way to build a DataTable from what is currently displayed in the DataGridView?
To be clear, I know you can do this DataTable data = (DataTable)(dgvMyMembers.DataSource); however that includes hidden columns. I would like to build it from the displayed columns only.
Hope that makes sense.
So I ended up trying a combination of a couple of answers as that seemed best. Below is what I am trying. Basically I am creating the DataTable from the DataSource and then working backwards based on if a column is visible or not. However, after it removes a column I get a Collection was modified; enumeration operation may not execute on the next iteration of the foreach.
I am confused as I am not trying to modify the DataGridView, only the DataTable so what's up?
DataTable data = GetDataTableFromDGV(dgvMyMembers);
private DataTable GetDataTableFromDGV(DataGridView dgv)
{
var dt = ((DataTable)dgv.DataSource).Copy();
foreach (DataGridViewColumn column in dgv.Columns)
{
if (!column.Visible)
{
dt.Columns.Remove(column.Name);
}
}
return dt;
}
Well, you can do
DataTable data = (DataTable)(dgvMyMembers.DataSource);
and then use
data.Columns.Remove(...);
I think it's the fastest way. This will modify data source table, if you don't want it, then copy of table is reqired. Also be aware that DataGridView.DataSource is not necessarily of DataTable type.
I don't know anything provided by the Framework (beyond what you want to avoid) that would do what you want but (as I suspect you know) it would be pretty easy to create something simple yourself:
private DataTable GetDataTableFromDGV(DataGridView dgv) {
var dt = new DataTable();
foreach (DataGridViewColumn column in dgv.Columns) {
if (column.Visible) {
// You could potentially name the column based on the DGV column name (beware of dupes)
// or assign a type based on the data type of the data bound to this DGV column.
dt.Columns.Add();
}
}
object[] cellValues = new object[dgv.Columns.Count];
foreach (DataGridViewRow row in dgv.Rows) {
for (int i = 0; i < row.Cells.Count; i++) {
cellValues[i] = row.Cells[i].Value;
}
dt.Rows.Add(cellValues);
}
return dt;
}
one of best solution enjoyed it ;)
public DataTable GetContentAsDataTable(bool IgnoreHideColumns=false)
{
try
{
if (dgv.ColumnCount == 0) return null;
DataTable dtSource = new DataTable();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (IgnoreHideColumns & !col.Visible) continue;
if (col.Name == string.Empty) continue;
dtSource.Columns.Add(col.Name, col.ValueType);
dtSource.Columns[col.Name].Caption = col.HeaderText;
}
if (dtSource.Columns.Count == 0) return null;
foreach (DataGridViewRow row in dgv.Rows)
{
DataRow drNewRow = dtSource.NewRow();
foreach (DataColumn col in dtSource .Columns)
{
drNewRow[col.ColumnName] = row.Cells[col.ColumnName].Value;
}
dtSource.Rows.Add(drNewRow);
}
return dtSource;
}
catch { return null; }
}
First convert you datagridview's data to List, then convert List to DataTable
public static DataTable ToDataTable<T>( this List<T> list) where T : class {
Type type = typeof(T);
var ps = type.GetProperties ( );
var cols = from p in ps
select new DataColumn ( p.Name , p.PropertyType );
DataTable dt = new DataTable();
dt.Columns.AddRange(cols.ToArray());
list.ForEach ( (l) => {
List<object> objs = new List<object>();
objs.AddRange ( ps.Select ( p => p.GetValue ( l , null ) ) );
dt.Rows.Add ( objs.ToArray ( ) );
} );
return dt;
}
How to add identity column to datatable using c#. Im using Sql compact server.
You could try something like this maybe?
private void AddAutoIncrementColumn()
{
DataColumn column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.AutoIncrement = true;
column.AutoIncrementSeed = 1000;
column.AutoIncrementStep = 10;
// Add the column to a new DataTable.
DataTable table = new DataTable("table");
table.Columns.Add(column);
}
DataTable table = new DataTable("table");
DataColumn dc= table.Columns.Add("id", typeof(int));
dc.AutoIncrement=true;
dc.AutoIncrementSeed = 1;
dc.AutoIncrementStep = 1;
// Add the new column name in DataTable
table.Columns.Add("name",typeof(string));
table.Rows.Add(null, "A");
table.Rows.Add(null, "B");
table.Rows.Add(null, "C");
If the DataTable is already populated. you can use below method
void AddAndPopulateDataTableRowID(DataTable dt, string col, bool isGUID)
{
if(isGUID)
dt.Columns.Add(col, typeof(System.Guid));
else
dt.Columns.Add(col, typeof(System.Int32));
int rowid = 1;
foreach (DataRow dr in dt.Rows)
{
if (isGUID)
dr[col] = Guid.NewGuid();
else
dr[col] = rowid++;
}
}
You don't do autoincrement on DataTable (or front-end for that matter), unless you want to make your application a single user application only.
If you need the autoincrement, just do it in database, then retrieve the autoincremented id produced from database to your front-end.
See my answer here, just change the SqliteDataAdapter to SqlDataAdapter, SqliteConnection to SqlConnection, etc : anyway see why I get this "Concurrency Violation" in these few lines of code??? Concurrency violation: the UpdateCommand affected 0 of the expected 1 records
Just my two cents. Auto-increment is useful in a Winform app (stand alone as Michael Buen rightly said), i.e.:
DatagridView is being used to display data that does not have a "key field", the same can be used for enumeration.
I dont think its a good idea to use autoincrement on datatable if you are using insert and delete to a datatable because the number will not be rearranget, no final i will share a small idea how can we use autoincrement manual.
DataTable dt = new DataTable();
dt.Columns.Add("ID",typeof(int));
dt.Columns.Add("Produto Nome", typeof(string));
dt.Rows.Add(null, "A");
dt.Rows.Add(null, "B");
dt.Rows.Add(null, "C");
for(int i=0;i < dt.Rows.Count;i++)
{
dt.Rows[i]["ID"] = i + 1;
}
always when finalizing the insert or delete must run this loop
for(int i=0;i < dt.Rows.Count;i++)
{
dt.Rows[i]["ID"] = i + 1;
}