I have a tree view, which I'm populating using a server side method taking values from the DB.
The tree view gets populated and works fine.
I'm trying to access the Tree View nodes inside a foreach loop, but to my surprise, the count property shows value as 1.
PFB the codes:
public void PopulateTreeView()
{
DataSet ds = new DataSet();
DataTable dtUltimateParent = GetUltimateParent();
DataTable dtA = GetParent();
DataTable dtB = GetChildren();
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
DataTable dt3 = new DataTable();
dt1 = dtUltimateParent.Copy();
dt2 = dtA.Copy();
dt3 = dtB.Copy();
ds.Tables.Add(dt1);
ds.Tables.Add(dt2);
ds.Tables.Add(dt3);
ds.Relations.Add("FirstHierarchy", dt1.Columns["ultimateParentID"], dt2.Columns["ParentID"]);
ds.Relations.Add("SecondHierarchy", dt2.Columns["ParentID"], dt3.Columns["ChildID"]);
if (ds.Tables[0].Rows.Count > 0)
{
TreeView1.Nodes.Clear();
foreach (DataRow ultimateRow in ds.Tables[0].Rows)
{
TreeNode ultimateNode = new TreeNode((string)ultimateRow["ultimateParentText"], Convert.ToString(ultimateRow["ultimateParentID"]));
TreeView1.Nodes.Add(ultimateNode);
ultimateNode.Expanded = true;
ultimateNode.SelectAction = TreeNodeSelectAction.None;
foreach (DataRow masterRow in ultimateRow.GetChildRows("FirstHierarchy"))
{
TreeNode masterNode = new TreeNode((string)masterRow["ParentText"], Convert.ToString(masterRow["ParentID"]));
ultimateNode.ChildNodes.Add(masterNode);
masterNode.Value = Convert.ToString(masterRow["ParentID"]);
masterNode.Expanded = false;
masterNode.SelectAction = TreeNodeSelectAction.None;
foreach (DataRow childRow in masterRow.GetChildRows("SecondHierarchy"))
{
TreeNode childNode = new TreeNode((string)childRow["ChildText"], Convert.ToString(childRow["ChildID"]));
masterNode.ChildNodes.Add(childNode);
childNode.Value = Convert.ToString(childRow["Child"]);
}
}
}
}
}
Now I am trying to access the Nodes using a foreach loop like below:
string[] myArray=an array having the texts of the Nodes
foreach(TreeNode node in TreeView1.Nodes)
{
foreach(string s in myArray)
{
if(s==node.Text)
{
node.checked=true;
}
}
}
TreeView1.Nodes shows only one node which is 'All', also TreeView1.Nodes.Count comes as 1, but the whole tree view is populated and is pretty visible.
The Tree View is something like:
All
A
a1
a2
B
b1
b2
Am I doing the correct way to iterate amongst the tree view nodes?
Experts please guide over here.
Regards
Anurag
All
A
a1
a2
B
b1
b2
TreeView1.Nodes //this shows the upper hierarchy...to access the nodes under All
you should use
TreeView1.Nodes[0].Nodes //this will show the nodes under All that means
A and B......so correct your code this way
TreeView1.Nodes[0].Nodes[0].Nodes.......a1 and a2
Related
How to melt a DataTable in C# (wide to long format) as Python Pandas.melt does? https://pandas.pydata.org/docs/reference/api/pandas.melt.html
Is there any method already implemented? If not, how the code for melting a DataTable would
For example:
I have one DataTable which is in wide format, that is that has one row per id and has as many columns as variables. I would like to transform this DataTable to long format that has as many rows as combinations of id with each variable column. You can see this example in top image.
Please, if there is not clear enough visit Pandas documentation, there is more clear. (https://pandas.pydata.org/docs/reference/api/pandas.melt.html)
Note: I would like a solution that is DataTable independent, that is, that the solution is able to take parameters as id_vars, value_vars, etc... like Pandas.melt does
Any help is appreciated.
I don't know that Melt method but according to docs it seems to be an unpivot method:
public static DataTable MeltTable(DataTable inputTable, string outputColumn, params string[] unpivotColumns)
{
DataTable resultTable = new DataTable();
DataColumn col = new DataColumn(outputColumn, inputTable.Columns[outputColumn].DataType);
resultTable.Columns.Add(col);
resultTable.Columns.Add("Variable");
resultTable.Columns.Add("Value");
foreach(string unpivotColumn in unpivotColumns)
{
foreach (DataRow row in inputTable.Rows)
{
resultTable.Rows.Add(row[outputColumn], unpivotColumn, row[unpivotColumn]);
}
}
return resultTable;
}
You use it in this way:
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Course");
table.Columns.Add("Age", typeof(int));
table.Rows.Add("Tim", "Masters", 47);
table.Rows.Add("Bob", "Graduate", 19);
table.Rows.Add("Sheila", "Graduate", 20);
DataTable resultTable = MeltTable(table, "Name", "Course", "Age");
Result:
Name Variable Value
Tim Course Masters
Bob Course Graduate
Sheila Course Graduate
Tim Age 47
Bob Age 19
Sheila Age 20
#TimSchmelter gave me the answer but I modified a little bit to be a more general solution. Here's the code:
public static List<string> GetDifferenceColumns(DataTable dt, List<string> diffCols)
{
string[] columns = GetColumnsList(dt).ToArray();
IEnumerable<string> differenceColumns = from column in columns.Except(diffCols.ToArray()) select column;
return differenceColumns.ToList();
}
public static DataTable Melt(DataTable dt, List<string> idCols = null, List<string> varCols = null)
{
string errorPrefixString = "Error in DataProcessing Melt Method:\n";
bool varsColsIsNull = (varCols == null || varCols.Count == 0);
bool idColsIsNull = (idCols == null || idCols.Count == 0);
string varsName = "Variable";
string valueName = "Value";
if (dt.Rows.Count == 0)
{
throw new Exception(errorPrefixString + "DataTable is empty");
}
if (varsColsIsNull && varsColsIsNull)
{
throw new Exception(errorPrefixString+"You should past at least varCols or idCols");
}
if (varsColsIsNull)
{
varCols = GetDifferenceColumns(dt, idCols);
}
if (idColsIsNull)
{
idCols = GetDifferenceColumns(dt, varCols);
}
DataTable resultTable = new DataTable();
// Creating final columns of resultTable
foreach (string id in idCols)
{
resultTable.Columns.Add(id);
}
resultTable.Columns.Add(varsName);
resultTable.Columns.Add(valueName);
// Populating resultTable with the new rows
// generated by unpivoting varCols
foreach (string varCol in varCols)
{
foreach (DataRow row in dt.Rows)
{
DataRow resultRow = resultTable.NewRow();
foreach(string id in idCols)
{
resultRow[id] = row[id]; // create id cols
}
resultRow[varsName] = varCol;
resultRow[valueName] = row[varCol];
resultTable.Rows.Add(resultRow);
}
}
return resultTable;
}
How to use it:
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Course");
dt.Columns.Add("Age");
dt.Rows.Add("Tim", "Masters", 47);
dt.Rows.Add("Bob", "Graduate", 19);
dt.Rows.Add("Sheila", "Graduate", 20);
List<string> varCols = new List<string> { "Course", "Age" };
DataTable finalDataTable = Melt(dt, varCols: varCols);
I have to fill a datagridview with 3 specifics XML nodes data from several XML files.
Here it´s an example:
<?xml version='1.0' encoding='iso-8859-1'?>
<retorno>
<mensagem>
<codigo>00001 - Sucesso</codigo>
</mensagem>
<alerta>
</alerta>
<numero_nfse>641</numero_nfse>
<serie_nfse>1</serie_nfse>
<data_nfse>08/09/2020</data_nfse>
<hora_nfse>12:16:10</hora_nfse>
<arquivo_gerador_nfse>688569.xml</arquivo_gerador_nfse>
<cod_verificador_autenticidade>03379569</cod_verificador_autenticidade>
</retorno>
I need these get 3 tags - <numero_nfse>, <data_nfse>, <cod_verificador_autenticidade> - and load them into a datagridview.
However, there are more XML files, with the same tags and I would to load all of them at the same time into a datagridview.
I wrote the code bellow and as you can see, it´s not working.
string[] arquivos = Directory.GetFiles(#"D:\Documentos\retorno");
DataSet retorno = new DataSet();
for (int j = 0; j < arquivos.Length; j++)
{
FileInfo Xmls = new FileInfo(arquivos[j]);
string caminhoXmls = Convert.ToString(Xmls);
XmlDocument retornoXml = new XmlDocument();
retornoXml.Load(caminhoXmls);
XmlNodeList retornoTags = retornoXml.GetElementsByTagName("retorno");
foreach (XmlNode xn in retornoTags)
{
string XmlNumeroNfse = xn["numero_nfse"].InnerText;
string XmlDataNfse = xn["data_nfse"].InnerText;
string XmlHoraNfse = xn["hora_nfse"].InnerText;
string XmlCodigo = xn["cod_verificador_autenticidade"].InnerText;
}
retorno.ReadXml(caminhoXmls);
dgvDadosNota.DataSource = retorno.Tables[j];
}
To clarify: I want one column for each tag. So my datagridview would be with 3 columns and as many rows as there are files in the directory. There´s only one <retorno> in each XML file.
Can anyone help me?
You are loading your multiple XML files into a DataSet with one DataTable for each file, but as explained in How to bind Dataset to DataGridView in windows application you can only bind a single DataTable to a DataGridView.
Since you have only one <retorno> node in each file, it would make sense to load the files into a DataTable with 3 columns - one each for <numero_nfse>, <data_nfse>, and <cod_verificador_autenticidade> - and one row for each file.
The following code does this:
static DataTable CreateDataTableFromRetornoXML(IEnumerable<string> fileNames)
{
var columnNames = new []
{
"numero_nfse",
"data_nfse",
"cod_verificador_autenticidade",
};
var rootName = "retorno";
var table = new DataTable();
foreach (var name in columnNames)
table.Columns.Add(name, typeof(string));
foreach (var fileName in fileNames)
{
var row = table.NewRow();
var root = XElement.Load(fileName);
var retorno = root.DescendantsAndSelf(rootName).Single(); // There should be only one.
foreach (DataColumn column in table.Columns)
{
row[column] = retorno.Element(column.ColumnName)?.Value;
}
table.Rows.Add(row);
}
return table;
}
Note I have switch to the LINQ to XML API which is generally easier to work with than the old XmlDocument API.
Demo fiddle here.
Why am I receiving the error Datatable named "Items" already belongs to this dataset in the code below? The error is only occurring when I am attempting to add values where if ((x > 1) || (x == 1)). I can successfully create multiple new "Item" datatables and then add them to the ds dataset in the while loop, however trying to more than one "AItem" datatable to AllItems dataset causes the error. The only difference between these datasets is that the ds dataset has many other datatables added to it which have data adapters filling them from a SQL db.
DataSet AllItems = new DataSet("Items");
DataSet ds = new DataSet("Header");
foreach (DataRow fieldRow in myDataset.Tables["tempTable"].AsEnumerable())
{
while (x < 1)
{
x++;
DataTable Item = new DataTable("Item");
Item.Columns.Add("ID");
Item.Columns.Add("LineNumber");
Item.Columns.Add("ItemID");
Item.Columns.Add("UnitPrice");
Item.Columns.Add("Description");
Item.Columns.Add("OrderUOM");
Item.Columns.Add("OrderQty");
DataRow aItem = Item.NewRow();
Item["ID"] = 1.ToString();
Item["LineNumber"] = x;
Item["ItemID"] = fieldRow[0].ToString();
Item["UnitPrice"] = fieldRow[1].ToString();
Item["Description"] = fieldRow[2].ToString();
Item["OrderUOM"] = fieldRow[3].ToString();
Item["OrderQty"] = fieldRow[4].ToString();
Item.Rows.Add(aItem);
ds.Tables.Add(Item);
}
if ((x > 1) || (x == 1))
{
DataTable AItem = new DataTable("AItem");
Item.Columns.Add("ID");
Item.Columns.Add("LineNumber");
Item.Columns.Add("ItemID");
Item.Columns.Add("UnitPrice");
Item.Columns.Add("Description");
Item.Columns.Add("OrderUOM");
Item.Columns.Add("OrderQty");
DataRow aItem = Item.NewRow();
Item["ID"] = 1.ToString();
Item["LineNumber"] = x;
Item["ItemID"] = fieldRow[0].ToString();
Item["UnitPrice"] = fieldRow[1].ToString();
Item["Description"] = fieldRow[2].ToString();
Item["OrderUOM"] = fieldRow[3].ToString();
Item["OrderQty"] = fieldRow[4].ToString();
Item.Rows.Add(aItem);
AllItems.Tables.Add(AItem);
}
}
I'm not sure if this is your only problem but you are using the DataTable as if it was a DataRow. Item is the table, it has no string-indexer, so replace
DataRow aItem = Item.NewRow();
Item["ID"] = 1.ToString();
Item["LineNumber"] = x;
Item["ItemID"] = fieldRow[0].ToString();
Item["UnitPrice"] = fieldRow[1].ToString();
Item["Description"] = fieldRow[2].ToString();
Item["OrderUOM"] = fieldRow[3].ToString();
Item["OrderQty"] = fieldRow[4].ToString();
Item.Rows.Add(aItem);
with
DataRow aItem = Item.NewRow();
aItem["ID"] = 1.ToString();
aItem["LineNumber"] = x;
aItem["ItemID"] = fieldRow[0].ToString();
aItem["UnitPrice"] = fieldRow[1].ToString();
aItem["Description"] = fieldRow[2].ToString();
aItem["OrderUOM"] = fieldRow[3].ToString();
aItem["OrderQty"] = fieldRow[4].ToString();
Item.Rows.Add(aItem);
But i assume that this is just the next error, "Datatable named “Items” already belongs to this dataset" is thrown because you are adding a table to a DataSet with the same name of another table. You are adding tables with the static name "AItem" in the loop here:
foreach (DataRow fieldRow in myDataset.Tables["tempTable"].AsEnumerable())
{
// ...
DataTable AItem = new DataTable("AItem");
// ...
AllItems.Tables.Add(AItem);
// ...
}
I don't understand the logic, maybe it's sufficient to use the default constructor without a name.
I have a datatable running in a foreach loop, getting site usage information on multiple sahrepoint websites. I would like to be able to add a column next to each foreach iteration adding the site url, I can only figure out how to do this adding a new row making the site url appear below the entry. Like So:
How can I get the url to go into the row above it?
My code is below:
SPListItemCollection items = list.GetItems(query);
DataTable aggregatedTable = new DataTable();
foreach (SPListItem item in items)
{
string url = item["SiteUrl"].ToString();
try
{
using (SPSite siteadd = new SPSite(url))
using (SPWeb webadd = siteadd.OpenWeb())
{
//
DataTable table = webadd.GetUsageData(Microsoft.SharePoint.Administration.SPUsageReportType.browser, Microsoft.SharePoint.Administration.SPUsagePeriodType.lastMonth);
table.Columns.Add("url");
if (table == null)
{
// HttpContext.Current.Response.Write("Table Null");
}
else
{
DataRow dr;
dr = table.NewRow();
dr["url"] = url;
table.Rows.Add(dr);
// table.Rows.Add(url);
aggregatedTable.Merge(table);//Append the data to previous site data.
}
}
}
catch { }
}
dataGridView1.DataSource = aggregatedTable;//bind datatable with
Why you adding a new row to you existing DataTable rather you should set value to you existing row.
e.g.
var CurRow = table.AsEnumerable().FirstOrDefault();
table.Columns.Add("url");
if (CurRow != null)
{
CurRow["url"] = url;
}
Would someone kindly assist me with the following? I have two DataGridView objects that each display a DataTable, where the two datatables are related with the following code:
DataSet dSet = new DataSet();
DataTable ParentList = ListToDataTable(_listOfAllAlbumObjects);
DataTable ChildList = ListToDataTable(_listOfAllTrackObjects);
dSet.Tables.AddRange(new DataTable[]{ParentList, ChildList});
DataColumn parentRelationColumn = ParentList.Columns["AlbumId"];
DataColumn childRelationColumn = ChildList.Columns["AlbumId"];
dSet.Relations.Add("ParentToChild", parentRelationColumn, childRelationColumn);
ParentDataGridView.DataSource = dSet;
ParentDataGridView.DataMember = "ParentList";
ChildDataGridView.DataSource = ???;
ChildDataGridView.DataMember = "ParentToChild";
Both DataTables are actually List<> converted to DataTables with the following:`
public static DataTable ListToDataTable<T>( IList<T> data)
{
var props = TypeDescriptor.GetProperties(typeof(T));
var table = new DataTable();
for (var i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
var values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0; i < values.Length; i++)
{ values[i] = props[i].GetValue(item); }
table.Rows.Add(values);
}
return table;
}
Initially it appears that the each DataGridView displays the data appropriately; however the child DataGridView does not update with any change of record in the parent DataGridView.
I see that the tables need to be interconnected through the binding-source; however I don't believe there is a true binding-source here.
Could someone please steer me in the right direction? Thanks.
There's an MSDN article showing what you want to do:
How to: Create a Master/Detail Form Using Two Windows Forms DataGridView Controls
Here's how this might work for you:
Either through the designer or through code add two BindingSources to your project: parentBindingSource and childBindingSource. Then try this in place of the code you have.
// Associate your BSs with your DGVs.
ParentDataGridView.DataSource = parentBindingSource;
ChildDataGridView.DataSource = childBindingSource;
// (Most of) your code here:
DataSet dSet = new DataSet();
DataTable ParentList = ListToDataTable(_listOfAllAlbumObjects);
DataTable ChildList = ListToDataTable(_listOfAllTrackObjects);
dSet.Tables.AddRange(new DataTable[]{ParentList, ChildList});
DataColumn parentRelationColumn = ParentList.Columns["AlbumId"];
DataColumn childRelationColumn = ChildList.Columns["AlbumId"];
dSet.Relations.Add("ParentToChild", parentRelationColumn, childRelationColumn);
// Let's name this DT to make clear what we're referencing later on.
ParentList.TableName = "ParentListDT";
// Rather than set the data properties on your DGVs, set them in your BindingSources.
parentBindingSource.DataSource = dSet;
parentBindingSource.DataMember = "ParentListDT";
childBindingSource.DataSource = parentBindingSource;
childBindingSource.DataMember = "ParentToChild";