Set value of each DataTable row in specific column - c#

I'm having some problem while trying to set column value.
I'v had a dataTable which get some values from SQL and then im adding two new columns by :
dataTable.Columns.Add("dest", typeof(int));
dataTable.Columns.Add("amount", typeof(int));
Which works great but now i want to put 0 in every row in column name dest - and later user will edit this, and then i want to set amount value as
amount = all(this column is in dataTable before I add these 2 columns) + dest;

int columnNumber = 5; //Put your column X number here
for (int i = 0; i < yourDataTable.Rows.Count; i++)
{
yourDataTable.Rows[i][columnNumber] = "0";
}

You can use foreach too.
foreach (DataRow row in myDataTable.Rows)
//if (row["X"] has condition) // or if any condition
row["colName"] = row[colIndex] = "abc";

Related

Merge values on multi-line columns on a Datagridview

I have Visual Studio 2019. The project is a .Net Windows Form on C# on .Net Framework 4.8.
I have a Datagridview which shows some tables data from different databases (MS SQL and Postgresql).
I merge that databases, and the result is too long, so we can't fit it on a screen; but we have to see all the data available on that screen, which are more than 40 columns. Reduce the font size is not plausible.
So, the solution proposed was to merge some values on the same column in this way (See this example):
The actual data view:
The way we need to view it:
If you have any ideas or you know an alternative to Datagridview which allows that, please share them.
Thanks in advance.
You can customize your datagridview's row and column to get the Multi-line columns on a datagridview.
I assume that the datatable is the table from database.
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
private void Form1_Load(object sender, EventArgs e)
{
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Field1");
table.Columns.Add("Field2");
table.Columns.Add("Field3");
table.Columns.Add("Field4");
table.Columns.Add("Field5");
table.Columns.Add("Field6");
table.Columns.Add("Field7");
table.Rows.Add("test1", 1, 2, 3, 4, 5, 6, 7);
table.Rows.Add("test2", 1, 2, 3, 4, 5, 6, 7);
table.Rows.Add("test3", 1, 2, 3, 4, 5, 6, 7);
dataGridView1.ColumnHeadersVisible = false;
for (int i = 0; i < table.Columns.Count/2; i++)
{
dataGridView1.Columns.Add("","");
}
string[] columnNames = table.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray();
int count = table.Columns.Count/2;
var col1 = columnNames.Take(count).ToArray();
var col2= columnNames.Skip(count).Take(count).ToArray();
dataGridView1.Rows.Add(col1);
dataGridView1.Rows.Add(col2);
object[] arr;
for (int i = 0; i < table.Rows.Count; i++)
{
arr = table.Rows[i].ItemArray;
var row1=arr.Take(count).ToArray();
var row2 = arr.Skip(count).Take(count).ToArray();
dataGridView1.Rows.Add(row1);
dataGridView1.Rows.Add(row2);
}
}
Result:
After reviewing your question, I have to say that IMHO your solution is not giving much thought to the end user or if the code has to grab one of the values. Stacking fields into a single column “creates” two issues IMHO…One, as mentioned, is that the user is going to have to do extra work and check the order of the headers to distinguish which field value is which… a subtle yet (annoying) non intuitive extra step. Two, if the user is allowed to change fields or the code needs to grad a field, then, there is going to be extra work needed to differentiated which field goes with which value. Extra work for the user and extra work for the coder doesn’t sound like a good start.
Sorry about my rant. Fortunately, if you wanted to take a table as shown in the question, and turn it into a table as you describe, then the code below should do this. It basically creates “two” (2) field columns. Such that each column contains two fields. The code is hacky yet it is not too complicated I hope. I made numerous comments in the code. Some notes would be that, since we are adding two fields for each column and (as far as I know) a DataGridView won’t allow double column headers, the code does NOT use the column headers row and instead uses the first two rows of the grid for the two column headers. This will allow you to format the two rows to look like headers and/or color code if needed.
Lastly, a better solution IMHO. As previously mentioned a pivot will work, however, there are a couple of issues given how the data is stored in the original table. In a basic pivot where we switch rows and columns, the posted example would have three (3) columns… “Jim”, “Hugh” and “Terrance”. Then the number of rows would be (one (1) + however many fields/field columns). The extra “one” is the field “LastName.” Given this, it may look something like…
Jim Hugh Terrance
LastName Carey Jackman Hill
Field1 1 a N/A
Field2 2 b N/A
……..
It would appear obvious that the “LastName” should go with the column header. Therefore, the transpose/pivot may look like…
Jim Carey Hugh Jackman Terrance Hill
Field1 1 a N/A
Field2 2 b N/A
……..
IMHO, this will be more intuitive for the user to identify fields and there should not be any extra coding if we need to reference a specific value. The picture below shows a complete example from the code below. Drop three (3) DataGridViews onto a form and paste my code. The top-left grid is the original data. The bottom-left grid is the transpose as per your requirements and finally, the grid on the right is the what I feel would work best considering your dilemma.
A note on the last grid on the right… Initially, like the column header rows in your solution, I had the fields as a column in the grid. It will not be difficult to change the code if you want this. However, the code currently adds the field names as “row headers” in the grid. Since the DataTable does not really have row headers, this addition had to be made “after” the data source was set and can be seen in the forms Load event. Again, it will not be difficult to move the field to an added column in the DataTable.
To make this example complete, below is code to create some test data. The incoming parameter totalCols will make totalCols columns in the DataTable for the "Field" values.
private DataTable GetDataFromDB(int totalCols) {
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("LastName", typeof(string));
for (int i = 1; i <= totalCols; i++) {
dt.Columns.Add("Field" + i, typeof(string));
}
DataRow curRow;
string name;
string lName;
for (int i = 1; i < 4; i++) {
switch (i) {
case 1:
name = "Jim";
lName = "Carrey";
break;
case 2:
name = "Hugh";
lName = "Jackman";
break;
default:
name = "Terence";
lName = "Hill";
break;
}
curRow = dt.NewRow();
curRow["Name"] = name;
curRow["LastName"] = lName;
if (i < 3) {
for (int j = 2; j < dt.Columns.Count; j++) {
curRow[j] = "N" + i + "F" + (j - 1);
}
}
dt.Rows.Add(curRow);
}
return dt;
}
We will use three (3) global DataTables, one for each grid.
DataTable originalDT;
DataTable pivotDT1;
DataTable pivotDT2;
The load event that sets each grid to the proper DataTable and some specific formatting for each grid.
private void Form1_Load(object sender, EventArgs e) {
originalDT = GetDataFromDB(45);
dataGridView1.DataSource = originalDT;
// pivot 1 - bottom left grid
pivotDT1 = PivotTable(originalDT);
dataGridView2.DataSource = pivotDT1;
dataGridView2.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
dataGridView2.Rows[0].DefaultCellStyle.BackColor = Color.Blue;
dataGridView2.Rows[1].DefaultCellStyle.BackColor = Color.Blue;
dataGridView2.Rows[0].DefaultCellStyle.ForeColor = Color.White;
dataGridView2.Rows[1].DefaultCellStyle.ForeColor = Color.White;
dataGridView2.Columns[0].Frozen = true;
// pivot 2 - right grid
pivotDT2 = PivotTable2(originalDT);
dataGridView3.DataSource = pivotDT2;
int dgvRow = 0;
// add column headers as row headers in the grid
for (int i = 2; i < originalDT.Columns.Count; i++) {
dataGridView3.Rows[dgvRow++].HeaderCell.Value = originalDT.Columns[i].ColumnName;
}
dataGridView3.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;
}
Finally, the two pivot/transform methods…
Using your solution and shown in the bottom-left grid…
private DataTable PivotTable(DataTable originalDT) {
DataTable pivotDT = new DataTable();
// the number of columns will be half the original number of columns
int halfCols = Math.DivRem(originalDT.Columns.Count, 2, out int rem);
// if there is a remainder then there is an odd number of columns and we need to add 1 col
if (rem > 0) {
halfCols++;
}
// add the columns to the pivot table
for (int i = 0; i < halfCols; i++) {
pivotDT.Columns.Add();
}
// the number of rows will be the number of original rows times 2
// PLUS 2 additional rows for the headers
for (int i = 0; i < (originalDT.Rows.Count * 2) + 2; i++) {
pivotDT.Rows.Add();
}
// Add the two header rows from the column names
int originalCol = 0;
for (int i = 0; i < halfCols; i++) {
pivotDT.Rows[0][i] = originalDT.Columns[originalCol++].ColumnName;
// if the original table had an odd number of columns
// then the last column only had one field
// - there would never be a column without at least one field
if (originalCol < originalDT.Columns.Count) {
pivotDT.Rows[1][i] = originalDT.Columns[originalCol++].ColumnName;
}
}
// finally add the rows from the original table.
int pivotRow = 2;
int pivotCol = 0;
int curPivotRow;
int curPivotCol;
string value;
for (int originalRow = 0; originalRow < originalDT.Rows.Count; originalRow++) {
curPivotRow = pivotRow;
curPivotCol = pivotCol;
for (originalCol = 0; originalCol < originalDT.Columns.Count; originalCol++) {
value = originalDT.Rows[originalRow][originalCol].ToString();
if (string.IsNullOrEmpty(value)) {
value = "N/A";
}
pivotDT.Rows[curPivotRow][curPivotCol] = value;
// if this is the first item then simply bump the pivot row
if (curPivotRow < pivotRow + 1) {
curPivotRow++;
}
else { // this is the second item -
// we want the curpivot row to start back at the starting pivotRow
// then move over a column for the next two columns in the original table
curPivotRow = pivotRow;
curPivotCol++;
}
}
// new row in the original data start back at column 0 in the pivot table
// and bump the row index by two since we added two rows
pivotRow += 2;
pivotCol = 0;
}
return pivotDT;
}
And my solution shown in the grid on the right.
private DataTable PivotTable2(DataTable originalDT) {
DataTable pivotDT = new DataTable();
for (int i = 0; i < originalDT.Rows.Count; i++) {
pivotDT.Columns.Add();
}
for (int i = 0; i < originalDT.Columns.Count - 2; i++) {
pivotDT.Rows.Add();
}
int pivotCol = 0;
foreach (DataRow row in originalDT.Rows) {
pivotDT.Columns[pivotCol++].ColumnName = row[0].ToString() + " " + row[1].ToString();
}
int pivotRow = 0;
pivotCol = 0;
string value;
for (int i = 0; i < originalDT.Rows.Count; i++) {
for (int j = 2; j < originalDT.Columns.Count; j++) {
value = originalDT.Rows[i][j].ToString();
if (string.IsNullOrEmpty(value)) {
value = "N/A";
}
pivotDT.Rows[pivotRow++][pivotCol] = value;
}
pivotCol++;
pivotRow = 0;
}
return pivotDT;
}
Finally, I am not that proficient using SQL, however, I am betting it is possible to create an SQL procedure that will produce my solution directly from the data base.

Remove a DataRow in a DataTable that contains no numeric values

I want to be able remove DataRows in a DataTable that contain null values for arrays containing no numeric data.
Trying to modify the code below which transposes the DataTable for use by Google Visualisation API:
private DataTable TransposeOpiate(DataTable inputTable)
{
DataTable outputTable = new DataTable();
// Add columns by looping rows
// Header row's first column is same as in inputTable
outputTable.Columns.Add(inputTable.Columns[0].ColumnName.ToString());
// Header row's second column onwards, 'inputTable's first column taken
foreach (DataRow inRow in inputTable.Rows)
{
string newColName = inRow[0].ToString();
outputTable.Columns.Add(newColName);
}
// Add rows by looping columns
for (int rCount = 1; rCount <= inputTable.Columns.Count - 1; rCount++)
{
DataRow newRow = outputTable.NewRow();
// First column is inputTable's Header row's second column
newRow[0] = inputTable.Columns[rCount].ColumnName.ToString();
for (int cCount = 0; cCount <= inputTable.Rows.Count - 1; cCount++)
{
string colValue = inputTable.Rows[cCount][rCount].ToString();
newRow[cCount + 1] = colValue;
}
outputTable.Rows.Add(newRow);
}
return outputTable;
}
For example in the intellisense output shown below the row for improved, where the values after contain no data (this would normally be numeric values) instead contains empty indexes:
I need to be able to delete these rows if they are in the above format, how can I modify my code to perform this?
Why not set a condition which prevents adding the row if all the row cells have a null value?
if (newRow.ItemArray.Any(x => x != null))
{
outputTable.Rows.Add(newRow);
}
If you need to check for columns as well, do this right before returning outputTable
for (int col = outputTable.Columns.Count - 1; col >= 0; col--)
{
bool toDelete = true;
for (int row = 0; row < outputTable.Rows.Count; row++)
{
if (outputTable.Rows[row][col] != null)
{
toDelete = false;
}
}
if (toDelete)
{
outputTable.Columns.RemoveAt(col);
}
}
You can try with something like this (comments are in code):
private void CheckColumns()
{
//table which we want to check
DataTable table = new DataTable();
//add column definition - first column will be string, other two are int columns
table.Columns.Add("string column", typeof(string));
table.Columns.Add("int column 1", typeof(int));
table.Columns.Add("int column 2", typeof(int));
//add data - in this example rows "abc" and "ghi" are valid because they have at least one numeric column
table.Rows.Add(new object[] { "abc", 1, 2 });
table.Rows.Add(new object[] { "def", null, null });
table.Rows.Add(new object[] { "ghi", null, 2 });
table.Rows.Add(new object[] { "jkl", null, null });
//filter rows in a way, using Linq, that rows are filtered where at least one column has numeric value
var validRows = table.AsEnumerable().Where(r => r.ItemArray.Any(c => IsNumeric(c))).ToList();
}
//this is helper method that code will call for each value in each row
private bool IsNumeric(object value)
{
int outputValue;
return int.TryParse(value.ToString(), out outputValue);
}
If you'll have decimal values, then you should une decimal.TyrParse in IsNumeric method.
If you need to be sure that all columns except first one have numeric values, you can do it like this (skipping first value from each row and checking if all other values are numeric)...
var validRows = table.AsEnumerable().Where(r => r.ItemArray.Skip(1).All(c => IsNumeric(c))).ToList();
once you have those 'valid' rows, you can write that data into another table or do whatever you need to do with those data...
Instead of .ToList() you can use .CopyToDataTable() method so you'll get new DataTable with those 'valid' rows. For example:
DataTable newDataTable = table.AsEnumerable().Where(r => r.ItemArray.Skip(1).All(c => IsNumeric(c))).CopyToDataTable();

Add rows to datagridview dynamically

I usually don't post questions, and I know this answer is already out there somewhere but I'm not finding it for some reason. Basically, I have a datagridview that has a variable number of columns, and variable number of rows. So when I'm building it for the screen, I first add the columns using this code:
// Set column names
for (int i = 0; i < count; i++)
{
fieldName = fromFileFields[i];
dtaRecordDisplay.Columns.Add(fromFileFields[i}, fromFileFields[i});
}
Now, I would like to populate the rows in the view with something like this:
// Set row data
for (int i = 0; i < count; i++)
{
dtaRecordDisplay.Rows.Add(Row1, Row2, etc., etc., etc..........);
}
I'm having a hard time figuring out the loop to be able to add all rows of data at once. Does anyone have any ideas on how to accomplish that?
Thanks in advance!
In this case I would fill a DataTable with columns and rows to work with, and bind that to the grid.
DataTable dt = new DataTable();
// first add your columns
for (int i = 0; i < count; i++)
{
dt.Columns.Add(fromFileFields[i]);
}
// and then add your rows
for (int i = 0; i < count; i++)
{
var row = dt.NewRow();
// Set values for columns with row[i] = xy
dt.Rows.Add(row);
}
datagridview.DataSource = dt;
You need to make a DataTable, and bind that to your grid view. Then add the rows and colums to that, like Marc says:
https://stackoverflow.com/a/35604744/5882760
The reason for that is, that the columns of the grid view instance only contain information on how to display the data from the attached DataTable.
They contain the information about what columbs to display, in what order, etc.
Try following code, you can add rows to dataGridViews without out any data Sources or data table.
dataGridView1.AllowUserToAddRows = true;
dtaGridView1 row = (DataGridViewRow)dataGridView1.Rows[0].Clone();
row.Cells[0].Value = "Your cell 0 Data";
row.Cells[1].Value = "Your cell 1 Data";
row.Cells[2].Value = "Your cell 2 Data";
………….
row.Cells[n].Value = "Your cell n Data";
dataGridView1.Rows.Add(row);

Get data based on column name

How to use Datarow to retrieve data based on column name? I am trying to loop the db data from my first looping
//Trying to get data
DataRow dr = dsResult.Tables[1].Rows[0];
//trying to get data successful
//what i trying to achieve is to retrieve data from database based on rows index and
column name
for(int i =0; i <datagridview.Rows.Count ; i++){
string a = dr['ColumnName'].['RowsIndex'].toString(); //Failed
}
for (int i = 0; i < dataTable.Rows.Count; i++)
{
DataRow dr = dataTable.Rows[i]; //Where the RowIndex
string a = dr[0].ToString(); //Where the ColumnIndex or ColumnName
}
Try ?
dr.Rows[RowsIndex]['ColumnName'].ToString()
Have you tried removing the period after the 'columnName' specification, and putting rows[i]["columnName"] rather than [columnName][rows]?

Create a Set of String with Column Header as string title and string value as row 0 value

I have column 1 column 2 and column 3. At row 0 for each of these i have Values 1,2,3 respectively.
How can I build a loop that will set
string column 1 = 1
string column 2 = 2
string column 3 = 3
so that the strings name is the column headers and the value of the string is the value in row 0 of that column.
This needs to be done for about 150+ columns and I am looking to avoid individually typing out.
Columns are in a table called "ShipmentInfo"
Try this code :
for (int index = 0; index < DT.Rows.Count; index++)
{
foreach (DataColumn DC in DT.Columns)
{
DT.Rows[index][DC] = index;
}
}
Here DT is DataTable object.
Hope this will help you.

Categories

Resources