C# LinkedListNode reference after calling Remove on LinkedList - c#

My question: Hi, does method- LinkedList.Remove(LinkedListNode n) changing references of others elements in LinkedList?
Application is little bit complex so atleast I will try to explain how I got here, but maybe It will be confusing
I have a program which storing references(LinkedListNodes from
LinkedList) into another iterable class. And in some point,
application starts deleting these LinkedListNodes with
method-Remove(LinkedListNode node) and also delete this element from
my class which stores these references. It runs good for a while, but
in some point It will loose one of the reference in my class and I get
null reference(in myNode) when i want to call
LinkeList.AddAfter(myNode, value) with error: “The LinkedList node
does not belong to current LinkedList”.
EDIT:
I was translating notes, etc...
So I am using BinarySearchTree for quick search, LinkedList for normal iterating and QUEUE for deleting old elements.
This is insert in my Tree class:
public Node Insert(DictionaryPair dictionaryPair, LinkedList<DictionaryPair> dictionary)
{
Node currentNode = nodeRoot;
while (true)
{
if (currentNode == null) //if i inserting 1st element
{
nodeRoot = new Node(); //creating node(root)
dictionary.AddFirst(dictionaryPair); //inserting into Linked list on 1st place
nodeRoot.dictionaryNode = dictionary.First; //and taking a reference
return nodeRoot; //end while
}
else if (currentNode.dictionaryNode.Value.CompareTo(dictionaryPair) >= 0)
{ //sending element into left
if (currentNode.left == null) // and is empty
{
currentNode.left = new Node(); //creating new node
currentNode.left.myParent = currentNode; //reference to parent
currentNode.left.dictionaryNode = dictionary.AddBefore(currentNode.dictionaryNode, dictionaryPair);
//and inserting into dictionary (Before) current node and save the refence on it to left
return currentNode.left; //end while
}
else
{ //or shift to left
currentNode = currentNode.left;
}
}
else
{ //sending element into right
if (currentNode.right == null) // is null
{
currentNode.right = new Node(); //create new node
currentNode.right.myParent = currentNode; //reference on parent
currentNode.right.dictionaryNode = dictionary.AddAfter(currentNode.dictionaryNode, dictionaryPair);
//and insert into dictionary (After) current node and save the refence on it to right
return currentNode.right; //endwhile
}
else
{ //or shift to right side
currentNode = currentNode.right;
}
}
}
}
class Node:
public LinkedListNode<DictionaryPair> dictionaryNode;
public Node left;
public Node right;
public Node myParent;
I can call delete method on my Node:
public LinkedListNode<DictionaryPair> DeleteMe()
{
LinkedListNode<DictionaryPair> deletedNode = this.dictionaryNode;
if (this.left == null && this.right == null)
{ //Delete leaf
if(myParent.left == this)
{
myParent.left = null;
}
else // else if(myParent.right == this)
{
myParent.right = null;
}
}
else if (this.left != null && this.right == null)
{
this.right = this.left.right;
this.dictionaryNode = this.left.dictionaryNode;
this.left = this.left.left;
}
else if (this.left == null && this.right != null)
{
this.left = this.right.left;
this.dictionaryNode = this.right.dictionaryNode;
this.right = this.right.right;
}
else
{ //on left and right are tries
Node currentNode = this.left; //throught the left side
bool oneCycle = false; //possibility of not iterating once thought the left side into the right (so it would be left)
while (currentNode.right != null)
{ //searching element most to the right
currentNode = currentNode.right;
oneCycle = true;
}
if (currentNode.left == null)
{ //i am leaf
if (oneCycle)
{
currentNode.myParent.right = null; //deleting refence on me
this.dictionaryNode = currentNode.dictionaryNode; //and change a value
}
else
{
currentNode.myParent.left = null; //deleting refence on me
this.dictionaryNode = currentNode.dictionaryNode; //and change a value
}
}
else
{ //im not leaf
if (oneCycle)
{
currentNode.myParent.right = currentNode.left; //change refence on my tree
this.dictionaryNode = currentNode.dictionaryNode; //and change value
}
else
{
currentNode.myParent.left = currentNode.left; //change refence on my tree
this.dictionaryNode = currentNode.dictionaryNode; //and change value
}
}
}
return deletedNode;
}
This is my main class for working with the arrays MyDictionary, whichs have
private LinkedList<DictionaryPair> data; //for iterating search
private Tree binarySearchTree; //for quick search
private Queue<Node> queue; //references on added nodes
//in binarySearchTree... they are ready to be deleted
private int maxCount;
private bool maxCountReached;
When I am inserting into MyDictionary, I calling this method
public void Insert(DictionaryPair input)
{
if (!maxCountReached)
{
if (queue.Count() >= maxCount)
{
maxCountReached = true;
}
}
if (maxCountReached)
{
data.Remove(queue.Dequeue().DeleteMe());
}
queue.Enqueue(binarySearchTree.Insert(input, data));
}

[...] does method LinkedList.Remove(LinkedListNode n) change references of other[s] elements in LinkedList?
When we look at the source code of the LinkedList.Remove method, we find that the framework does not mess with other elements except for adjusting their prev and next pointers (in order to close the gap caused by the removal, and as per definition of the linked list principle).
Except for the border cases, it is simply
node.next.prev = node.prev;
node.prev.next = node.next;
The object (item in the internal data structure) of other elemenst is not modified by the Remove operation. The object targeted by the Remove operation itself is also not directly affected. As the node is detached from the list, it becomes eligible for garbage collection if no other living objects keep a reference.
The exception you see is generated here:
if ( node.list != this) {
throw new InvalidOperationException(SR.GetString(SR.ExternalLinkedListNode));
}
If this validation fails in AddAfter, it can mean that:
Calling code is attempting to reference an existing node that is not attached to any LinkedList at all, for example a node that was previously removed from the list. In the code you posted, this would be currentNode.dictionaryNode and I'd focus on lines where this is assigned when debugging
Calling code is attempting to reference an existing node that belongs to another instance of LinkedList.

I found a mistake in implementation of DeleteMe method in my BinarySearchTree. I didnt change parents reference of nodes which was under a found currentNode. But thank you for help. Have a great day...

Related

Restricting child element in Enterprise Architect through C# add-in

I am working on C# add-ins in Enterprise Architect to give a restriction to user so that only a particular child element can be added to a specific parent element.
For example if child element A must be dropped on parent element B it is deleted if child element A is dragged and dropped on parent element C. I am using EA_OnPostNewElement method and a delete method for the same and it works fine.
My doubt is, after the user has dropped the child element on the specific parent, after some time he can drag the child element outside the parent element and add it as a child to any other element in the diagram.
Is there a way to add a restriction here by observing the changes made by user on Enterprise architect GUI and bring back the child element to original parent location. Kindly help.
You need to use both EA_OnContextItemChanged and EA_OnNotifyContextItemModified so you can achieve it .
declare a dictonry
public Dictionary<int, int> lstChildElements = new Dictionary<int, int>();
and here is the sample code
public void EA_OnContextItemChanged(EA.Repository Repository, string GUID, EA.ObjectType ot)
{
EA.Element addedElement = Repository.GetElementByGuid(GUID);
if (addedElement == null)
return;
int identifiedParentID = 0;
bool isAvailable = lstChildElements.TryGetValue(addedElement.ElementID, out identifiedParentID);
if (!isAvailable)
{
if (addedElement.Stereotype == "Child" && addedElement.ParentID != 0)
{
EA.Element parentElemnt = Session.Repository.GetElementByID(addedElement.ParentID);
if (parentElemnt != null)
if (parentElemnt.Stereotype == "anyCustomDefined")
lstChildElements.Add(addedElement.ElementID, addedElement.ParentID);
}
}
}
public void EA_OnNotifyContextItemModified(EA.Repository Repository, string GUID, EA.ObjectType ot)
{
EA.Element addedElement = Repository.GetElementByGuid(GUID);
if (addedElement == null)
return;
int identifiedParentID = 0;
bool isAvailable = lstChildElements.TryGetValue(addedElement.ElementID, out identifiedParentID);
if (isAvailable)
{
if (addedElement.Stereotype == "Child" && addedElement.ParentID != 0)
{
EA.Element parentElemnt = Repository.GetElementByID(addedElement.ParentID);
if (parentElemnt != null)
if (parentElemnt.Stereotype != "anyCustomDefined")
{
addedElement.ParentID = identifiedParentID != 0 ? identifiedParentID : addedElement.ParentID;
addedElement.Update();
lstChildElements[addedElement.ElementID] = addedElement.ParentID;
}
}
else if (addedElement.Stereotype == "Child" && addedElement.ParentID == 0)
{
addedElement.ParentID = identifiedParentID;
addedElement.Update();
}
}
}
Hope it helps..!
and for updating in diagram need to reload it.
EA.Diagram activeDiagram = Session.Repository.GetCurrentDiagram();
if (activeDiagram != null)
Session.Repository.ReloadDiagram(activeDiagram.DiagramID);
or
Repository.RefreshOpenDiagrams();
Both the codes can be used for reloading the diagram.
You can use the context events to keep track of the selected element and it's owner.
I'm not sure if the event EA_OnNotifyContextItemModified is being fired when you change the owner of an element.
But even it that is not the case you can verify if it still has the same owner after a new element has been selected.

how to change my Node-Text

i created some symbol without Labels, after dropping my Symbols from Palette, my symbol will automatic be labeled with Text. my problem is that the first Node-Drop from every symbol i´ve created is not labeled, after the second, third, fourth, etc. Node-Drop that node will automatic assigned with Label.
Second requirement i would to know, after dropping my Node, how can i edit my nodeText. by clicking or double-clicking the node.
Here is my Code:
protected void DiagramWebControl1_NodeDropFromPalette(object sender, Syncfusion.Web.UI.WebControls.Diagram.NodeDropFromPaletteEventArgs e)
{
if (e.Node is PathNode || e.Node is Group)
{
PathNode node = e.Node as PathNode;
if (node != null)
{
if (node.FullName == "Prozess Start")
{
node.Name = "Prozess Start";
node.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(node, node.Name));
}
else if (node.FullName == "Prozess")
{
node.Name = "Prozess";
node.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(node, node.Name));
}
}
else
{
Group gnode = e.Node as Group;
if (gnode.FullName == "Organisationseinheit")
{
gnode.Name = "Organisationseinheit";
gnode.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(gnode, gnode.Name));
}
else if (gnode.FullName == "Rolle")
{
gnode.Name = "Rolle";
gnode.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(gnode, gnode.Name));
}
else if (gnode.FullName == "Externe Rolle")
{
gnode.Name = "Externe Rolle";
gnode.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(gnode, gnode.Name));
}
else if (gnode.FullName == "IT-System")
{
TextNode rtxNode = new TextNode("");
rtxNode.Text = "IT-System";
gnode.Labels.Add(new Syncfusion.Windows.Forms.Diagram.Label(gnode, rtxNode.Text));
}
}
}
Since the node’s FullName is generated uniquely by appending the ‘Model’ to the node’s name by the DiagramWebControl, check whether the dropped node’s FullName and the name given in your code are the same one. If you want to add ‘Labels’ to the node based on their names, then use the node’s ‘Name’ property instead of using FullName.
Refer the Syncfusion’s public forum regarding your requirement of editing nodeText in double-clicking the node.
Here's a link.

Select TreeView Node

I've a TreeNode and I need to allow user to select only one child node for parent.
Example:
-Car
---Ferrari
---Lamborghini
---Porsche
-Shoes
---Nike
---Puma
---Adidas
I can select "Ferrari" and "Nike", but not other child in "Car" or "Shoes". How can I make it?
After I do this, I need to concat text of Parent and child like this: Car: Ferrari.
Can you help me?
Regards.
You could handle the BeforeCheck event and clear the siblings checkboxes, e.g. :
private bool skipEvents = false; // this flag to avoid infinite recursion
void treeView1_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
// if is a root (car or shoes), or it's a recursive call, just return
if (skipEvents || e.Node.Parent == null)
return;
skipEvents = true;
foreach (TreeNode sibling in e.Node.Parent.Nodes)
{
// set the other siblings to unchecked
if (sibling != e.Node)
sibling.Checked = false;
}
skipEvents = false;
}
Here's an example to concatenate the parents and childs selected:
public string GetSelectionString()
{
string categorySep = Environment.NewLine;
string parentChildSep = " : ";
StringBuilder sb = new StringBuilder();
foreach (TreeNode root in this.treeView1.Nodes)
{
foreach (TreeNode node in root.Nodes)
{
if (node.Checked)
{
if (sb.Length > 0)
sb.Append(categorySep);
sb.Append(root.Text);
sb.Append(parentChildSep);
sb.Append(node.Text);
break;
}
}
}
return sb.ToString();
}
for example if Ferrari and Puma are selected, it returns a string like this:
Car : Ferrari
Shoes : Puma
EDIT as per comment:
This code does what you ask in your comment (selection/deselection of parents children):
private bool skipEvents = false; // this flag to avoid infinite recursion
void treeView1_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
// if it's a recursive call, just return
if (skipEvents)
return;
skipEvents = true;
// it's a root (e.g. car or shoes)
if (e.Node.Parent == null)
{
// if root node is going to be checked, just cancel the action (i.e. block parent selection)
if (!e.Node.Checked)
{
e.Cancel = true;
}
}
else
{
foreach (TreeNode sibling in e.Node.Parent.Nodes)
{
// set the other siblings to unchecked
if (sibling != e.Node)
sibling.Checked = false;
}
}
skipEvents = false;
}
void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
// if it's a recursive call, just return
if (skipEvents)
return;
this.skipEvents = true;
// it's a root (e.g. car or shoes)
if (e.Node.Parent == null)
{
// root node has been unchecked, so uncheck the children
if (!e.Node.Checked)
{
foreach (TreeNode child in e.Node.Nodes)
child.Checked = false;
}
}
else
{
// if a child node has been checked --> check the parent
// otherwise, uncheck the parent
e.Node.Parent.Checked = e.Node.Checked;
}
this.skipEvents = false;
}
N.B.
TreeView class has a known bug that arises in Vista/Windows7 concerning checkboxes.
Basically if you double-click a checkbox it doesn't lauch any event, so this management will be compromised.
To solve this issue, you can disable double-click by using the class explained in this post instead of TreeView.
If you need one selection per tree, I would suggest using two TreeViews. I would also question whether or not you need to be using TreeViews or whether two ListBoxes or ComboBoxes might be more appropriate.
If you don't know how many 'trees' you'll have, but you do know how deep they are, you could use two or more ListBoxes (or ListViews) to display basically a list of lists:
Categories:
Shoes: Nike
Cars: Ferrari
Fruits: Apple (selected)
Selected Category (Fruits):
Apple (selected)
Orange
Pear
Kiwi

How to add a child node to a dynamically added child node

I am having a treeview on my main form
I have my code from a from to main form is as follows
Buttonclick
StrNode = string.Empty;
StrNode = "Batch" + Append.Batchcnt.ToString() + "(" + strSelectedClassCode + ")";
frmmain.loadFromForm(StrNode, true, strSelectedClassCode);
On my main form i have my code as follows
public void loadFromForm(string strNode, bool bResult, string strStandardClsCode)
{
if (Append.oldbatchcontrol != strNode)
{
if (tvwACH.SelectedNode.Text == "FileHeader")
{
tvwACH.SelectedNode.Nodes.Add(strNode);
}
if (tvwACH.SelectedNode.Text == "BatchHeader")
{
tvwACH.SelectedNode.Nodes.Add(strNode);// After this i have to add another node as a child to that added node and also if a node with particular name exists i would like to write the text with a count value appended
}
}
}
So that my treeview should be as follows
ACH
|->Some.txt
|->Fileheader
|->BatchHeader
|->Batch1
|->Entry1
|->Entry2 and so on // These two should be added dynamically after that Batch1
Use this instead :
public void loadFromForm(string strNode, bool bResult, string strStandardClsCode)
{
if (Append.oldbatchcontrol != strNode)
{
if (tvwACH.SelectedNode.Text == "FileHeader")
{
tvwACH.SelectedNode.Nodes.Add(strNode);
}
if (tvwACH.SelectedNode.Text == "BatchHeader")
{
TreeNode node = tvwACH.SelectedNode.Nodes.Add(strNode,strNode);// After this i have to add another node as a child to that added node and also if a node with particular name exists i would like to write the text with a count value appended
node.Nodes.Add(...);
}
}
}
You usually need a recursive function to build a tree. For example:
private void AddNode(NodeParent, Data)
{
Node oNode;
//Check if this is the first node
if (NodeParent ==null)
{
oNode = new Node(Data.Name);
}
//Step though each child item in the data
foreach(DataItem in Data)
{
//Add the node
this.AddNode(oNode, DataItem);
}
oNode.Nodes.Add(new Node(Data));
}
This code is a rough guide, but it should give you an idea.

Move Node in Tree up or Down

What is the most accurate way to move a node up and down in a treeview. I got a context menu on each node and the selected node should be moved with all its subnodes.
I'm using C# .Net 3.5 WinForms
You can use the following extensions :
public static class Extensions
{
public static void MoveUp(this TreeNode node)
{
TreeNode parent = node.Parent;
TreeView view = node.TreeView;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index > 0)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index - 1, node);
}
}
else if (node.TreeView.Nodes.Contains(node)) //root node
{
int index = view.Nodes.IndexOf(node);
if (index > 0)
{
view.Nodes.RemoveAt(index);
view.Nodes.Insert(index - 1, node);
}
}
}
public static void MoveDown(this TreeNode node)
{
TreeNode parent = node.Parent;
TreeView view = node.TreeView;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index < parent.Nodes.Count -1)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index + 1, node);
}
}
else if (view != null && view.Nodes.Contains(node)) //root node
{
int index = view.Nodes.IndexOf(node);
if (index < view.Nodes.Count - 1)
{
view.Nodes.RemoveAt(index);
view.Nodes.Insert(index + 1, node);
}
}
}
}
Child nodes will follow their parents.
EDIT: Added case that node to move is a root in the TreeView.
While I feel writing this code is a waste of time, given the lack of response to comments by the OP, the least I can do is show how the code example by Le-Savard can be fixed so that muliple clicks of the up or down choice on the context menu ... assuming the context menu is not auto-closed each time and the user is forced to select the same node over and over again ... will do the right thing with the orignally selected node, and not create un-intended side effects :
public static class Extensions
{
public static void MoveUp(this TreeNode node)
{
TreeNode parent = node.Parent;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index > 0)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index - 1, node);
// bw : add this line to restore the originally selected node as selected
node.TreeView.SelectedNode = node;
}
}
}
public static void MoveDown(this TreeNode node)
{
TreeNode parent = node.Parent;
if (parent != null)
{
int index = parent.Nodes.IndexOf(node);
if (index < parent.Nodes.Count - 1)
{
parent.Nodes.RemoveAt(index);
parent.Nodes.Insert(index + 1, node);
// bw : add this line to restore the originally selected node as selected
node.TreeView.SelectedNode = node;
}
}
}
}
Of course this fix, still does not address the fact that in the example code that multiple root nodes cannot be moved (since they are 'parentless) : that's easiliy fixable.
Nor does it address the more interesting case where moving up a top child-node means you make some interpretation of where that "promoted" child code should go : exactly the same "strategic choice" is involved where you "move down" the last child node of a parent node and are thus required to decide where it should go. In Dynami Le-Savard's code : these cases are just ignored.
However, it is a design-choice to restrict child node from only being moved within their parent nodes Nodes collection : a design choice that may be perfectly suitable for one solution.
Similarly, it is a design choice to force a user to select a node and context-click to get a context menu that allows a choice of moving up or down every single time they want to move it : that's not a design choice I'd make : I'd be using drag-and-drop here or buttons that allow repeated rapid-fire relocation of any selected node anywhere in the tree.
By the way I like Dynami Le-Savard's use of extensions here.
Here's a solution that allows you to drag & drop nodes to wherever you want. To move a node to the same level as another node, just hold down shift when dropping the node. This is a really easy way to go compared to the alternatives and their potential problems. Example was written with a more recent version of .Net (4.5).
Note: Be sure and AllowDrop=true on the treeview control otherwise you can't drop nodes.
/// <summary>
/// Handle user dragging nodes in treeview
/// </summary>
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
/// <summary>
/// Handle user dragging node into another node
/// </summary>
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
/// <summary>
/// Handle user dropping a dragged node onto another node
/// </summary>
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
// Retrieve the client coordinates of the drop location.
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
// Retrieve the node that was dragged.
TreeNode draggedNode = e.Data.GetData(typeof(TreeNode));
// Sanity check
if (draggedNode == null)
{
return;
}
// Retrieve the node at the drop location.
TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
// Did the user drop the node
if (targetNode == null)
{
draggedNode.Remove();
treeView1.Nodes.Add(draggedNode);
draggedNode.Expand();
}
else
{
TreeNode parentNode = targetNode;
// Confirm that the node at the drop location is not
// the dragged node and that target node isn't null
// (for example if you drag outside the control)
if (!draggedNode.Equals(targetNode) && targetNode != null)
{
bool canDrop = true;
while (canDrop && (parentNode != null))
{
canDrop = !Object.ReferenceEquals(draggedNode, parentNode);
parentNode = parentNode.Parent;
}
if (canDrop)
{
// Have to remove nodes before you can move them.
draggedNode.Remove();
// Is the user holding down shift?
if (e.KeyState == 4)
{
// Is the targets parent node null?
if (targetNode.Parent == null)
{
// The target node has no parent. That means
// the target node is at the root level. We'll
// insert the node at the root level below the
// target node.
treeView1.Nodes.Insert(targetNode.Index + 1, draggedNode);
}
else
{
// The target node has a valid parent so we'll
// drop the node into it's index.
targetNode.Parent.Nodes.Insert(targetNode.Index + 1, draggedNode);
}
}
else
{
targetNode.Nodes.Add(draggedNode);
}
targetNode.Expand();
}
}
}
// Optional: The following lines are an example of how you might
// provide a better experience by highlighting and displaying the
// content of the dropped node.
// treeView1.SelectedNode = draggedNode;
// NavigateToNodeContent(draggedNode.Tag);
}

Categories