I am using TreeViewAdv. I know how to create a tree with a fix amount of columns. I drop the TreeView in the form and add columns and nodecontrols.
private class ColumnNode: Node
{
public string NodeControl1=""; // This should make the DataPropertyName specified in the Node Collection.
public string NodeControl2 = "";
public string NodeControl3 = "";
public ColumnNode(string nodeControl1, string nodeControl2, int nodeControl3)
: base(form)
{
NodeControl1 = nodeControl1;
NodeControl2 = nodeControl2;
NodeControl3 = nodeControl3.ToString();
}
}
Then I load the tree like this:
_treeViewAdv.Model = _model;
_treeViewAdv.BeginUpdate();
for (int i = 0; i < 20; i++)
{
Node parentNode = new ColumnNode("root" + i, "",0);
_model.Nodes.Add(parentNode);
for (int n = 0; n < 2; n++)
{
Node childNode = new ColumnNode("child" + n,"Further Information",1);
parentNode.Nodes.Add(childNode);
}
}
_treeViewAdv.EndUpdate();
But is it possible to create a dynamic number of columns?
Related
Value
ID
A
A
A
B
B
C
Desired output
Value
ID
A
1
A
1
A
1
B
2
B
2
C
3
I need to create IDs based on grouping the value column. A single ID for all A's and then B's respectively.
Thanks in advance!
You could simply use a loop like:
for(int i = 0; i < dataTable.Rows.Count; i++){
switch(dataTable.Rows[i][0].ToString()){
case "A" :
dataTable.Rows[i][1] = 1;
break;
case "B" :
dataTable.Rows[i][1] = 2;
break;
case "C" :
dataTable.Rows[i][1] = 3;
break;
// other cases
}
}
here dt is your datatable. Use a loop like this:
int id = 1;
for (int i = 0; i < dt.Rows.Count; i++)
{
if (i == 0)
{
dt.Rows[i]["ID"] = id;
if (i != dt.Rows.Count && dt.Rows[i + 1]["Value"] != dt.Rows[i]["Value"])
{
id++;
}
}
else
{
if (dt.Rows[i - 1]["Value"] == dt.Rows[i]["Value"])
{
dt.Rows[i]["ID"] = id;
}
else
{
id = id + 1;
dt.Rows[i]["ID"] = id;
}
}
}
If the values might fall between A-Z then consider the following done in a form but can be done where ever you want.
public class Replacement
{
public string Value { get; set; }
public int Index { get; set; }
}
Form code
private void SetIdButton_Click(object sender, EventArgs e)
{
var dataTable = MockedDataTable();
var items = ReplacementData;
for (int index = 0; index < dataTable.Rows.Count; index++)
{
dataTable.Rows[index].SetField("ID",
items.FirstOrDefault(replacement =>
replacement.Value == dataTable.Rows[index].Field<string>("Value")).Index);
}
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine($"{string.Join(",", row.ItemArray)}");
}
}
private static Replacement[] ReplacementData
=> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
.ToCharArray().Select((value, index) => new Replacement
{
Value = value.ToString(),
Index = index + 1
})
.ToArray();
private static DataTable MockedDataTable()
{
DataTable dataTable = new DataTable();
dataTable.Columns.Add("Value", typeof(string));
dataTable.Columns.Add("ID", typeof(int));
dataTable.Rows.Add("A");
dataTable.Rows.Add("A");
dataTable.Rows.Add("A");
dataTable.Rows.Add("B");
dataTable.Rows.Add("B");
dataTable.Rows.Add("C");
dataTable.Rows.Add("D");
return dataTable;
}
Output
A,1
A,1
A,1
B,2
B,2
C,3
D,4
You could also use a dictionary:
dictionary<string,int> record_dic = new dictionary<string,int>{{"A",1},{"B",2}};
I have a gridview with multiple groups and I use the CustomDrawGroupRow Event to display the row count for each group:
private void gridView_CustomDrawGroupRow(object sender, DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
info.GroupText = $"{caption} : {info.GroupValueText} ({view.GetChildRowCount(e.RowHandle)})";
}
Now I would like to display the row count recursively, so that the first level shows a count of 2171 (1913 + 135 + 123).
This is what I have tried, but it throws a StackOverflowException and I cannot see the problem here:
private void gridView_CustomDrawGroupRow(object sender, DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
info.GroupText = $"{caption} : {info.GroupValueText} ({GetRowCountRecursive(view, e.RowHandle)})";
}
private int GetRowCountRecursive(GridView view, int rowHandle)
{
int totalCount = 0;
int childrenCount = view.GetChildRowCount(rowHandle);
for (int i = 0; i < childrenCount; i++)
{
var childRowHandle = view.GetChildRowHandle(rowHandle, i);
totalCount += GetRowCountRecursive(view, childRowHandle);
}
return totalCount;
}
I was missing to check if childRowHandle is a group row with IsGroupRow(). If not, the recursion have to stop and totalCount need to be increased by 1.
private int GetRowCountRecursive(GridView view, int rowHandle)
{
int totalCount = 0;
int childrenCount = view.GetChildRowCount(rowHandle);
for (int i = 0; i < childrenCount; i++)
{
var childRowHandle = view.GetChildRowHandle(rowHandle, i);
if (view.IsGroupRow(childRowHandle))
{
totalCount += GetRowCountRecursive(view, childRowHandle);
}
else
{
totalCount++;
}
}
return totalCount;
}
You can use GroupRowInfo.ChildControllerRowCount property to get the row count. The instance of GroupRowInfo class you can get from GridGroupRowInfo.RowKey property.
Here is example:
private void gridView1_CustomDrawGroupRow(object sender, RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
var groupInfo = info.RowKey as GroupRowInfo;
info.GroupText = $"{caption} : {info.GroupValueText} ({groupInfo?.ChildControllerRowCount})";
}
Here is the screenshot:
I am trying to populate TextBoxes from a list. I have been able to populate ComboBoxes with comboList:
var comboList = new System.Windows.Forms.ComboBox[4];
comboList[0] = cmbSite1Asset;
comboList[1] = cmbSite2Asset;
comboList[2] = cmbSite3Asset;
comboList[3] = cmbSite4Asset;
List<CRCS.CAsset> assets = _rcs.Assets;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int i = 0; i < 4; ++i)
{
comboList[i].Items.Add(id);
}
}
But when I try and apply the same principle to TextBoxes
var aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int n = 0; n < 8; ++n)
{
aosList[n].Items.Add(id);
}
}
TextBox does not like Items.Add ( aosList[n]Items.Add(id); )
I am looking fore a reference or guidance resolving this issue. Thanks!
You should use ComboBox for your problem,instead of iterating on each element,You simply use below lines to populate combobox.
comboList.DataSource=assets;
comboList.DisplayMember="ID";
comboList.ValueMember="ID";
However,if you want your values in TextBox,you can use TextBox.AppendText Method, but it will not work like ComboBox as it will contain texts+texts+texts, will not have indexes like ComboBox.
private void AppendTextBoxLine(string myStr)
{
if (textBox1.Text.Length > 0)
{
textBox1.AppendText(Environment.NewLine);
}
textBox1.AppendText(myStr);
}
private void TestMethod()
{
for (int i = 0; i < 2; i++)
{
AppendTextBoxLine("Some text");
}
}
A Combobox is a collection of items, and so has an Items property from which you can add/remove to change it's contents. A Textbox is just a control that displays some text value, so it has a Text property which you can set/get, and which denotes the string that is displayed.
System.Windows.Forms.TextBox[] aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
for (int n = 0; n < 8; ++n)
{
aosList[n].Text = assets[n].ID; // make sure you have 8 assets also!
}
int i = 1;
foreach (var asset in assets)
{
this.Controls["txtAsset" + i].Text = asset.ID;
i++;
}
I need some help implementing Dijkstra's Algorithm and was hoping someone would be able to assist me. I have it so that it is printing some of routes but it isn't capturing the correct costs for the path.
Here is my node structure:
class Node
{
public enum Color {White, Gray, Black};
public string Name { get; set; } //city
public List<NeighborNode> Neighbors { get; set; } //Connected Edges
public Color nodeColor = Color.White;
public int timeDiscover { get; set; }//discover time
public int timeFinish { get; set; } // finish time
public Node()
{
Neighbors = new List<NeighborNode>();
}
public Node(string n, int discover)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
timeDiscover = discover;
}
public Node(string n, NeighborNode e, decimal m)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
this.Neighbors.Add(e);
}
}
class NeighborNode
{
public Node Name { get; set; }
public decimal Miles { get; set; } //Track the miles on the neighbor node
public NeighborNode() { }
public NeighborNode(Node n, decimal m)
{
Name = n;
Miles = m;
}
}
Here is my algorithm:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
Stack<Node> _allCities = new Stack<Node>(); // add all cities into this for examination
Node _nodeToExamine = new Node(); //this is the node we're currently looking at.
decimal _cost = 0;
foreach (var city in graph) // putting these onto a stack for easy manipulation. Probably could have just made this a stack to start
{
_allCities.Push(city);
_algorithmList.Add(new DA(city));
}
_nodeToExamine = _allCities.Pop(); //pop off the first node
while (_allCities.Count != 0) // loop through each city
{
foreach (var neighbor in _nodeToExamine.Neighbors) //loop through each neighbor of the node
{
for (int i = 0; i < _algorithmList.Count; i++) //search the alorithm list for the current neighbor node
{
if (_algorithmList[i].Name.Name == neighbor.Name.Name) //found it
{
for (int j = 0; j < _algorithmList.Count; j++) //check for the cost of the parent node
{
if (_algorithmList[j].Name.Name == _nodeToExamine.Name) //looping through
{
if (_algorithmList[j].Cost != 100000000) //not infinity
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
break;
}
}
_cost = _cost + neighbor.Miles;
if (_algorithmList[i].Cost > _cost) // check to make sure the miles are less (better path)
{
_algorithmList[i].Parent = _nodeToExamine; //set the parent to be the top node
_algorithmList[i].Cost = _cost; // set the weight to be correct
break;
}
}
}
}
_cost = 0;
_nodeToExamine = _allCities.Pop();
}
}
This is what the graph looks like:
The graph list node is essentially
Node -- Neighbor Nodes
So for example:
Node = Olympia, Neighbor Nodes = Lacey and Tacoma
I think the problem is that
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
You do a direct assignment of cost, instead of an addition of old and new cost.
Also, the fact that you do
if (_algorithmList[j].Cost != 100000000) //not infinity
directly before it means that if the cost of the path is infinity, you do the very opposite - you add zero to the cost of the path, making it the least expensive instead of most expensive path.
If you want to check for infinity properly, you have to outright skip taking that path when you inspect its cost, not just skip calculating the cost.
I needed to rewrite the entire algorithm as it wasn't processing correctly:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
DA _nodeToExamine = new DA(); //this is the node we're currently looking at.
bool flag = true; //for exting the while loop later
foreach (var node in graph)
{
_algorithmList.Add(new DA(node));
}
foreach (var children in _algorithmList[0].Name.Neighbors) //just starting at the first node
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
_algorithmList[i].Parent = _algorithmList[0].Name;
_algorithmList[i].Cost = children.Miles;
_algorithmList[0].Complete = true;
}
}
}
while (flag) //loop through the rest to organize
{
_algorithmList = _algorithmList.OrderBy(x => x.Cost).ToList(); //sort by shortest path
for (int i = 0; i < _algorithmList.Count; i++) //loop through each looking for a node that isn't complete
{
if (_algorithmList[i].Complete == false)
{
_nodeToExamine = _algorithmList[i];
break;
}
if (i == 13) //if the counter reaches 13 then we have completed all nodes and should bail out of the loop
flag = false;
}
if (_nodeToExamine.Name.Neighbors.Count == 0) //set any nodes that do not have children to be complete
{
_nodeToExamine.Complete = true;
}
foreach (var children in _nodeToExamine.Name.Neighbors) //loop through the children/neighbors to see if there's one with a shorter path
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
if (_nodeToExamine.Cost + children.Miles < _algorithmList[i].Cost) //found a better path
{
_algorithmList[i].Parent = _nodeToExamine.Name;
_algorithmList[i].Cost = _nodeToExamine.Cost + children.Miles;
}
}
}
_nodeToExamine.Complete = true;
}
}
PrintDijkstraAlgoirthm(_algorithmList);
}
public void PrintDijkstraAlgoirthm(List<DA> _finalList)
{
foreach (var item in _finalList)
{
if (item.Parent != null)
Console.WriteLine("{0} ---> {1}: {2}", item.Parent.Name, item.Name.Name, item.Cost);
}
}
I Have this problem where I can´t load my data into my TreeView but I have to do it manually instead. My static solution looks like this:
public static ObservableCollection<ClassOfDoom> GetAll()
{
ObservableCollection<ClassOfDoom> listToReturn = new ObservableCollection<ClassOfDoom>();
ClassOfDoom treeItem = null;
OUViewModel ouvm = new OUViewModel();
int[] tempOU = ouvm.HierarchyIntOU;
int[] tempP = ouvm.HierarchyIntParent;
treeItem = new ClassOfDoom("Root");
treeItem.cID = tempOU[0];
treeItem.pID = tempP[0];
for (int i = 0; i < ouvm.HierarchyIntParent.Length; i++)
{
if (treeItem.cID == tempP[i])
{
treeItem.ClassOfDooms.Add(new ClassOfDoom(tempOU[i].ToString()));
treeItem.ClassOfDooms.Last().pID = tempP[i];
treeItem.ClassOfDooms.Last().cID = tempOU[i];
for (int i1 = 0; i1 < ouvm.HierarchyIntParent.Length; i1++)
{
if (treeItem.ClassOfDooms.Last().cID == tempP[i1])
{
treeItem.ClassOfDooms.Last().Countries.Add(new ClassOfDoom(tempOU[i1].ToString()));
}
}
}
}
listToReturn.Add(treeItem);
return listToReturn;
}
This works but as you can see it´s only three levels and I wan´t a dynamic amount of levels. If someone wonders my ClassOfDooms list looks like this:
public ObservableCollection<ClassOfDoom> ClassOfDooms
{
get
{
if (classOfDooms == null) classOfDooms = new ObservableCollection<ClassOfDoom>();
return classOfDooms;
}
set { classOfDooms = value; }
}
I want to state again that I have no trouble reading data from my database or anything like that. The TreeView get´s the right information just not all of it.
EDIT: I solved it myself like this:
ClassOfDoom[] asdfDooms = new ClassOfDoom[ouvm.HierarchyIntParent.Length];
for (int i = 0; i < ouvm.HierarchyIntParent.Length; i++)
{
asdfDooms[i] = new ClassOfDoom();
asdfDooms[i].cID = tempOU[i];
asdfDooms[i].pID = tempP[i];
asdfDooms[i].name = tempPersonName + asdfDooms[i].cID.ToString();
}
for (int aint = 0; aint < ouvm.HierarchyIntParent.Length; aint++)
{
for (int i = 0; i < ouvm.HierarchyIntParent.Length; i++)
{
if (asdfDooms[aint].cID == asdfDooms[i].pID)
{
asdfDooms[aint].classOfDooms.Add(asdfDooms[i]);
}
}
}
listToReturn.Add(asdfDooms[0]);
Try implementing the INotifyPropertyChanged interface in conjunction with the HierarchicalDataTemplate control, this should ensure that changes made to the underlying data structure are reflected in the TreeView structure.