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

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.

Related

UWP VisualTreeHelper Wrong Parameter Reference

I want to modify the content in a TabItem of a TabView. And that TabItem uses DataTemplate.
When I am trying to access the children of that item like the following:
var container = tabview.ContainerFromIndex(tabview.SelectedIndex);
int count = VisualTreeHelper.GetChildrenCount(container);
I got the ArgumentException: Wrong Parameter Reference on the second line. How should I use VisualTreeHelper to modify it?
Here is a easy method:
public static T GetChildObject<T>(DependencyObject obj, string name) where T : FrameworkElement
{
DependencyObject child = null;
T grandChild = null;
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
{
return (T)child;
}
else
{
grandChild = GetChildObject<T>(child, name);
}
if (grandChild != null)
{
return grandChild;
}
}
return null;
}
From your description, you can already get the container of the target element. Let's assume that the element you need is named TargetEle and the type is TextBlock. You can write it like this:
var target = GetChildObject<TextBlock>(container,"TargetEle");
Update
I tested your code and found that you didn't capture the events loaded by the page.
In fact, the SelectionChanged event is fired when the TabView is just created, but the visual tree is not loaded yet, and you can't get the content from it through the code. You can create an IsLoaded property in the page, set it to True when Page Loaded, and determine this property in the SelectionChanged time.
Only when it is True, proceed to the next step.

C# LinkedListNode reference after calling Remove on LinkedList

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...

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.

Drop Item into Specific Index in ListView in WPF C#

I have a list of files in a ListView in WPF. Users can drag files onto the list view, and right now they are just appended to the end of the list. Is it possible to insert the file into the ListView right where the user dropped it?
WPF isn't really designed to be used that way. While you can brute force add ListViewItem's directly to the ListView, the way it's really supposed to work is that you have a collection of some kind (ObservableCollection<FileInfo> would work well) and bind the ListView's ItemsSource property to that collection.
Then the answer is simple. Instead of the Add method, you use the Insert method of the collection which takes an index.
As for finding which ListViewItem the mouse event occurred over, you could use the VisualTreeHelper.HitTest method.
From my point of view it is little tricky when I used the templated item. I have fight with it little bit. I am sharing my usecase which works with DraggableListBox. But I suppose the same solution works with ListBox control.
As the first I created the dependency object extension which is able to provide me ListItem element:
public static class WpfDomHelper
{
public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
}
Then I implemented Drop logic which inserts(adds) item according specific Drop Y position of destination ListBoxItems:
private void Grid_Drop(object sender, DragEventArgs e)
{
int dropIndex = -1; // default position directong to add() call
// checking drop destination position
Point pt = e.GetPosition((UIElement)sender);
HitTestResult result = VisualTreeHelper.HitTest(this, pt);
if (result != null && result.VisualHit != null)
{
// checking the object behin the drop position (Item type depend)
var theOne = result.VisualHit.FindParent<Microsoft.TeamFoundation.Controls.WPF.DraggableListBoxItem>();
// identifiing the position according bound view model (context of item)
if (theOne != null)
{
//identifing the position of drop within the item
var itemCenterPosY = theOne.ActualHeight / 2;
var dropPosInItemPos = e.GetPosition(theOne);
// geting the index
var itemIndex = tasksListBox.Items.IndexOf(theOne.Content);
// decission if insert before or below
if (dropPosInItemPos.Y > itemCenterPosY)
{ // when drag is gropped in second half the item is inserted bellow
itemIndex = itemIndex + 1;
}
dropIndex = itemIndex;
}
}
.... here create the item .....
if (dropIndex < 0)
ViewModel.Items.Add(item);
else
ViewModel.Items.Insert(dropIndex, item);
e.Handled = true;
}
So this solution works with my template DraggableListBoxView, I suppose the same solution must work with standard ListBoxView. Good Luck
You can do this. It takes a bit of work, but it can be done. There are a couple demos out there, here is one on CodeProject. This particular one is by the wpf master known as Josh Smith. It's probably not exactly what you are looking for, but it should be pretty darn close.

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