Fill in Data fields in design file

Hi Guys,

I'm in the middle of transferring to Microstation Connect Version 13, and coming from Microstation V8i (Select series 4). Very busy transferring al the VBA from old to new, but running in to a problem. I have a Function to fill in the textblock in the drawing, which works perfect on V8i, but gives me a type mismatch on Connect, on this line :

Set ele = Pos.GetElementByID(idProjectnummer)

Complete function is :

Public Function WelkeID() As String
Dim ele As TextElement
Dim idProjectnummer As DLong
Dim Pos As ModelReference
idProjectnummer = DLongFromString("25428")
    'On Error GoTo Foutje
    Set Pos = ActiveDesignFile.Models("Default")
    Set ele = Pos.GetElementByID(idProjectnummer)
    WelkeID = "Nieuw"
    Set ele = Nothing
Exit Function
    
Foutje:
WelkeID = "Oud"
Set ele = Nothing
End Function

Is this way of programming no longer valid in Microstation Connect?

Thanks already for the help,

Leo.

Parents
  • Hi Leo,

    similarly to Jon I have also a few comments.

    Complete function is :

    Honestly, in my opinion the function is badly written. When I reformat it to something that makes more sense to me and add comments:

    Public Function WelkeID() As String
        'On Error GoTo Foutje                           ' Error is generated here only when code is wrong, so why to use OnError?
        
        Dim idProjectnummer As DLong
        idProjectnummer = DLongFromString("25428")      ' Why id created from string?
                                                        ' Do you know the element with ID 25428 exists?
        
        Dim Pos As ModelReference
        Set Pos = ActiveDesignFile.Models("Default")    ' Are you sure a mode "Default" exists?
        
        Dim ele As TextElement
        Set ele = Pos.GetElementByID(idProjectnummer)   ' How you can know that element is text?
        
        WelkeID = "Nieuw"
    Exit Function

    But it still does not answer my main question: What is an intention of the function? To find whether element id 25428 exists in default model?

    but gives me a type mismatch on Connect, on this line :

    It's why I asked in the code above whether you are sure the element exists and when it exists, that it's text.

    Both ideas are wrong in my case: Element should never be identified explicitly by its id (something else is to obtain id dynamically using e.g. scanning) and without further test code, it's risky to expect element type (assign element o TextElement variable).

    Is this way of programming no longer valid in Microstation Connect?

    My experience and how VBA API was migrated to CE is similar to what Jon wrote: Typically VBA code runs without modifications. But I would like to change it to that typically good VBA code runs fine. Problems appears often when code written incorrectly, running fine in V8, is ported to CE, because something tiny has changed in API or inside MicroStation engine.

    With regards,

      Jan

  • Hi Jan and Jon,

    I' m quite aware of the possibility the code is written a bit poorly, but I can't take the credit for that, since it's made by one of my predecessors for op to 15 years ago. But al that time it worked.

    When I go through the code it code it does seems overly complicated,  so I do plan to optimize the code, but for now my priority is to get it working.

    I'll try to explain what the code does, or should do.

    This code is used to create a new drawing file from a seed file.

    The seed file contains two models, one is Default, the other DefaultPosities. We have an Userform for the user to enter the text that has to go in the Title block.  which is Projectnumber (idProjectnummer) project name , client name and Calculation Number, User and date, when a Project is started.User fills in project number, name, client name, and calculation number, in a textbox of the userform, after a button press there are subs that check if file already exists and such.

    Then the code arrived at the above part. This part should check if we are dealing with a New project ( "Nieuw" ), Which is when "25428"is found,  or an existing ( "Oud" ) When "25428"isn't found.

    When it's a new one Variable "WelkeID" is set to NEW. when not new to old. I have no Idea why this is done with an error, and also find this rather sloppy.

    Project always contains one or more positions, which have a Name and position number. These are filled in on another userform, and stored in model DefaultPosities. But this is al done further along in the code.

    When I comment out the line " Set ele = Pos.GetElementByID(idProjectnummer) " I get the same error in the next sub, where the actual data should be entered to the title block

    I put a watch on the "idprojectnummer", see screenshot:

    I checked the ID numbers, they are all existing and correct, Model Default exists, and there is only text to insert.

    The sub the error is in now:

    Public Sub WijzigDefaultTitelbalk()
    Dim ele As TextElement
    Dim idProjectnummer As DLong
    Dim idCalculatienummer As DLong
    Dim idOpdrachtgever As DLong
    Dim idProject As DLong
    Dim model As ModelReference
    
    Set model = ActiveDesignFile.Models("Default")
    
    If WelkeID() = "Nieuw" Then
        idProjectnummer = DLongFromString("25428")
        idCalculatienummer = DLongFromString("25429")
        idOpdrachtgever = DLongFromString("25425")
        idProject = DLongFromString("25424")
        idBenaming = DLongFromString("25401")
        idPositienummer = DLongFromString("25430")
    Else
        idOpdrachtgever = DLongFromString("325105")
        idBenaming = DLongFromString("325104")
        idProject = DLongFromString("325103")
        idProjectnummer = DLongFromString("325106")
        idCalculatienummer = DLongFromString("325107")
        idPositienummer = DLongFromString("325100")
    End If
    
    
    If frmNieuwProject.txtProject.TextLength >= 9 Then
        If frmNieuwProject.txtCalculatie.TextLength = 10 Then 'was 9 140225
            Set ele = model.GetElementByID(idProjectnummer)
            ele.IsLocked = False
            ele.Text = frmNieuwProject.txtProject.Value
            ele.Rewrite
            ele.IsLocked = True
            Set ele = Nothing
                        
            Set ele = model.GetElementByID(idCalculatienummer)
            ele.IsLocked = False
            ele.Text = frmNieuwProject.txtCalculatie.Value
            ele.Rewrite
            ele.IsLocked = True
            Set ele = Nothing
    
            Set ele = model.GetElementByID(idOpdrachtgever)
            ele.IsLocked = False
            ele.Text = frmNieuwProject.txtOpdrachtgever.Value
            ele.Rewrite
            ele.IsLocked = True
            Set ele = Nothing
    
            Set ele = model.GetElementByID(idProject)
            ele.IsLocked = False
            ele.Text = frmNieuwProject.txtOmschrijving.Value
            ele.Rewrite
            ele.IsLocked = True
            Set ele = Nothing
        End If
    End If
    
    End Sub

    the error now is on Line 30. So what can this be?

    Thanks Leo

  • the error now is on Line 30
    Set ele = model.GetElementByID(idProjectnummer)

    That element ID is invalid in that DGN model.  We can't see which model is the active model.  Add some debug statements...

    Debug.Print "Active model: " & model.Name
    Debug.Print "Get element from ID " & DLongToString (idProjectnummer)
    Set ele = model.GetElementByID(idProjectnummer)
    Debug.Assert Not ele Is Nothing
    

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    I entered the debug statements as requested.

    Immediate windows gives back:

    Active model: Default
    Get element from ID 25428

    Looks good to me.

    The debug.Assert line never runs, since the error happens in the Set ele line.

    Thanks,

    Leo

  • Get element from ID 25428

    What type of element is ID 25428?

    Can you change ele to be Element (not TextElement) and to debug what is its type?

    With regards,

      Jan

  • the error happens in the Set ele line

    Try this...

    Debug.Print "Active model: " & model.Name
    Debug.Print "Get element from ID " & DLongToString (idProjectnummer)
    Dim oRaw As Element
    Set oRaw = model.GetElementByID(idProjectnummer)
    Debug.Assert Not oRaw Is Nothing
    Set ele = oRaw.AsTextElement
    Debug.Assert Not ele Is Nothing

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jan,

    I'm not sure how to do what you suggested, but when I change the definition from

    Dim ele As TextElement

    to

    Dim ele As Element

    I get a compile error: Method or data member not found on Line:

            ele.Text = frmNieuwProject.txtProject.Value

  • Hi Jon,

    I inserted the line as suggested, the piece now looks like:

    If frmNieuwProject.txtProject.TextLength >= 9 Then
        If frmNieuwProject.txtCalculatie.TextLength = 10 Then 'was 9 140225
                Debug.Print "Active model: " & model.Name
                Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
                Dim oRaw As Element
                    Set oRaw = model.GetElementByID(idProjectnummer)
                    Debug.Assert Not oRaw Is Nothing
                    Set ele = oRaw.AsTextElement
                    Debug.Assert Not ele Is Nothing
                Debug.Print "Active model: " & model.Name
            Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
            Set ele = model.GetElementByID(idProjectnummer)
            

    But this gives me a run time error 430:

    "Class does not support Automation or does not support expected interface"

    on line: Set ele = oRaw.AsTextElement

    Am I doing it wrong?

Reply
  • Hi Jon,

    I inserted the line as suggested, the piece now looks like:

    If frmNieuwProject.txtProject.TextLength >= 9 Then
        If frmNieuwProject.txtCalculatie.TextLength = 10 Then 'was 9 140225
                Debug.Print "Active model: " & model.Name
                Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
                Dim oRaw As Element
                    Set oRaw = model.GetElementByID(idProjectnummer)
                    Debug.Assert Not oRaw Is Nothing
                    Set ele = oRaw.AsTextElement
                    Debug.Assert Not ele Is Nothing
                Debug.Print "Active model: " & model.Name
            Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
            Set ele = model.GetElementByID(idProjectnummer)
            

    But this gives me a run time error 430:

    "Class does not support Automation or does not support expected interface"

    on line: Set ele = oRaw.AsTextElement

    Am I doing it wrong?

Children
  • Continue to test your assumptions...

    Set oRaw = model.GetElementByID(idProjectnummer)
    Debug.Assert Not oRaw Is Nothing
    If oRaw.IsTextElement Then
        Set ele = oRaw.AsTextElement
    Else
        Debug.Print "oRaw is element type " & CStr(oRaw.Type)
    EndIf

    Or, better still, encapsulate your assumptions in a function...

    Function GetTextElementById ( _
        ByVal oModel As ModelReference, _
        ByVal elementId As String) As TextElement
      Set GetTextElementById = Nothing
      Dim oRaw As Element
      Set oRaw = oModel.GetElementById (DLongFromString (elementId))
      Debug.Assert Not oRaw Is Nothing
      Dim oFound As TextElement
      If oRaw.IsTextElement Then
        Set oFound = oRaw.AsTextElement
        Debug.Print "Element [" & elementId & "] content '" & _
          oFound.Text & "'"
      ElseIf oRaw.IsCellElement Then
        Dim oComponents As ElementEnumerator
        Set oComponents = oRaw.AsCellElement.GetSubElements
        Do While oComponents.MoveNext
            If DLongToString(oComponents.Current.ID) = elementId Then
              If oComponents.Current.IsTextElement Then
                Set oFound = oComponents.Current.AsTextElement
                Exit Do
            End If
          End If
        Loop
      End If
     
      If oFound Is Nothing Then
        Dim msg As String
        msg = "GetTextElementById(" & elementId & ") failed because the element is not a TextElement"
        ShowMessage msg, msg, msdMessageCenterPriorityWarning, True
      Else
        Set GetTextElementById = oFound
      EndIf
    Exit Function

    Usage...

    Dim oText As TextElement
    Set oText = GetTextElementById (ActiveModelReference, "1234")

     
    Regards, Jon Summers
    LA Solutions

  • Hi Jon,

    It looks like we are getting close,

    I inserted the lines from the first part, code is now:

    If frmNieuwProject.txtProject.TextLength >= 9 Then
        If frmNieuwProject.txtCalculatie.TextLength = 10 Then 'was 9 140225
                Debug.Print "Active model: " & model.Name
                Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
                Dim oRaw As Element
                    Set oRaw = model.GetElementByID(idProjectnummer)
                    If oRaw.IsTextElement Then
                            Set ele = oRaw.AsTextElement
                    Else
                            Debug.Print "oRaw is element type " & CStr(oRaw.Type)
                    End If
                    Debug.Assert Not oRaw Is Nothing
                    
                    Debug.Assert Not ele Is Nothing
                Debug.Print "Active model: " & model.Name
            Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
            Set ele = model.GetElementByID(idProjectnummer)

    Wich gives me:

    Active model: Default
    Get element from ID 25428
    oRaw is element type 2

    So oRaw isn't a textelement but a type 2

    So how do we correct this?

    Greetings Leo

  • So how do we correct this?

    You cannot correct it. When the element is type 2 (Cell Header), it's that type and cannot be other.

    The problem is that are expecting that element id xyz is TextElement (Type 17), which is not true. When you still want to use fixed IDs, you have to find in the design file yourself , what id a particular text element has.

    Regards,

      Jan

  • Hi Jan,

    The weird thing is that I have an element with the correct ID number:

    So it looks like it's converted somewhere??

    Greetings,

    Leo

  • So it looks like it's converted somewhere??

    Not, it's the bug recorded as Defect #655078, when GetElementById returns outermost (parent) element, not the element itself. For more details, see e.g. this discussion.

    I do not see this defect in the list for Update 13 or Update 14. Maybe can provide information when it will be fixed?

    Right now you have probably change your code to obtain subelements and to find a proper text element inside the cell.

    Regards,

      Jan

  • I have an element with the correct ID number

    You have a TextElement embedded in a CellHeaderElement.  That's perfectly normal and legal.  I don't know why GetElementById returns the cell and not the text — it looks like you're suffering from the bug Jan mentions.

     
    Regards, Jon Summers
    LA Solutions

  • Hi All,

    I have just checked the records and see Defect 655078 is still listed as being in progress, no solution available right now.
    The description of this Defect is exactly addressing the issue experienced here with this sample code.
    As workaround the correct text element should be found with iterating the SubElements of the found cell.

    Let me test with the given code above to add a workaround. I will return with the results ASAP.

    Best regards,
    Artur

  • Hi,

    the workaround below works with my test. I am searching in the found cell, if it contains a Textelement with given ID.

    I hope this helps?

    Best regards,

    Artur

    If frmNieuwProject.txtProject.TextLength >= 9 Then
        If frmNieuwProject.txtCalculatie.TextLength = 10 Then 'was 9 140225
                Debug.Print "Active model: " & Model.name
                Debug.Print "Get element from ID " & DLongToString(idProjectnummer)
                Dim oRaw As element
                    Set oRaw = Model.GetElementByID(idProjectnummer)
                    If oRaw.IsTextElement Then
                            Set ele = oRaw.AsTextElement
                    Else
                        If oRaw.IsCellElement Then
                            Dim ee As ElementEnumerator
                            Set ee = oRaw.AsCellElement.GetSubElements
                            Do While ee.MoveNext
                                If DLongToString(ee.Current.ID) = DLongToString(idProjectnummer) Then
                                    If ee.Current.IsTextElement Then
                                        Set ele = ee.Current
                                        Exit Do
                                    End If
                                End If
                            Loop
                        End If
                    End If
                    If ele Is Nothing Then Exit Sub
                    If ele.IsTextElement Then
                        Debug.Print "Text found: " & ele.AsTextElement.Text
                    End If
                    

  • Hi Jan,

    I ran a quick test, and inserted 6 textfield in our seed file, and used the ID numbers from them. This does work. Textfields are replaced with the text from the userbox. So it's indeed the way the ID numbers are coming back from the drawing.

    Unfortunately since we have thousands of old files which wouldn't work anymore I can't really change the code so the old files wouldn't work.

    I also ran a quick test on Arthur's workaround and for now I'm Hope full, cause it seems to work. But to be sure I will have to change all the places where this is used, which are quite a few, and to much for today.

    I'll pick this up in the morning,

    Thanks you all for now,

    Leo

  • I will have to change all the places where this is used

    DRY — write a function once.  Use that function many times.  See my post above, where I've incorporated Artur's workaround into the function.

     
    Regards, Jon Summers
    LA Solutions