Delete step in scenario in Add-in for Enterprise Architect - c#

I have problem with deleting steps from scenario in Add-in for Enterprise Architect
I want delete empty steps of scenario from element, but its not working, this "deleted" steps exists in this scenario.
Where is mistake in my code?
short esCnt = element.Scenarios.Count;
for (short esIdx = (short)(esCnt - 1); esIdx >= 0; --esIdx)
{
EA.IDualScenario es = element.Scenarios.GetAt(esIdx);
short essCnt = es.Steps.Count;
for (short essIdx = (short)(essCnt - 1); essIdx >= 0; --essIdx)
{
EA.IDualScenarioStep ess = es.Steps.GetAt(essIdx);
if (ess.Name.Trim().Length == 0 &&
ess.Uses.Trim().Length == 0 &&
ess.Results.Trim().Length == 0)
{
//1. section
es.Steps.Delete(essIdx);
ess.Update();
}
}
//2. section
es.Update();
}
Do you have any ideas?

Looks like off-by-one. The Collection index is zero-based, but the scenario step numbering in the GUI, as reflected in ScenarioStep.Pos, starts from 1. So you might in fact be deleting the wrong steps.
To be on the safe side, you shouldn't use a foreach loop when you're making changes to the collection you're looping over, but a reverse for loop:
int nrScenarios = element.Scenarios.Count;
for (int scenIx = nrScenarios - 1; scenIx >= 0; --scenIx) {
Scenario scenario = element.Scenarios.GetAt(scenIx);
int nrSteps = scenario.Steps.Count;
for (int stepIx = nrSteps - 1; stepIx >= 0; --stepIx) {
In this case, not as important in the outer loop as in the inner, since that's the collection you're manipulating.
Other than that, you shouldn't need to call es.Update() at all, and element.Scenarios.Refresh() should be called outside the inner loop.
Finally, are you sure that Step.Name is actually empty? I'm unable to create steps with empty names ("Action") in the GUI, but you might have been able to do it through the API.

I think the problem lies in the Update() calls.
The EA.Collection.DeleteAt() will immediately delete the element from the database (but not from the collection in memory). If however you call Update() on the object you just created I think it will recreate it, possibly with a new sequence number; which explains why the deleted steps are now "moved" to the end.
Try removing the Update() call and see if that helps.

Related

How to skip over deleted items in a Transform inside a loop?

I have code which loops through a Transform and sends each GameObject in said transform to a waypoint. This bit works fine.
This issue however comes when I delete a GameObject inside the transform using Collision. The objects indexed in the Transform after the one that gets deleted get thrown off the waypoint path. (The ones indexed before the deleted one stay on).
My question is how can I get my ForLoop to skip over the deleted elements in the Transform so that the program doesn't "break" as such.
I gather that there is probably a rather simple solution to this but I did look online and there is very few articles on this.
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
(Breaks on the first targets[i] line)
Thank you for any help!
Note: The objects will not necessarily be deleted in order.
Your question seems to boil down to this line:
how can I get my ForLoop to skip over the deleted elements in the Transform
The answer would be:
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
if (targets[i] == null) continue;
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Note the first conditional in the for loop, checking whether an element in the array is null. The overloaded equality == not only checks if an item is null, but also if the underlying managed object is set for removal as well.
We can’t see a big portion of your code, but you suggest you’re getting your array from the Transform, which if done this frame, might, for a small window of time, still contain items that have been marked for destruction. But the last comment is conjecture.

Something working on most systems but not everywhere c#

I have a console application in C# for Windows and RPi (Mono). I make a filelist and store it in an array. Then there are two files which I don't want in that list, so I want to remove those from the list. This works on most systems where the application runs (Windows and RPi's alike) but there is one RPi - so far - where it refuses to delete one of those files from the list. As if it cannot find the file (because I don't get the message within the if-statement). I am looking for possible causes. Any suggestions?
MonthfileList = Directory.GetFiles("data/", "*log.txt");
for (int i=0; i<MonthfileList.Length; i++)
{
if (MonthfileList[i].Contains("alltimelog") )
{
Sup.LogDebugMessage(message: $"MonthfileList removing: {MonthfileList[i]}");
var foos = new List<string>(MonthfileList);
foos.RemoveAt(i);
MonthfileList = foos.ToArray();
}
}
The stated problem could happen if you have two files with the required string stored in your array MonthFileList in two adiacent indexes.
In this case, the logic inside the loop causes the second file to be evaluated.
Suppose you have an array of 5 elements (index 0-4) where at index 2 and 3 there is a file with the searched word.
When the first index is evaluated (2) the file is removed from the list, then you rebuild the array without the removed file, but this has a secondary effect. The file that was at index 3 is now at index 2 and your indexer that was at 2 is incremented to 3, thus the old element, now at index 2, is not evaluated and remains into the array.
Fix is simple. loop backwards
// Invert logic, so you don't need a conversion list/array each time you find a match
var foos = new List<string>(MonthfileList);
for (int i=foos.Count - 1; i >= 0; i--)
{
if (foos[i].Contains("alltimelog") )
{
Sup.LogDebugMessage(message: $"MonthfileList removing: {foos[i]}");
foos.RemoveAt(i);
}
}
MonthfileList = foos.ToArray();
#Steve made the correct hint. I changed the code with i-- to account for the missing entry:
MonthfileList = Directory.GetFiles("data/", "*log.txt");
for (int i=0; i<MonthfileList.Length; i++)
{
if (MonthfileList[i].Contains("alltimelog") )
{
Sup.LogDebugMessage(message: $"MonthfileList removing: {MonthfileList[i]}");
var foos = new List<string>(MonthfileList);
foos.RemoveAt(i--);
MonthfileList = foos.ToArray();
}
}

Increment through a list on a button list

I've stored a list of colors in my program. I am after an object in my scene to one of the colors in the list. So far, I have done the followings:
if(Input.GetKeyDown(KeyCode.O))
{
for(int i = 0; i < claddingColor.Count; i++)
{
claddingMaterial.color = claddingColor[i];
}
}
This isn't working due to a reason I know (and you can probably spot) but I lack to the verbal fortitude to write it down.
As opposed to have a multiple lines of the following:
claddingMaterial.color = claddingColor[0];
Each tied to different buttons, I like a way I can emulate the above but tie it to a single button press. Thus, if I hit the 0 button 5 times, it will loop through each color stored in the list. If I hit it for a sixth time, it will go back to the first color in the list.
Could someone please help me implement this? Or point me to something that I may learn how to do it for myself?
Define LastColor property as class member:
int LastColor;
In your function use modulo
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[(LastColor++) % claddingColor.Count];
}
Note: Depending on the type of claddingColor use Count for a List or Length for Array.
You won't need a for loop
int lastStep = 0;
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[lastStep++];
if (lastStep == claddingColor.Count)
lastStep = 0;
}

Clearing a list using a for loop

I'm making a Black Jack game, and at the start of every new round I need to clear the list of cards that represents the Player's and the Dealer's hands. I used this to do so:
public void ClearPlayerHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
PlayerHand.Remove(PlayerHand[i]);
}
}
Problem is I always seem to be left with one card left in the list, or I receive an out of bounds error, no matter how I change the value of i, what is the best method of removing all the elements from the PlayerHand?
If your collection PlayerHand implements ICollection<T> you can just call the .Clear() method.
A common implementation of this interface is List<T>.
If you do want to clear a List<T> via a for loop, you should use a reverse for loop. The reason for this is that as you remove an item from the list, it will shift all the index's down one, and you could easy run into index out of bounds exceptions.
An example of this would be:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
The other answers are right: use Clear.
But, if you wanted to do this with a loop and Remove calls, here's how you would do it:
for(int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
Reversing the direction of the iteration is the real trick.
This is the best/easiest way to do it.
PlayerHand.Clear();
Reason for out of bounds
As for why you are receiving the out of bounds exception, it's happening because you're removing elements from the list but continually counting up. You would want the last operation to remove i = 0 but it keeps counting.
Say PlayerHand has 3 items in it, the following occurs:
i = 0
remove PlayerHand[0] (it now contains 2 elements)
i = 1
remove PlayerHand[1] (it now contains 1 element)
i = 2
remove PlayerHand[2] (this throws an exception as only PlayerHand[0] exists)
Normally you would count backwards in this case:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
Alternatively, you can consider using data binding and then you should update the ItemSource, instead of directly manipulating the listbox or listview items.
List<T> SomeSource=...
PlayHand.ItemSource=SomeSource;
SomeSource.Clear();
Another suggested approach beside Clear method, you can also use RemoveAll to either remove all or part of list
// Remove all items
PlayerHand.RemoveAll(x => true);
// Remove part of list
PlayerHand.RemoveAll(x => ConditionMethod(x));

Iterating over an array using index and assigning values depending on index

I was facing this problem earlier today, and since I could not find a satisfactory solution, I decided to change my class design, and have seperate properties such as Tag 1, Tag 2, Tag 3 etc.
My main problem is the fact that I need to bind a grid to an object that contains a list among other properties and I need to show each item in the list as a separate column which I am unable to do. Hence I am resorting to declaring variables separately. Original question is here...
Now, I'm facing one of the most common design problem that probably every programmer has at some point of time. Here is the code to demonstrate it,
for (int i = 0; i < tags.Length; ++i) // Length not known here.
{
if(i==0){
tag1 = tags[0];
}
else if(i == 1){
tag2 = tags[1];
}
else if(i == 2){
tag3 = tags[2];
}
....
}
Here tags is a string array.
I was wondering if there is a more elegant way to do this. Another thing to note is that the efficiency of this loop decreases as it progresses, since with more iterations it has to check more conditions. If we could remove a condition after it had become true once it would speed up each iteration since we know that each condition will become true only once in all the iterations
Moved answer about DataGridView and using ComponentModel to the correct question:
Displaying a list of object containing a list in a grid view
Briefing
The DataGridView controll supports the ComponentModel namespace so that you can create classes that appear to have properties that don't exist. It is the same mechanism the PropertyGrid uses.
The sample code is in this answer of that question:
https://stackoverflow.com/a/13078735/195417
OLD ANSWER
This was my previous answer, when I didn't realize the real question was about the DataGridView control.
Isn't this the same as setting the values directly:
this.tag1 = tags[0];
this.tag2 = tags[1];
this.tag3 = tags[2];
EDIT: as you sayd you don't know how many variables will be needed, then you need only one, and that is a list:
var list = new List<string>();
for (int i = 0; i < tags.Length; ++i)
{
list.add(tags[i]);
}
If all you want is to copy all values, you can even do this:
var list = new List<string>(tags);
Tell me whether this is what you want or not... maybe I have misunderstood the question.
The whole loop is pointless. But unless the tags array length is always going to be the same, you have to be sure not to go out of bounds...
if(tags.Length >= 1) this.tag1 = tags[0];
if(tags.Length >= 2) this.tag2 = tags[1];
if(tags.Length >= 3) this.tag3 = tags[2];
if(tags.Length >= 4) this.tag4 = tags[3];
if(tags.Length >= 5) this.tag5 = tags[4];
... so on for however many this.tag# you have.
This is essentially the same:
for(int index = 0; index < tags.Length[]; index++){
switch(index){
case 0:
tag1 = tags[0];
break;
// And so on
}
}

Categories