How to simplify multiple nested foreach loops? - c#

private DataTable GetAttributeTable()
{
DataTable cltAttributeTable = new DataTable("CLT_ATTRIBUTE");
DataColumnCollection iRefColumns = cltAttributeTable.Columns;
//BETHiddenColumn is defined for hiding certain columns at the UI
//And can be used for manipulating entities internally
iRefColumns.AddRange(new[]
{
new BETHiddenColumn { ColumnName = CLDConstants.CLTGUID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.CLTNAME, DataType = typeof(string), ReadOnly = true },
new BETHiddenColumn { ColumnName = CLDConstants.SHEETID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.SHEETNAME, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_TYPE", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_VALUE", DataType = typeof(string), ReadOnly = false }
});
return cltAttributeTable;
}
public override async Task<DataTable> GetDataAsync(ControlNetworkStructure controlNetwork)
{
DataTable cltAttributeTable = GetAttributeTable();
try
{
using (var automationService = ConsumedServiceProvider.Provider.AutomationService)
{
foreach (string userDefinedLogicTemplate in selectedCLT)
{
var controlLogicClt =
automationService.AutomationClt.GetControlLogicTemplate(userDefinedLogicTemplate);
foreach (ISheet sheet in await controlLogicClt.GetSheets())
{
foreach (IFunctionCode functionCode in await sheet.GetFunctionCodes())
{
foreach (IHarmonyAttribute functionCodeAttribute in functionCode.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "FUNCTION CODE";
row["OBJECT_NAME"] = functionCode.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IInputReference inputReference in await sheet.GetInputReferences())
{
foreach (IHarmonyAttribute functionCodeAttribute in inputReference.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "IREF";
row["OBJECT_NAME"] = inputReference.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IOutputReference outputReference in await sheet.GetOutputReferences())
{
foreach (IHarmonyAttribute functionCodeAttribute in outputReference.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "OREF";
row["OBJECT_NAME"] = outputReference.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IText text in await sheet.GetTexts())
{
foreach (IHarmonyAttribute functionCodeAttribute in text.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "TEXT";
row["OBJECT_NAME"] = text.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
}
}
}
}
catch (Exception exception)
{
LogService.LogException(this, ServiceResources.CONTEXT_CLD_EDITOR, "CLT Attribute",
exception);
}
finally
{
// Accepting all the modification to the table before leaving this method call
cltAttributeTable.AcceptChanges();
}
return cltAttributeTable;
}
Description
I have a method with multiple foreach loops inside and I have hard time understanding it as I'm beginner to C#. Also I read its not a good practice to write multiple foreach loops in program.
The method returns a data table and it is bound to a Datagrid.
Can anyone help me simplify it so that it becomes better readable and more intuitive?

if the Attribute property of the IFunctionCode, IInputReference, etc is based on a common interface, you could do something like this:
List<ICommomInterface> items = new List<ICommomInterface>();
items.AddRange(await sheet.GetFunctionCodes());
items.AddRange(await sheet.GetInputReferences());
items.AddRange(await sheet.GetOutputReferences());
items.AddRange(await sheet.GetTexts());
items.foreach(item =>
{
item.Attributes.ForEach(attrib =>
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
if(item is IFunctionCode){
row["OBJECT_TYPE"] = "FUNCTION CODE";
} else if(_other types_)
{
}
row["OBJECT_NAME"] = item.Name;
row["ATTRIBUTE_NAME"] = attrib.Type;
row["ATTRIBUTE_VALUE"] = attrib.Value;
cltAttributeTable.Rows.Add(row);
});
});
If that's your intention.
If they do not share a common base interface you have to use object class for the list and extend the if(item is IFunctionCode){... check.

Ok, this is pretty hastily done but I think is a good start. I didn't have time to test it so it probably has some bugs. Also, there's plenty of room for improvements.
private DataTable GetAttributeTable()
{
DataTable cltAttributeTable = new DataTable("CLT_ATTRIBUTE");
DataColumnCollection iRefColumns = cltAttributeTable.Columns;
//BETHiddenColumn is defined for hiding certain columns at the UI
//And can be used for manipulating entities internally
iRefColumns.AddRange(new[]
{
new BETHiddenColumn { ColumnName = CLDConstants.CLTGUID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.CLTNAME, DataType = typeof(string), ReadOnly = true },
new BETHiddenColumn { ColumnName = CLDConstants.SHEETID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.SHEETNAME, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_TYPE", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_VALUE", DataType = typeof(string), ReadOnly = false }
});
return cltAttributeTable;
}
public override async Task<DataTable> GetDataAsync(ControlNetworkStructure controlNetwork)
{
DataTable cltAttributeTable = new DataTable();
try
{
using (var automationService = ConsumedServiceProvider.Provider.AutomationService)
{
foreach (string userDefinedLogicTemplate in selectedCLT)
{
var controlLogicClt =
automationService.AutomationClt.GetControlLogicTemplate(userDefinedLogicTemplate);
cltAttributeTable = await LoopDeLoop(controlLogicClt);
}
}
}
catch (Exception exception)
{
LogService.LogException(this, ServiceResources.CONTEXT_CLD_EDITOR, "CLT Attribute",
exception);
}
finally
{
// Accepting all the modification to the table before leaving this method call
cltAttributeTable.AcceptChanges();
}
return cltAttributeTable;
}
//Main loop with loops adding rows
private async Task<DataTable> LoopDeLoop(dynamic controlLogicClt)
{
DataTable cltAttributeTable = GetAttributeTable();
foreach (ISheet sheet in await controlLogicClt.GetSheets())
{
foreach (IFunctionCode functionCode in await sheet.GetFunctionCodes())
{
cltAttributeTable = GetNewRows(cltAttributeTable, functionCode.Attributes, functionCode.Name, "FUNCTION CODE", controlLogicClt, sheet);
}
foreach (IInputReference inputReference in await sheet.GetInputReferences())
{
cltAttributeTable = GetNewRows(cltAttributeTable, inputReference.Attributes, inputReference.Name, "IREF", controlLogicClt, sheet);
}
foreach (IOutputReference outputReference in await sheet.GetOutputReferences())
{
cltAttributeTable = GetNewRows(cltAttributeTable, outputReference.Attributes, outputReference.Name, "OREF", controlLogicClt, sheet);
}
foreach (IText text in await sheet.GetTexts())
{
cltAttributeTable = GetNewRows(cltAttributeTable, text.Attributes, text.Name, "TEXT", controlLogicClt, sheet);
}
}
}
//Adds the new created rows to the DataTable
private DataTable GetNewRows(DataTable cltAttributeTable, List<IHarmonyAttribute> attributes, string name, string objectType, dynamic controlLogicClt, ISheet sheet)
{
foreach (IHarmonyAttribute attribute in attributes)
{
cltAttributeTable.Rows.Add(GetNewRow(sourceDataRow, attribute, name, objectType, controlLogicClt, sheet));
}
}
//Creates and populates the new row
private DateRow GetNewRow(IHarmonyAttribute attribute, string name, string objectType, dynamic controlLogicClt, ISheet sheet)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = objectType;
row["OBJECT_NAME"] = name;
row["ATTRIBUTE_NAME"] = attribute.Type;
row["ATTRIBUTE_VALUE"] = attribute.Value;
return row;
}
So I made three more methods:
LoopDeLoop --> This one's for taking out of the main method the majority of the loops and thus making it clearer. Also, it makes the sheets loops clearer.
´GetNewRows´ --> This one is for making a common IHarmonyAttribute loop and get rid of the need of using four loops.
GetNewRow --> This one just populates a new row, but now you don't have to do that four times.
Hope this helps

Related

Problem with filling a Table in c# from a list

I am working with WindowsForm and C# and I am filling out a List<> and everything is fine so far
In my list<> I have 103 records but when I pass to my DataTable no record appears only the headers, that is to say that when I receive my table everything shows in null
The truth is, I'm a little new to this and it's the first one that I'm working on.
List<MSProject.Task> tasks = new List<MSProject.Task>();
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
my code
public static DataTable ListToDataTable<T>(IList<T> data)
{
DataTable table = new DataTable();
//special handling for value types and string
if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
{
DataColumn dc = new DataColumn("Value");
table.Columns.Add(dc);
foreach (T item in data)
{
DataRow dr = table.NewRow();
dr[0] = item;
table.Rows.Add(dr);
}
}
else
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
foreach (PropertyDescriptor prop in properties)
{
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
{
try
{
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
}
catch (Exception ex)
{
row[prop.Name] = DBNull.Value;
}
}
table.Rows.Add(row);
}
}
return table;
}
Thanks in advance
I've run your code and it's work, The problem I believe is that the MSProject.Task doesn't have any public properties and you are iterating only in properties. PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
see the code below:
//with properties
public class MSProject.Task
{
public string Name {get; set;}
public string code {get; set;}
}
//no properties
public class MSProject.Task2
{
public string Name;
public string code;
}
private void ShowRownsInGrid()
{
//it works shows rows in DataGridView!
List<MSProject.Task> tasks = new List<MSProject.Task>()
{
new MSProject.Task(){ Name = "Joao", code = "01215452124"},
new MSProject.Task(){ Name = "Maria", code = "4564678"},
};
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
MessageBox.Show(Tabla.Rows.Count.ToString()); //Show '2'
dataGridView1.DataSource = Tabla; //Show 2 rows
}
private void DontShowRownsInGrid()
{
//it doesn't work, no rows are displayed in the DataGridView!
List<MSProject.Task2> tasks = new List<MSProject.Task2>()
{
new MSProject.Task2(){ Name = "Joao", code = "01215452124"},
new MSProject.Task2(){ Name = "Maria", code = "4564678"},
};
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
MessageBox.Show(Tabla.Rows.Count.ToString()); //Show '2'
dataGridView1.DataSource = Tabla; //does not show lines
}

How to evaluate a DataColumn expression faster?

I created a class that's responsible to evaluate an expression values for the row when I need it but the problem that when my methods works very slow in big datatables.
public static class RowExpressionEvaluator
{
public static object EvaluateValue(DataRow row, string expression, Type outputType)
{
if (row == null) throw new ArgumentNullException(nameof(row));
return EvaluateValue(row.Table, row, expression, outputType);
}
private static object EvaluateValue(DataTable table, DataRow row, string expression, Type outputType)
{
if (table == null) throw new ArgumentNullException(nameof(table));
if (row == null) throw new ArgumentNullException(nameof(row));
if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Expression cannot be null or empty.", nameof(expression));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(#"The rowState is detached.");
object result = null;
using (var tempColumn = new DataColumn("Exp", outputType))
{
table.Columns.Add(tempColumn);
tempColumn.Expression = expression;
if (!row[tempColumn.ColumnName].IsDbNull())
result = row[tempColumn.ColumnName];
table.Columns.Remove(tempColumn);
}
return result;
}
}
This code works fine but its slow when the datatable contains big data
I tried to improve this code so when I have many expressions to evaluate
then when I finish I dispose it it works better but still needs to improve because I think when I add the column with an expression the datatable evaluate it for all the rows but I need to evaluate the value only for the passed row.
like this one :
public sealed class BetterRowExpressionEvaluator :IDisposable
{
private readonly DataRow _row;
private readonly DataColumn _expressionColumn;
public BetterRowExpressionEvaluator(DataRow row)
{
_row = row ?? throw new ArgumentNullException(nameof(row));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(#"The rowState is detached.");
_expressionColumn = new DataColumn("Expression",typeof(object));
DataTable table = _row.Table;
table.Columns.Add(_expressionColumn);
}
public object Evaluate(string expression)
{
if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Value cannot be null or empty.", nameof(expression));
_expressionColumn.Expression = expression;
return !_row[_expressionColumn.ColumnName].IsDbNull() ? _row[_expressionColumn.ColumnName] : null;
}
public void Dispose()
{
if (_expressionColumn == null) return;
DataTable table = _row.Table;
table.Columns.Remove(_expressionColumn);
_expressionColumn?.Dispose();
}
}
I did something like this to clone the row and it's parent and child relations so when I evaluate the expressions it comes faster and this is what I did :
private DataSet CloneTableWithRelations(DataRow row)
{
var dataset = new DataSet("EvaluationDataSet") {Locale = CultureInfo.InvariantCulture};
dataset.Tables.Add(row.Table.Clone());
dataset.Tables[row.Table.TableName].ImportRow(row);
foreach (DataRelation parentRelation in row.Table.ParentRelations)
{
string relationName = parentRelation.RelationName;
DataTable parentTable = parentRelation.ParentTable;
// clone the parent table
dataset.Tables.Add(parentTable.Clone());
// copy the parent rows related only to the passed row
DataRow parentRow= row.GetParentRow(relationName);
dataset.Tables[parentTable.TableName].ImportRow(parentRow);
DataColumn parentColumn=parentRelation.ParentColumns[0];
DataColumn childColumn=parentRelation.ChildColumns[0];
dataset.Relations.Add(relationName, parentColumn, childColumn,false);
}
foreach (DataRelation dataRelation in row.Table.ChildRelations)
{
DataTable childTable = dataRelation.ChildTable;
// clone the parent table
dataset.Tables.Add(childTable.Clone());
// copy the parent rows related only to the passed row
foreach (DataRow childRow in row.GetChildRows(dataRelation.RelationName))
{
dataset.Tables[childTable.TableName].ImportRow(childRow);
}
DataColumn parentColumn=dataRelation.ParentColumns[0];
DataColumn childColumn=dataRelation.ChildColumns[0];
dataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn,false);
}
return dataset;
}
is there a better way to do it and more reliable way ?
Finally It works much better
when I clone the row and its parent and child data
using this class I've created
public class RowCloneHandler
{
private readonly DataRow _row;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "<Pending>")]
public RowCloneHandler(DataRow row)
{
_row = row ?? throw new ArgumentNullException(nameof(row));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException("The rowState is detached.");
}
public DataSet CloneToDataSet()
{
var ClonedDataset = new DataSet { Locale = CultureInfo.InvariantCulture };
DataTable clonedMainTable = _row.Table.Clone();
ClonedDataset.Tables.Add(clonedMainTable);
ClonedDataset.Tables[_row.Table.TableName].ImportRow(_row);
CloneParentTablesToDataset(ClonedDataset, clonedMainTable);
CloneChildTablesToDataSet(ClonedDataset, clonedMainTable);
return ClonedDataset;
}
private void CloneChildTablesToDataSet(DataSet clonedDataset, DataTable clonedMainTable)
{
foreach (DataRelation dataRelation in _row.Table.ChildRelations)
{
DataTable childTable = dataRelation.ChildTable;
// clone the parent table
DataTable clonedChildTable = childTable.Clone();
// copy the parent rows related only to the passed row
foreach (DataRow childRow in _row.GetChildRows(dataRelation.RelationName))
{
clonedChildTable.ImportRow(childRow);
}
clonedDataset.Tables.Add(clonedChildTable);
DataColumn parentColumn = clonedMainTable.Columns[dataRelation.ParentColumns[0].ColumnName];
DataColumn childColumn = clonedChildTable.Columns[dataRelation.ChildColumns[0].ColumnName];
clonedDataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn, false);
}
}
private void CloneParentTablesToDataset(DataSet clonedDataset, DataTable clonedMainTable)
{
foreach (DataRelation parentRelation in _row.Table.ParentRelations)
{
DataTable parentTable = parentRelation.ParentTable;
// clone the parent table
DataTable clonedParentTable = parentTable.Clone();
// copy the parent rows related only to the passed row
DataRow parentRow = _row.GetParentRow(parentRelation.RelationName);
clonedParentTable.ImportRow(parentRow);
clonedDataset.Tables.Add(clonedParentTable);
DataColumn parentColumn = clonedParentTable.Columns[parentRelation.ParentColumns[0].ColumnName];
DataColumn childColumn = clonedMainTable.Columns[parentRelation.ChildColumns[0].ColumnName];
clonedDataset.Relations.Add(parentRelation.RelationName, parentColumn, childColumn, false);
}
}
}
You need to create a clone of your DataTable, import the DataRow to the cloned DataTable, and then add the computed column in the cloned DataTable. Here are some extension methods that do exactly that.
Update: I revised the code to take into account the existing relations with other tables. The code became much more complicated because now the cloned table must be placed inside the existing DataSet, and the existing relations must be also cloned, and temporary renamed. The cloned relations are created without constraints, so hopefully the performance will not be negatively affected.
public static class DataRowExtensions
{
public static object Compute(this DataRow dataRow, string expression)
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(object), expression);
return clonedRow[dataColumn];
}
}
public static T Compute<T>(this DataRow dataRow, string expression)
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
return clonedRow.Field<T>(dataColumn);
}
}
public static T? ComputeNullable<T>(this DataRow dataRow, string expression)
where T : struct
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
return clonedRow.Field<T?>(dataColumn);
}
}
private static DataTable CloneDataTable(DataRow dataRow)
{
var dataTable = dataRow.Table;
var dataSet = dataRow.Table.DataSet;
if (dataSet == null) return dataTable.Clone();
var clonedDT = dataSet.Tables.Add();
foreach (DataColumn column in dataTable.Columns)
{
clonedDT.Columns.Add(column.ColumnName, column.DataType);
}
var relationsAdded = new List<
(DataRelation Cloned, DataRelation Original)>();
foreach (var relation in dataTable.ParentRelations
.Cast<DataRelation>().ToArray())
{
var relationName = relation.RelationName;
relation.RelationName = Guid.NewGuid().ToString();
var clonedColumns = relation.ChildColumns
.Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
var clonedRelation = dataSet.Relations.Add(relationName,
relation.ParentColumns, clonedColumns, createConstraints: false);
relationsAdded.Add((clonedRelation, relation));
}
foreach (var relation in dataTable.ChildRelations
.Cast<DataRelation>().ToArray())
{
var relationName = relation.RelationName;
relation.RelationName = Guid.NewGuid().ToString();
var clonedColumns = relation.ParentColumns
.Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
var clonedRelation = dataSet.Relations.Add(relationName,
clonedColumns, relation.ChildColumns, createConstraints: false);
relationsAdded.Add((clonedRelation, relation));
}
clonedDT.Disposed += (s, e) => // Cleanup
{
clonedDT.Rows.Clear();
foreach (var entry in relationsAdded)
{
dataSet.Relations.Remove(entry.Cloned);
entry.Original.RelationName = entry.Cloned.RelationName;
}
clonedDT.Columns.Clear();
dataSet.Tables.Remove(clonedDT);
};
return clonedDT;
}
}
Usage example:
var dt = new DataTable();
dt.Columns.Add("Price", typeof(decimal));
dt.Rows.Add(10);
decimal doublePrice = dt.Rows[0].Compute<decimal>("Price * 2");
Console.WriteLine(doublePrice);
Output:
20

How can I write to a DataTable?

I'm in need of a fresh pair of eyes here. I'm trying to extract details from a user-uploaded file, add them to a DataTable, then write them to my SQL Server table.
It all appears to work nicely but for the actual fields being added to the DataTable (rather crucial!). The code runs to completion but with a blank DataTable, so obviously nothing is being written to my table. I attach my Controller - I'm sure it's something obvious that I'm missing, but I can't determine why.
public class FileUploaderController : Controller
{
public FileUploaderController()
{
db = new DatabaseContext();
}
public FileUploaderController(DatabaseContext context)
{
db = context;
}
private DatabaseContext db { get; }
public ActionResult UploadFilesPartial()
{
return View();
}
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
var dt = CreateDataTable();
if (file != null)
{
var fileName = file.FileName;
var filePath = Path.GetFullPath(fileName);
var fileType = Path.GetExtension(fileName);
DataRow fileDetails = dt.NewRow();
fileDetails["FileName"] = fileName;
fileDetails["FilePath"] = filePath;
fileDetails["FileType"] = fileType;
UploadToDataBase(db, dt);
}
return RedirectToAction("NettingBatch", "Views");
}
private DataTable CreateDataTable()
{
System.Data.DataTable table = new DataTable("FileUploads");
DataColumn id;
id = new DataColumn
{
DataType = System.Type.GetType("System.Int32"),
ColumnName = "Id",
ReadOnly = true,
Unique = true
};
table.Columns.Add(id);
DataColumn name;
name = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FileName",
AutoIncrement = false,
Caption = "FileName",
ReadOnly = false,
Unique = false
};
table.Columns.Add(name);
DataColumn path;
path = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FilePath",
AutoIncrement = false,
Caption = "FilePath",
ReadOnly = false,
Unique = false
};
table.Columns.Add(path);
DataColumn type;
type = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FileType",
AutoIncrement = false,
Caption = "FileType",
ReadOnly = false,
Unique = false
};
table.Columns.Add(type);
DataColumn[] PrimaryKeyColumns = new DataColumn[1];
PrimaryKeyColumns[0] = table.Columns["Id"];
table.PrimaryKey = PrimaryKeyColumns;
return table;
}
private void UploadToDataBase(DbContext context, DataTable data)
{
try
{
var columnMappings = new List<Tuple<string, string>>
{
new Tuple<string, string>("Id", "Id"),
new Tuple<string, string>("FileName", "FileName"),
new Tuple<string, string>("FilePath", "FilePath"),
new Tuple<string, string>("FileType", "FileType"),
};
PopulateHistoryTable.BulkCopyDataTableIntoDatabase(context, data, "[mdp].[FileUploads]",
columnMappings);
}
catch (Exception e)
{
Console.WriteLine(e.InnerException);
}
}
}
I expect the details of my test file that I upload on my view to be extracted and written back to FileUploads table. Actual results are blank.
NewRow returns a row that matches your DataTable scheme, but it doesn't actually "add" it to the DataTable. Use the "Add" function of the Rows collection to do that:
DataRow fileDetails = dt.NewRow();
fileDetails["FileName"] = fileName;
fileDetails["FilePath"] = filePath;
fileDetails["FileType"] = fileType;
dt.Rows.Add(fileDetails);

how to change datagridview column type as programmatically after binding

I bound the list of string datasource to the gridview,Then i tried to change those to DataGridViewLinkCell but it's not reflect on the gridview.
public ExpandReport(String operation,List<string> dataSource,string companyCode,string DBName, string DType)
{
try
{
InitializeComponent();
this.ex_dataGridView.DefaultCellStyle.Font=new Font("Verdana", 8);
ex_linkLabel.Text = operation;
filteredResult = dataSource;
ex_dataGridView.DataSource = dataSource.ConvertAll(x => new { Value = x });
company_Code = companyCode;
DB_Name = DBName;
D_Type = DType;
if (DType.Equals("QT"))
{
foreach (DataGridViewRow row in ex_dataGridView.Rows)
{
DataGridViewLinkCell linkCell = new DataGridViewLinkCell();
linkCell.Value = row.Cells[0].Value;
row.Cells[0] = linkCell;
}
}
}

Winforms load text file to datagridview and set default value in combobox

I have my problem in my datagrid view.
The datagridview is from the text file:
Student Name|Grade Level|Student Number|Gender
John,Grade 7,54015,1
Jessy,Grade 3,20147,2
Kyle,Grade 9,41812,1
Howard,Grade 6,8436,1
And my code:
var lines = File.ReadAllLines(#"C:\Users\1\Desktop\1.txt");
if (lines.Count() > 0)
{
foreach (var columnName in lines.FirstOrDefault()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
dataGridView1.Columns.Add(columnName, columnName);
}
foreach (var cellValues in lines.Skip(1))
{
var cellArray = cellValues
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (cellArray.Length == dataGridView1.Columns.Count)
dataGridView1.Rows.Add(cellArray);
}
foreach (var columnName in lines.First()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
if (columnName == "Gender")
{
var dgc = new DataGridViewComboBoxColumn() { Name = "hi", HeaderText = "bye" };
// dgc.Items.AddRange("Male", "Female");
DataTable dt = new DataTable();
DataColumn dc1 = new DataColumn("ID");
DataColumn dc2 = new DataColumn("Name");
dt.Columns.Add(dc1);
dt.Columns.Add(dc2);
dt.Rows.Add(1, "Male");
dt.Rows.Add(2, "Female");
DataGridViewComboBoxColumn c1 = new DataGridViewComboBoxColumn();
c1.DataSource = dt;
c1.DisplayMember = "Name";
c1.ValueMember = "ID";
for (int i = 0; i < dt.Rows.Count; i++)
{
if (dt.Rows[i]["ID"].ToString() == "1") // 1 for MALE & 2 for FEMALE
{
c1.DefaultCellStyle.NullValue = dt.Rows[i]["Name"];
}
}
dataGridView1.Columns.Add(c1);
continue;
}
}
}
What i want is to remove the 3rd column and replace it by the fourth column and also to change the value of this
if (dt.Rows[i]["ID"].ToString() == "1") // 1 for MALE & 2 for FEMALE
on value member ID
to the fourth value of a line in the text file.
John,Grade 7,54015,1
Jessy,Grade 3,20147,2
Kyle,Grade 9,41812,1
Howard,Grade 6,8436,1
If the value is 1, the default value of combobox is male.
While, if the value 2, the default value of combobox is female.
Can anyone fix and edit my code?
Thanks to everyone!
Try changing your first loop to handle the "gender" column differently than the rest.
Create a ComboBox column and populate it with the two gender values.
foreach (var columnName in lines.First()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
if (columnName == "Gender")
{
var dgc = new DataGridViewComboBoxColumn() { HeaderText = "Gender" };
dgc.Items.AddRange(
new KeyValuePair<string, string>("1", "Male"),
new KeyValuePair<string, string>("2", "Female"));
dgc.ValueMember = "Key";
dgc.DisplayMember = "Value";
dataGridView1.Columns.Add(dgc);
continue;
}
dataGridView1.Columns.Add(columnName, columnName);
}
Then when you add your rows of data, including the gender, it should display correctly in the ComboBox but also allow you to change the value.
Edit (update for setting a default value in the ComboBox):
foreach (var cellValues in lines.Skip(1))
{
var cellArray = cellValues.Split(new[] { ',' });
if (cellArray.Length == dataGridView1.Columns.Count)
{
if (String.IsNullOrEmpty(cellArray[dataGridView1.Columns.Count]))
cellArray[dataGridView1.Columns.Count] = "1";
dataGridView1.Rows.Add(cellArray);
}
}
Basically, the above just looks at the last "column" from the file and, if it's empty, then default to "Male".
I also removed StringSplitOptions.RemoveEmptyEntries. That option will only return 3 elements in cellArray when the gender column is empty, which will also cause cellArray.Length == dataGridView1.Columns.Count to fail.
This works,
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Rows.Add(1, "Male");
dt.Rows.Add(2, "Female");
var lines = File.ReadAllLines(#"C:\Users\1\Desktop\1.txt");
if (lines.Count() > 0)
{
foreach (var columnName in lines.FirstOrDefault()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
if (columnName == "Gender")
{
var c1 = new DataGridViewComboBoxColumn();
c1.DataSource = dt;
c1.DisplayMember = "Name";
c1.ValueMember = "ID";
c1.HeaderText = "Gender";
dataGridView1.Columns.Add(c1);
continue;
}
dataGridView1.Columns.Add(columnName, columnName);
}
foreach (var cellValues in lines.Skip(1))
{
var cellArray = cellValues
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (cellArray.Length == dataGridView1.Columns.Count)
dataGridView1.Rows.Add(cellArray);
}
}
I also like to point out that you are using
cellValues.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
which could be dangerous, because of StringSplitOptions.RemoveEmptyEntries the entry
Hulk,,8436,1
would be skipped.
And I would suggest you use a DataTable as a DataSource
private void Form1_Shown(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Rows.Add(1, "Male");
dt.Rows.Add(2, "Female");
var table = GetDataTable(#"C:\temp\1.txt");
foreach (DataColumn column in table.Columns)
{
var columnName = column.ColumnName;
DataGridViewColumn col;
if (columnName == "Gender")
{
var c1 = new DataGridViewComboBoxColumn();
c1.DataSource = dt;
c1.DisplayMember = "Name";
c1.ValueMember = "ID";
col = c1;
}
else
{
col = new DataGridViewTextBoxColumn();
}
col.HeaderText = "Gender";
col.DataPropertyName = columnName;
dataGridView1.Columns.Add(col);
}
dataGridView1.DataSource = table;
}
the method could look like this
private DataTable GetDataTable(string fileName)
{
var table = new DataTable();
var lines = File.ReadAllLines(#"C:\temp\1.txt");
if (lines.Count() > 0)
{
foreach (var columnName in lines.FirstOrDefault()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
table.Columns.Add(columnName);
}
foreach (var cellValues in lines.Skip(1))
{
var cellArray = cellValues
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (cellArray.Length == table.Columns.Count)
table.Rows.Add(cellArray);
}
}
return table;
}
Do something like this,
public partial class Form1 : Form
{
bool bIsComboBox = false;
delegate void SetComboBoxCellType(int iRowIndex);
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
var lines = File.ReadAllLines(#"C:\\1.txt");
if (lines.Count() > 0)
{
foreach (var columnName in lines.FirstOrDefault()
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
dt.Columns.Add(columnName, typeof(string));
}
foreach (var cellValues in lines.Skip(1))
{
var cellArray = cellValues
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
// if (cellArray.Length == dataGridView1.Columns.Count)
dt.Rows.Add(cellArray);
}
dataGridView1.DataSource = dt;
}
}
private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
SetComboBoxCellType objChangeCellType = new SetComboBoxCellType(ChangeCellToComboBox);
if (e.ColumnIndex == this.dataGridView1.Columns["Gender"].Index)
{
this.dataGridView1.BeginInvoke(objChangeCellType, e.RowIndex);
bIsComboBox = false;
}
}
private void ChangeCellToComboBox(int iRowIndex)
{
if (bIsComboBox == false)
{
DataGridViewComboBoxCell dgComboCell = new DataGridViewComboBoxCell();
dgComboCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
dgComboCell.DataSource = dt;
dgComboCell.ValueMember = "Gender";
dgComboCell.DisplayMember = "Gender";
dataGridView1.Rows[iRowIndex].Cells[dataGridView1.CurrentCell.ColumnIndex] = dgComboCell;
bIsComboBox = true;
}
}

Categories

Resources