Some problems about JSON data to C# Winforms TreeView - c#

Firstly, I read heaps of topics about JSON to TreeView on the Stackoverflow. After this, I create a JSON data like this:
{
"Cars": {
"Audi": [{
"A6 2.0 TDI quatro 2018 Red": ["S-Line", "17 inch rim", "Full sport packet"],
"A5 1.6 TFSI 2018 Blue": ["Desing packet", "Sunroof"]
}],
"Mercedes-Benz": [{
"E220d AMG 2018 white": ["Glass ceiling", "Vacuum doors", "Navigation"],
"E220d Exclusive Black 2018 Blue": ["Power seats", "Start & Stop"]
}]
}
}
Here is the C# code content:
private void Form1_Load(object sender, EventArgs e)
{
try
{
treeView1.Nodes.Clear();
var json = File.ReadAllText(Uz.path + #"cars.json");
var obj = JObject.Parse(json);
var parent = Json2Tree(obj);
treeView1.Nodes.Add(parent);
treeView1.ExpandAll();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, #"ERROR");
}
}
private static TreeNode Json2Tree(JObject obj)
{
//create the parent node
var parent = new TreeNode();
//loop through the obj. all token should be pair<key, value>
foreach (var token in obj)
{
//change the display Content of the parent
parent.Text = token.Key;
//create the child node
var child = new TreeNode();
child.Text = token.Key;
//check if the value is of type obj recall the method
if (token.Value.Type.ToString() == "Object")
{
// child.Text = token.Key.ToString();
//create a new JObject using the the Token.value
var o = (JObject)token.Value;
//recall the method
child = Json2Tree(o);
//add the child to the parentNode
parent.Nodes.Add(child);
}
//if type is of array
else if (token.Value.Type.ToString() == "Array")
{
int ix = -1;
// child.Text = token.Key.ToString();
//loop though the array
foreach (var itm in token.Value)
{
//check if value is an Array of objects
if (itm.Type.ToString() == "Object")
{
//child.Text = token.Key.ToString();
//call back the method
ix++;
var o = (JObject)itm;
var objTN = Json2Tree(o);
//objTN.Text = token.Key + "[" + ix + "]";
child.Nodes.Add(objTN);
//parent.Nodes.Add(child);
}
//regular array string, int, etc
else if (itm.Type.ToString() == "Array")
{
ix++;
var dataArray = new TreeNode();
foreach (var data in itm)
{
//dataArray.Text = token.Key + "[" + ix + "]";
dataArray.Nodes.Add(data.ToString());
}
child.Nodes.Add(dataArray);
}
else
{
child.Nodes.Add(itm.ToString());
}
}
parent.Nodes.Add(child);
}
else
{
//if token.Value is not nested
// child.Text = token.Key.ToString();
//change the value into N/A if value == null or an empty string
child.Nodes.Add(token.Value.ToString() == "" ? "N/A" : token.Value.ToString());
parent.Nodes.Add(child);
}
}
return parent;
}
when I run the code, the screenshot looks like this:
But marked as 1, 2 and 3 are should not be shown. It must be like this:
Although I worked 3 days, I did not succeed.

In JsonTreeView project, it show like this:
using System.Windows.Forms;
using Newtonsoft.Json.Linq;
namespace JsonTreeView
{
public static class JsonToTreeView
{
public static void Json2Tree(this TreeView treeView, string json, string group_name)
{
if (string.IsNullOrWhiteSpace(json))
{
return;
}
var obj = JObject.Parse(json);
AddObjectNodes(obj, group_name, treeView.Nodes);
}
public static void AddObjectNodes(JObject obj, string name, TreeNodeCollection parent)
{
var node = new TreeNode(name);
parent.Add(node);
foreach (var property in obj.Properties())
{
AddTokenNodes(property.Value, property.Name, node.Nodes);
}
}
private static void AddArrayNodes(JArray array, string name, TreeNodeCollection parent)
{
var node = new TreeNode(name);
parent.Add(node);
for (var i = 0; i < array.Count; i++)
{
AddTokenNodes(array[i], $"[{i}]", node.Nodes);
}
}
private static void AddTokenNodes(JToken token, string name, TreeNodeCollection parent)
{
switch (token)
{
case JValue _:
parent.Add(new TreeNode($"{((JValue) token).Value}"));
break;
case JArray _:
AddArrayNodes((JArray)token, name, parent);
break;
case JObject _:
AddObjectNodes((JObject)token, name, parent);
break;
}
}
}
}

Related

Add dynamically a string or an XElement as child to XElement

I'm trying to convert a two dimension list of string to an html table.
I did this who does the job :
public string htmlTableFromTwoDimensionList(List<List<string>> list)
{
XDocument xDocument = new XDocument(new XElement("table"));
XElement xTable = xDocument.Element("table");
foreach(List<string> row in list)
{
XElement xRow = new XElement("tr");
foreach(string col in row)
{
if (list.First() == row) xRow.Add(new XElement("th", col));
else xRow.Add(new XElement("td", col));
}
xTable.Add(xRow);
}
return xDocument.ToString();
}
But now, i learn that the string can be some html. So i would like to parse it if it's html or use a string if it's not. I tried to do something like that, without success :
public string htmlTableFromTwoDimensionList(List<List<string>> list)
{
XDocument xDocument = new XDocument(new XElement("table"));
XElement xTable = xDocument.Element("table");
foreach(List<string> row in list)
{
XElement xRow = new XElement("tr");
foreach(string col in row)
{
XElement content;
string text = "";
// tcheck if content is html or text :
try
{
content = XElement.Parse(col);
}
catch
{
text = col;
}
if (list.First() == row) xRow.Add(new XElement("th", string.IsNullOrEmpty(text) ? content : text));
else xRow.Add(new XElement("td", string.IsNullOrEmpty(text) ? content : text));
}
xTable.Add(xRow);
}
return xDocument.ToString();
}
But I'm not even sure to use try catch in this situation. Any idea to do that properly ?
here is a solution, probably not the best, with some sample input :
class Program
{
static void Main(string[] args)
{
List<List<string>> table = new List<List<String>>{
new List<String> { "1d", "Client", "some html", "Date", "col n"},
new List<String> { "1", "Client 1","google","31/12/2022", "some content ..." },
new List<String> { "2", "Client 2","google","31/12/2022", "some content ..." },
new List<String> { "3", "Client 3","google","31/12/2022", "some content ..." },
};
Console.Write(htmlTableFromTwoDimensionList(table));
Console.Read();
}
public static string htmlTableFromTwoDimensionList(List<List<string>> list)
{
XDocument xDocument = new XDocument(new XElement("table"));
XElement xTable = xDocument.Element("table");
foreach (List<string> row in list)
{
XElement xRow = new XElement("tr");
foreach (string col in row)
{
XElement htmlCel;
if (list.First() == row) htmlCel = new XElement("th");
else htmlCel = new XElement("td");
XElement content;
try
{
content = XElement.Parse(col);
htmlCel.Add(content);
}
catch
{
htmlCel.Add(col);
}
xRow.Add(htmlCel);
}
xTable.Add(xRow);
}
return xDocument.ToString();
}
}

How to increase the speed of finding elements?

This code performs extremely long time and in the end, doesn't accomplish its goal - combine element's parameters inside of a schedule. It clearly happens due to a large number of elements of the category "Pipe Fitting" in the project. How to increase the speed? Is it through selecting elements using some scheduled class?
Win Forms GUI is used.
Document revitDoc { get; set; }
public Form1(Document doc)
{
InitializeComponent();
this.revitDoc = doc;
//Create a list of the parameters you want your user to choose from
List<string> stringParameters = new List<string>
{
"GP_Description",
"GP_Model",
"GP_Angle"
};
//Add list to comboboxes on form
foreach (string parameterName in stringParameters)
{
comboBox1.Items.Insert(0, parameterName);
comboBox2.Items.Insert(0, parameterName);
comboBox3.Items.Insert(0, parameterName);
}
}
private void button1_Click(object sender, EventArgs e)
{
FilteredElementCollector collector = new FilteredElementCollector(revitDoc);
ElementCategoryFilter filter = new ElementCategoryFilter(BuiltInCategory.OST_PipeFitting);
IList<Element> ducts = collector.WherePasses(filter).WhereElementIsNotElementType().ToElements();
foreach (Element duct in ducts)
{
Parameter parameter1 = duct.LookupParameter(comboBox1.Text);
Parameter parameter2 = duct.LookupParameter(comboBox2.Text);
Parameter parameter3 = duct.LookupParameter(comboBox3.Text);
List<string> parameterValues = new List<string>();
if (parameter1 != null)
{
string parameterValue1 = parameter1.AsString();
if (parameterValue1 != "") parameterValues.Add(parameterValue1);
}
if (parameter2 != null)
{
string parameterValue2 = parameter2.AsString();
if (parameterValue2 != "") parameterValues.Add(parameterValue2);
}
if (parameter3 != null)
{
string parameterValue3 = parameter3.AsString();
if (parameterValue3 != "") parameterValues.Add(parameterValue3);
}
if (parameterValues.Count > 0)
{
string newValue = String.Join(" ,", parameterValues);
using (Transaction t = new Transaction(revitDoc, "Set Parameter name"))
{
t.Start();
duct.LookupParameter("Outcome").Set(newValue);
t.Commit();
}
This is untested. The biggest item is the location of the Transaction. I suggest wrapping the foreach loop with the Transaction.
public void Process(Document doc, string text1, string text2, string text3)
{
FilteredElementCollector collector =
new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeFitting)
.WhereElementIsNotElementType();
using (Transaction t = new Transaction(doc))
{
if (t.Start("Set Parameter Names") == TransactionStatus.Started)
{
foreach (Element elem in collector)
{
string newValue = string.Empty;
Parameter outcomeParam = elem.LookupParameter("Outcome");
if (outcomeParam == null)
{
continue;
}
newValue += this.GetParameterValue(elem, text1);
newValue += this.GetParameterValue(elem, text2);
newValue += this.GetParameterValue(elem, text3);
newValue.TrimEnd(',', ' ');
outcomeParam.Set(newValue);
}
t.Commit();
}
}
}
public string GetParameterValue(Element elem, string parameterName)
{
Parameter param = elem.LookupParameter(parameterName);
return param == null ? string.Empty : param.AsString() + ", ";
}

Select 1 item from the filter in a pivot table

I'm generating an excel in a server that doesn't have the excel application installed, so I used EPPlus to generate the Excel and in the excel there are many pivot tables. By default, all items in a filter of the pivot table are selected but I need specific filters to have only one item selected.Is there a way to do that using EPPlus?
If there isn't, is there another library that could do that? I need a something that could search all pivot tables, search the filters for a filter with a specific name and make only 1 item selected in the filters with that specific name for every pivot table.
If it's not possible in C#, is there another way to make a program that does it without having Excel installed?If there is, I could just run that program when the excel is done.
For example, I have a filter with 13 months :
Jan/2017
Feb/2017
Mar/2017
Apr/2017
May/2017
Jun/2017
Jul/2017
Aug/2017
Sep/2017
Oct/2017
Nov/2017
Dec/2017
Jan/2018
In the filter, by default all of them are selected, I want the newest one to be selected(in this example Jan/2018).
This is the class that I created to build pivot tables :
public class PivotTableFactory
{
private ExcelPivotTable _pivotTable;
public void CreatePivotTable (Worksheet baseSheet, Worksheet targetSheet, string startRange, string tableName)
{
// Selecting All Range of the base Sheet
String baseAddress = "A1:" + baseSheet.EPPlusSheet.Dimension.End.Address;
ExcelRange baseRange = baseSheet.EPPlusSheet.Cells[baseAddress];
ExcelAddress startAddress = targetSheet.EPPlusSheet.Cells[startRange];
// Creating Pivot Table
_pivotTable = targetSheet.EPPlusSheet.PivotTables.Add (startAddress, baseRange, tableName);
_pivotTable.UseAutoFormatting = false;
}
public void AddFilterField(string columnName, string fieldName, eSortType sortType = eSortType.None)
{
ExcelPivotTableField dataField = _pivotTable.PageFields.Add (_pivotTable.Fields[columnName]);
dataField.Name = fieldName;
dataField.Sort = sortType;
if (columnName.Equals("MES_REFERENCIA")) selectFilterValue("MÊS", 10);
}
public void AddRowField(string columnName, string fieldName, eSortType sortType = eSortType.None)
{
ExcelPivotTableField rowField = _pivotTable.RowFields.Add (_pivotTable.Fields[columnName]);
rowField.Name = fieldName;
rowField.Sort = sortType;
}
public void AddColumnField (string columnName, string fieldName, eSortType sortType = eSortType.None)
{
ExcelPivotTableField rowField = _pivotTable.ColumnFields.Add (_pivotTable.Fields[columnName]);
rowField.Name = fieldName;
rowField.Sort = sortType;
}
public void AddValueField (string columnName, string fieldName, DataFieldFunctions func = DataFieldFunctions.Sum, string format = "")
{
ExcelPivotTableDataField dataField = _pivotTable.DataFields.Add (_pivotTable.Fields[columnName]);
dataField.Name = fieldName;
dataField.Function = func;
dataField.Format = format;
}
public void SetStyle (TableStyles style)
{
_pivotTable.TableStyle = style;
}
public void SetDataOnRows (bool dataOnRow)
{
_pivotTable.DataOnRows = dataOnRow;
}
public void SetDataCaption(string name)
{
_pivotTable.DataCaption = name;
}
public void setAutoFormating(bool value)
{
_pivotTable.UseAutoFormatting = value;
}
public void printPivotXml()
{
_pivotTable.PivotTableXml.Save("pivotsXmls\\pivotFinal.xml");
}
private int getFieldIndex(string name)
{
for (int i = 0;i < _pivotTable.Fields.Count; i++)
{
if (_pivotTable.Fields[i].Name.Equals(name)) return i;
}
return -1;
}
public void selectFilterValue(string name, int valueIndex)
{
int fieldIndex = getFieldIndex(name);
if (fieldIndex < 0) return;
XmlDocument xDoc = _pivotTable.PivotTableXml;
XmlAttribute attr = xDoc.CreateAttribute("item");
attr.Value = valueIndex.ToString();
XmlNode currentDoc = null;
foreach (XmlNode node in xDoc.ChildNodes)
{
if (node.Name.Equals("pivotTableDefinition"))
{
currentDoc = node;
break;
}
}
foreach (XmlNode node in currentDoc.ChildNodes)
{
if (node.Name.Equals("pageFields"))
{
currentDoc = node;
}
if (node.Name.Equals("pivotFields"))
{
foreach (XmlNode field in node.ChildNodes)
{
if (field.Attributes["name"] == null) continue;
if (field.Attributes["name"].Value.Equals(name))
{
XmlNode itemsNode = field.ChildNodes[0];
itemsNode.InnerXml = "";
List<string> items = new List<string>();
// Add item tags
for(int i = 0; i < 13; i++){
XmlNode createdNode = xDoc.CreateNode(XmlNodeType.Element, "item" , currentDoc.NamespaceURI);
XmlAttribute tempAttr = xDoc.CreateAttribute("x");
tempAttr.Value = i.ToString();
createdNode.Attributes.Append(tempAttr);
itemsNode.AppendChild(createdNode);
}
XmlNode createdNodeDefault = xDoc.CreateNode(XmlNodeType.Element, "item", currentDoc.NamespaceURI);
XmlAttribute tempAttrDefault = xDoc.CreateAttribute("t");
tempAttrDefault.Value = "default";
createdNodeDefault.Attributes.Append(tempAttrDefault);
itemsNode.AppendChild(createdNodeDefault);
itemsNode.Attributes["count"].Value = "14";
}
}
}
}
foreach (XmlNode node in currentDoc.ChildNodes)
{
if (node.Attributes["fld"].Value.Equals(fieldIndex.ToString()))
{
node.Attributes.InsertAfter(attr, node.Attributes["fld"]);
}
}
}
}
I created the function "selectFilterValue" that tries to select the value of the filter using XML but it didn't work.Please ignore the extremely rough way of selecting the XML nodes...

Recursive tree search return wrong results

I have this implementation of a tree recursive function. It gets all possible paths from A to D.
The recursive function is:
private static List<Path> GetPath(int depth, Path path)
{
if (depth == nodes.Length)
{
return new List<Path> { path };
}
else
{
var result = new List<Path>();
foreach(var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
path.Add(node);
result.AddRange(
GetPath(
depth+1, path));
}
return result;
}
}
The expected results should be:
A1-B2->C3->D4
A1-B5->C3->D4
However, the paths returned are the same and they include all possible nodes twice.
What's wrong with the function?
foreach(var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
path.Add(node);
You probably intend to make a new path (which is a copy of path) for each node that you find here, before appending the next node.
As #moreON's suggest, I add clone function into class Path, and modify the loop, in loop i copy to new instance of path:
public class Path : List<Node>
{
public override string ToString()
{
String s = "";
foreach (var node in this)
{
s += node.Name + node.Links[0] + "->";
}
return s;
}
public Path Clone()
{
var newPath = new Path();
ForEach(x => newPath.Add(new Node {Name = x.Name, Links = new int[] {x.Links[0]}}));
return newPath;
}
}
private static List<Path> GetPath(int depth, Path path)
{
if (depth == nodes.Length)
{
return new List<Path> { path };
}
else
{
var result = new List<Path>();
foreach (var link in nodes[depth].Links)
{
Node node = new Node { Name = nodes[depth].Name, Links = new[] { link } };
var currentPath = path.Clone();
currentPath.Add(node);
result.AddRange(GetPath(depth + 1, currentPath));
}
return result;
}
}
Hope this help.

Transfer stringlist into treeview

I have a String List with items like this
"Root"
"Root/Item1"
"Root/Item2"
"Root/Item3/SubItem1"
"Root/Item3/SubItem2"
"Root/Item4/SubItem1"
"AnotherRoot"
How do I transfer this stringlist into a treeview ?
You can split each item into it's substrings. Then via recursion look for each item, if the parent exists add to it, and if the parent doesn't exists create it.
If you can't see how to do it, i`ll post you a sample code
Sample Code
public void AddItem(TreeView treeControl, TreeNode parent, string item)
{
TreeNodeCollection nodesRef = (parent != null) ? parent.Nodes : treeControl.Nodes;
string currentNodeName;
if (-1 == item.IndexOf('/')) currentNodeName = item;
else currentNodeName = item.Substring(0, item.IndexOf('/'));
if (nodesRef.ContainsKey(currentNodeName))
{
AddItem(treeControl, nodesRef[currentNodeName], item.Substring(currentNodeName.Length+1));
}
else
{
TreeNode newItem = nodesRef.Add(currentNodeName, currentNodeName);
if (item.Length > currentNodeName.Length)
{
AddItem(treeControl, newItem, item.Substring(item.IndexOf('/', currentNodeName.Length) + 1));
}
}
}
And the caller example:
string[] stringArr = {
"Root",
"Root/Item1",
"Root/Item2",
"Root/Item3/SubItem1",
"Root/Item3/SubItem2",
"Root/Item4/SubItem1",
"AnotherRoot"
};
foreach (string item in stringArr)
{
AddItem(treeView1, null, item);
}
One way is to iterate the items split the item and push them on a list and if the parent doesn't match pop an item from the list until the stack is empty or you have a match.
You can use this code:
private void button1_Click(object sender, EventArgs e) {
List<String> paths = new List<String> {
"Root", "Root/Item1", "Root/Item2", "Root/Item3/SubItem1",
"Root/Item3/SubItem2", "Root/Item4/SubItem1", "AnotherRoot"
};
List<TreeNode> nodeCollection = new List<TreeNode>();
foreach (var path in paths) {
AddPath(nodeCollection, path);
}
treeView1.Nodes.Clear();
treeView1.Nodes.AddRange(nodeCollection.ToArray());
}
public void AddPath(List<TreeNode> collection, String path) {
LinkedList<String> pathToBeAdded = new LinkedList<String>(path.Split(new String[] { #"/" }, StringSplitOptions.RemoveEmptyEntries));
if (pathToBeAdded.Count == 0) {
return;
}
String rootPath = pathToBeAdded.First.Value;
TreeNode root = collection.FirstOrDefault(n => n.Text.Equals(rootPath));
if (root == null) {
root = new TreeNode(rootPath);
collection.Add(root);
}
pathToBeAdded.RemoveFirst();
AddPath(root, pathToBeAdded);
}
public void AddPath(TreeNode rootNode, LinkedList<String> pathToBeAdded) {
if (pathToBeAdded.Count == 0) {
return;
}
String part = pathToBeAdded.First.Value;
TreeNode subNode = null;
if (!rootNode.Nodes.ContainsKey(part)) {
subNode = rootNode.Nodes.Add(part, part);
} else {
subNode = rootNode.Nodes[part];
}
pathToBeAdded.RemoveFirst();
AddPath(subNode, pathToBeAdded);
}
Hope this helps.
Ricardo Lacerda Castelo Branco

Categories

Resources