[V8i VBA] While looping through an enumerator, can I delete Enum.Current and replace with another element?

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.

MaryB
MSTN 08.11.09.829

  • Hi Mary,

    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):

    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.

    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.

    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)

    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.

    Or am I thinking too hard?

    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

    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.918
    Power InRoads 08.11.09.918
    OpenRoads 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 Wing
    Senior Systems Analyst

    Answer Verified By: MaryB 

  • Hi Rod,

    a few comments:

    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 ;-)

    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.

    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.

    With regards,

      Jan

  • 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

    Power GeoPak 08.11.09.918
    Power InRoads 08.11.09.918
    OpenRoads Designer 2021 R2

        

  • Hi Mary,

    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.

    With regards,

      Jan

    Answer Verified By: MaryB 

  • Thank you!

    MaryB

    Power GeoPak 08.11.09.918
    Power InRoads 08.11.09.918
    OpenRoads Designer 2021 R2