I have 1 root node and many child nodes of that root node.
I want to get all of the visible nodes key.
The recursive code block like below;
public void PrintNodesRecursive(UltraTreeNode oParentNode)
{
foreach (UltraTreeNode oSubNode in ultraTree1.Nodes[0].Nodes)
{
MessageBox.Show(oSubNode.Key.ToString());
PrintNodesRecursive(oSubNode);
}
}
private void ultraButton3_Click(object sender, EventArgs e)
{
PrintNodesRecursive(ultraTree1.Nodes[0]);
}
However messagebox always show me '1' value. It doesn't count and endless loop happens.
How can I make it happen?
Try like this;
public void PrintNodesRecursive(UltraTreeNode oParentNode)
{
if (oParentNode.Nodes.Length == 0)
{
return;
}
foreach (UltraTreeNode oSubNode in oParentNode.Nodes)
{
if(oSubNode.Visible)
{
MessageBox.Show(oSubNode.Key.ToString());
}
PrintNodesRecursive(oSubNode);
}
}
Also, put the visible condition in the loop.
You made a simple programming error. This line:
foreach (UltraTreeNode oSubNode in ultraTree1.Nodes[0].Nodes)
should probably be
foreach (UltraTreeNode oSubNode in oParentNode.Nodes)
Otherwise, every recursion step starts again from the top.
Related
private void Invoke()
{
try
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(delegate { Invoke(); }));
}
else
{
removeUc();
}
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
this is my invoke function im using this because it came from another thread sorry im new here.
private void removeUc()
{
foreach (UserControl uc in fPanel.Controls)
{
fPanel.Controls.Remove(uc);
}
}
and this is my remove function, my problem is for example i have 3 usercontrol yet it only remove 2 it always leaves one usercontrol i want to remove all
The general rule for such situations (language-agnostic) is:
If the collection is known to be array or array-like, i.e. accessible by index, and remove shifts indices of all following items:
→ then you just iterate it backwards (with for not foreach).
If the collection is iterable but assumptions about internal data structure cannot be made:
→ then you create a list/array of elements to be removed, then remove them in a second loop (like in apomene's answer).
private void removeUc()
{
var forRemoval = new List<UserControl>();
//Create removal list
foreach (UserControl uc in fPanel.Controls)
{
forRemoval.Add(uc);
}
//remove from fpanel
foreach (UserControl uc in forRemoval)
{
fPanel.Controls.Remove(uc);
}
}
I have a TreeView that contains database objects that are basically folders. I want to be able to click on a "folder" in the tree and have it populate a set of controls with data about that "folder". While this all works fine with the code I've written, the issue is that using the arrow keys on the keyboard to go up and down the folder list will eventually hang the application. My assumption is that the background worker I am using to populate the controls is getting hung up.
I've searched and I can't find anything similar to my issue.
Here's my tree view afterselect code.
private void dmTree_AfterSelect(object sender, TreeViewEventArgs e)
{
object[] tagParts = e.Node.Tag as object[];
SelectedFolderNumber = tagParts[1].ToString();
if (!String.IsNullOrEmpty(SelectedFolderNumber) && SelectedFolderNumber != "0")
{
//update mini profile
if (bgwMiniProfile.IsBusy)
{
bgwMiniProfile.CancelAsync();
}
while (bgwMiniProfile.CancellationPending)
{
Application.DoEvents();
}
bgwMiniProfile.RunWorkerAsync();
while (bgwMiniProfile.IsBusy)
{
Application.DoEvents();
}
securityPanel.DisplayTrusteeList(folderTrustees);
}
}
securityPanel is a user control on the form.
Here is the DisplayTrusteeList code
public void DisplayTrusteeList(List<DocumentTrustee> documentTrustees)
{
try
{
dgvTrustees.Rows.Clear();
foreach (DocumentTrustee dt in documentTrustees)
{
dgvTrustees.Rows.Add(imagePG.Images[(int)dt.TrusteeType], dt.GetFullName(dmLogin), dt.AccessRights);
}
foreach (DataGridViewRow myRow in dgvTrustees.Rows)
{
ValidateRights(int.Parse(myRow.Cells["dmRights"].Value.ToString()), myRow);
}
dgvTrustees.ClearSelection();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "DisplayTrusteeList");
}
}
And here is the background worker:
private void bgwMiniProfile_DoWork(object sender, DoWorkEventArgs e)
{
if (!bgwMiniProfile.CancellationPending)
{
SetText(txtDocNumber, SelectedFolderNumber);
SetText(txtDocName, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "DOCNAME"));
SetText(txtClientId, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "CLIENT_ID"));
SetText(txtClientName, Utility.SetDescription(adminLogin, "CLIENT", txtClientId.Text));
SetText(txtMatterId, Utility.GetProfileValue(adminLogin, SelectedFolderNumber, "MATTER_ID"));
SetText(txtMatterName, Utility.SetDescription(adminLogin, "CLIENT", txtClientId.Text, txtMatterId.Text));
folderTrustees = Utility.GetFolderTrustees(adminLogin, SelectedFolderNumber);
}
else
{
e.Cancel = true;
}
}
I would like to be able to cursor through the tree nodes with the arrow keys and not have the after select code fire until the user lands on a node and stays there for a few seconds. Is that possible?
Thanks and this is my first question. Sorry if the format ins't great. I've used a lot of solutions from here.
I found a better way. Instead of using AfterSelect I'm using NodeMouseClick. This mirrors Windows Explorer functionality. Now the user can cursor up and down the folder tree without any issues. The data to the right will fill in only when they click on the node. This works for me perfectly.
See the linked screenshot below.
In short, I need those little white boxes to disappear - they're supposed to house an image, but there is no image, and so I'd rather they disappear.
I've accomplished this using the follow code:
foreach (ToolStripMenuItem menuItem in mnuMain.Items)
((ToolStripDropDownMenu)menuItem.DropDown).ShowImageMargin = false;
This works for what I guess are the main items, but not the sub-items, as you can see in the picture.
I've tried a few variations on the above code to try and get it to capture everything instead of just the first level items, but no luck.
What am I doing wrong?
http://i.imgur.com/bst1i4v.png
You should do that for sub items too. To do so, you can use this code:
private void Form1_Load(object sender, EventArgs e)
{
SetValuesOnSubItems(this.menuStrip1.Items.OfType<ToolStripMenuItem>().ToList());
}
private void SetValuesOnSubItems(List<ToolStripMenuItem> items)
{
items.ForEach(item =>
{
var dropdown = (ToolStripDropDownMenu)item.DropDown;
if (dropdown != null)
{
dropdown.ShowImageMargin = false;
SetValuesOnSubItems(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
}
});
}
This is a modified version of above. Use:
MainMenuStrip.HideImageMargins();
Because the recursive method performs the intended manipulation, I used overloading to make it clearer what is intended. Pattern matching is used because the above sample will throw an exception, not return null.
public static void HideImageMargins([NotNull] this MenuStrip menuStrip)
{
HideImageMargins(menuStrip.Items.OfType<ToolStripMenuItem>().ToList());
}
private static void HideImageMargins([NotNull] this List<ToolStripMenuItem> toolStripMenuItems)
{
toolStripMenuItems.ForEach(item =>
{
if (!(item.DropDown is ToolStripDropDownMenu dropdown))
{
return;
}
dropdown.ShowImageMargin = false;
HideImageMargins(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
});
}
I've seen lots of posts asking a question similar to this, but none seem to answer the question. I have a TreeView of vendors like this:
Soda
Regular
SmallCan
SmallBottle
Diet
SmallCan
Water
Regular
EcoBottle
I created a context menu that allows the user to rename the selected node, but cannot find a way to enforce that if it makes a duplicate node name, either the change is refused or the node text is reverted to the previous value. This is the context change event and the method to handle the enforcing:
private void contextMenuRename_Click(object sender, System.EventArgs e)
{
restoreNode = treProducts.SelectedNode;
treProducts.LabelEdit = true;
if (!treProducts.SelectedNode.IsEditing)
{
treProducts.SelectedNode.BeginEdit();
}
enforceNoTreeDuplicates();
}
private void enforceNoTreeDuplicates()
{
nodeNames.Clear();
if (treProducts.SelectedNode.Level != 0)
{
foreach (TreeNode node in treProducts.SelectedNode.Parent.Nodes)
{
nodeNames.Add(node.Text);
}
}
else
{
foreach (TreeNode node in treProducts.Nodes)
{
nodeNames.Add(node.Text);
}
}
int countDuplicates = 0;
foreach (string nodeName in nodeNames)
{
if (restoreNode.Text == nodeName)
{
countDuplicates++;
}
if (countDuplicates > 1)
{
treProducts.SelectedNode = restoreNode;
}
}
}
However, the BeginEdit() doesn't seem to run if the enforceNoTreeDuplicates() method is in there. Is there a better way to handle the editing of the selected node or is there something wrong with the enforceNoTreeDuplicates() method?
Generally, you would use the AfterLabelEdit for that, which has an option to cancel the edit:
void treProducts_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
foreach (TreeNode tn in e.Node.Parent.Nodes) {
if (tn.Text == e.Label) {
e.CancelEdit = true;
}
}
}
My point is to display number of all child nodes at StatusStripLabel. My point is that I want StatusStripLabel to get updated everytime number of child nodes get's changed - I'll add or delete some of already exists. First, I placed code in Public Form, but it didn't worked as I expected. After some time I came with idea that actually works: I placed the code inside button method. But after that I realized that I'll need to place it in second place, in case of deleting node. So My question is: is there anything that can make it simplier? If my explanation isn't enough, just tell me, I'll try my best.
Code from Public Form (Because I want that counter to work from the beggining, not after I'll press the button)
childNodeCounter();
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
Method:
public void childNodeCounter()
{
NodeCounter = 0;
foreach (TreeNode RootNode in treeView1.Nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
{
NodeCounter++;
}
}
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
}
Code inside button method:
private void button1_Click(object sender, EventArgs e)
{
NodeCounter = 0;
foreach (TreeNode RootNode in treeView1.Nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
{
NodeCounter++;
}
}
toolStripStatusLabel1.Text = "Number of games in database: " + NodeCounter.ToString();
}
Edit: Thanks to mr. Hans Passant I wrote this and it works very well:
public int childNodeCounter(TreeNodeCollection nodes)
{
int count = 0;
foreach (TreeNode RootNode in nodes)
{
foreach (TreeNode ChildNode in RootNode.Nodes)
count++;
}
return count;
Event handler looks like this:
toolStripStatusLabel1.Text = "Number of games in database: " + childNodeCounter(treeView1.Nodes);
Three tiny optimizations
Rather that iterate the tree yourself, just use ChildNode.Nodes.GetNodeCount
Rather than repeat the same logic in different places, have your button Click events simply call the UpdateNodeCount() method.
The text initializer in the first code fragment is redundant, and can be eliminated: the call to childNodeCounter already does the status label update.
The natural way to traverse a tree structure is by using recursion. That's always a bit hard to reason through but there are lots of resources available. Doing it iteratively is a lot uglier, you have to use a Stack<> to allow you to backtrack again out of a nested node. I'll therefore post the recursion solution:
private static int CountNodes(TreeNodeCollection nodes) {
int count = nodes.Count;
foreach (TreeNode node in nodes) count += CountNodes(node.Nodes);
return count;
}
Then your event handler becomes:
private void button1_Click(object sender, EventArgs e) {
toolStripStatusLabel1.Text = "Number of games in database: " +
CountNodes(treeView1.Nodes);
}
If you're adding and removing "game" nodes to the treeView, you must be having methods like void AddGame(string title) and void RemoveGame(string title) which add/remove (child) nodes - those whose total number you want to count. If I understood well, you want toolStripStatusLabel1.Text to be automatically updated each time the number of child nodes changes. In that case you can add field
private int nodesCount;
to your Form class and have something like this:
void AddGame(string title)
{
if(InvokeRequired)
{
Invoke(new MethodInvoker(delegate() { AddGame(title); }));
}
else
{
AddGameNodeToTreeView(title); // add new game node to desired place in TreeView
nodesCount++; // increase node counter
toolStripStatusLabel1.Text = "Number of games in database: " + nodesCount;
}
}
RemoveGame() would be implemented in the same way (or joined with AddGame() into a single method with one additional argument - bool add). Both methods could be extended if you're adding/removing multiple nodes in which case you'll be passing title array and updating nodesCount accordingly.
The advantage of this approach is that you don't have to count nodes in the tree each time before updating toolStripStatusLabel1.Text. Also, toolStripStatusLabel1.Text is updated automatically, not only when user clicks the button.
Drawback is that nodesCount is somewhat redundant information: total number of nodes of interest is 'hidden' in the treeView. You have to make sure that nodesCount is in sync with the actual number of nodes.