Again, let me ask before I go too far down this road.
I've been able to convert all my text nodes with fields to single-line text nodes. Works great (Thanks to Jan!)
So for thinking...
Can I loop through an enumerator of single line text nodes, and change them to text elements?This could be done a few different ways...My concern is that something weird would go on if I were While...Wend-ing through my enumerator, if I do something to Enumerator.Current to change the element type, does that mess up the enumerator process at all?I mean, if Enumerator.Current is a text node, but then I Drop it to a text element, does that have any effect on what has been or is enumerated? The text element didn't exist when the enumerator was put together, and the text node that was part of the enumerator doesn't exist anymore...
Or am I thinking too hard?Thank you.
MaryBMSTN 08.11.09.829
Hi Mary,
MaryB said:Or am I thinking too hard?
Hmmm ... yes ... and no ;-)
In fact, it's very good and seriously important question, usually VBA "I think I am developer" people don't ask it, but they should. To answer it fully would require pretty lenghty explanation, moreover you asked for two different things, so briefly (with simpler solution at the end):
MaryB said:While looping through an enumerator, can I delete Enum.Current and replace with another element?
No, you cannot change current enumerated element type (replace one element by another).
In some situations (e.g. when you enumerate ActiveModelReference elements) it's possible to modify elements properties during the enumeration (including removal), but I think it's quite bad idea for a variety of reasons and you sould be aware of consequences. In other situations (like to enumerate complex elements content) it's not possible and API provides other methods how to replace elements inside parent element.
Just for curiosity, in many languages enumarable collections are read-only, because to allow object modifications often leads, as you correctly expect, to dangerous situations.
MaryB said:Can I loop through an enumerator of single line text nodes, and change them to text elements?
You cannot change element type, no way. It's alwyas about to remove old element and to create a new one and to add it to a model. It means even when you will enumerate text node content, you cannot change the text nodes, but you have to create new text elements.
MaryB said:if I do something to Enumerator.Current to change the element type, does that mess up the enumerator process at all?
No, because it's not possible to change element type ;-)
When you will enumerate elements in model and will delete the current element (which has to be done as the last operation in current loop), it invalidates this element and everything is ok. When you will "change element type" using your code or calling MicroStation function or tool, this operation will include the element removal (fine for enumeration) and adding a new element to the model (which maybe will be enumerated later also when it fits criteria)
MaryB said:The text element didn't exist when the enumerator was put together, and the text node that was part of the enumerator doesn't exist anymore...
In this specific situation it's fine, VBA API allows it. Of course you cannot drop an element and do someting with it later, any modification should be the last operation in the loop.
I think there is a simpler solution (but based on the same approach, so your original idea is not wrong):
Dim esc As New ElementScanCriteria esc.ExcludeAllTypes esc.IncludeType msdElementTypeTextNode Dim ee As ElementEnumerator Set ee = ActiveModelReference.Scan(esc) ActiveModelReference.UnselectAllElements Do While ee.MoveNext Dim tn As TextNodeElement Set tn = ee.Current.AsTextNodeElement If (tn.TextLinesCount = 1) Then ActiveModelReference.SelectElement tn End If Loop CadInputQueue.SendKeyin "drop complex" CadInputQueue.SendKeyin "xy=#,#,#" ActiveModelReference.UnselectAllElements
The code fings all text nodes with one line (so there is only one text inside), adds the elements to selection set and use key-in to drop these nodes, which means they will be converted to normal text elements.
With regards,
Jan
Bentley Accredited Developer: iTwin Platform - AssociateLabyrinth Technology | dev.notes() | cad.point
Answer Verified By: MaryB
Wonderful!
I didn't know I could create an enumerator, then sift through that with other criteria to build a selection set. That could be useful for a couple of other things...And I often forget that I can just send key-in commands. I always try to look at the programmatic way to do something, but you're right - that's exactly what I'm trying to do, so why get too complicated when CadInputQueue will get the job done.
Thank you again, Jan.MaryB
MaryB
Power GeoPak 08.11.09.918Power InRoads 08.11.09.918OpenRoads Designer 2021 R2
When using an enumerator to loop through elements treat the elements as read only. The enumerator is dynamic, so that modifying an element can reorder the elements causing some to be processed twice while others are not processed at all.
Depending on the modification you want to do adding the elements to a selection set as Jan showed is one way. You can also use the enumerator's BuildArrayFromContents method to create an element array.
The link below shows a tip on how this is done.
https://envisioncad.com/microstation-element-enumerator/
Rod WingSenior Systems Analyst
Hi Rod,
a few comments:
Rod Wing said:When using an enumerator to loop through elements treat the elements as read only.
when talking about MicroStation VBA (and not other languages where enumerable objects are more strict), to treat element enumerator as read-only is recommendation, but not requirement (for some objects like complex element it's always readonly, for other's like ModelReference not). But I agree, when oneself is not sure how exactly enumeration mechanism works, to do not change anything during enumeration is a good start ;-)
Rod Wing said:The enumerator is dynamic, so that modifying an element can reorder the elements causing some to be processed twice while others are not processed
The first statement is correct, when element are changed, they can be enumerated twice, but second is incorrect: When an element passes scan criteria, it's always enumerated. If you have any code + data that works in a different way, please share it.
Rod Wing said:You can also use the enumerator's BuildArrayFromContents method to create an element array.
Yes .. an no ... beucase this advice is can be quite dangerous in not skilled hands and leads always to bad architecture and inefficient code. Whenever the array is created, it increases memroy consumptopn and decreases performance. It's not visible problem when there are just hundreds or thousands elements, but when the scan criteria is wide and fuzzy, so the most of elements are enumerated, the array represents in memory copies of all references to these elements and when they are millions, the memory allocation grows rapidly.
As far as I remember, in every cases when I saw BuildArrayFromContents used, an alternative implementation without it existed.
What might be some of the alternative implementations to build array from contents?
I know there was your use of the enumerator to add to a selection set. Are there others?
MaryB said:What might be some of the alternative implementations to build array from contents?
usually, when you start to think about static array of element references, it's a proof (of course exceptions exist) that your algorithm or application architecture is wrong. The most of problems can be solved enumerating elements collection directly.
When you need to modify an element and the modification will change the element size, so it will be rewritten at the end of the file, an alternative solution is to remember current end of the file and to test whether enumerated element's position is after the original end.
Every solution and algorithm has to evaluated in a context of required performance, number of processed elements, memory consumption and other aspects (e.g. used too, because VBA is pretty bad environment to implement more complex algorithm and data structures efficiently). So when you are sure there will be always just a few elements, to build static array of elements is perfectly fine. When it cannot be assured, the same approach is bad ones.
BTW To add elements to selection set is also bad, because it's terribly slow (it has been discussed in this forum several times). But I assume there will be not millions of text nodes in one model, so it's acceptable issue. Alternatively you can do the conversion (drop text node) directly in enumaration loop. Because text node is deleted and is not used anymore, it will not cause any problem.
Thank you!