[VBA] Object Required, need help with detect and/or create level VBA

Hi All,

         I have been trying to create a VBA that will detect if a level exists, if it does then set that level to active, if it doesn't then create that level and set it active.  I wrote a code that I thought would work, but I am receiving a compile error saying that an object is required with the following part of the code highlighted:

Sub CreateOrActiveLevel()

Dim LevelName As String
Dim LinksLevel As Level
Dim oLevel As Level

Set LevelName = "Links"
Set LinksLevel = ActiveDesignFile.Levels(Links).IsDisplayed
If LinksLevel = True Then
    ActiveSettings.Level = ActiveDesignFile.Levels(Links)
    Else
    Set oLevel = ActiveDesignFile.AddNewLevel(LevelName)
    Levels.Rewrite
    ActiveSettings.Level = ActiveDesignFile.Levels(Links)
End If

End Sub

I have already found a similar post relating to this (http://communities.bentley.com/products/programming/microstation_programming/f/343173/t/88062) but despite Jon Summers always helpful and informative posts, I couldn't get my own to work.  Can anyone see where I'm going wrong?

P.S.  I am new to VBAs and coding, so it would be great if answers were kept in laymans terms.

Jeff

  • Unknown said:
    I have been trying to create a VBA that will detect if a level exists

    We wrote this article on that topic.

    Unknown said:
    Set LevelName = "Links"

    The Set keyword is required in an assignment when the target is an object.  A String is not an object (at least within the rules of VBA), and the Set keyword should not be used.  The error message is telling you that it's seen the Set keyword, and expects an object to follow.  Just do this:

    LevelName = "Links"

    This VBA syntax has befuddled many of us over the years.  It may be the reason why Microsoft invented VB.NET, which is more straightforward.

     
    Regards, Jon Summers
    LA Solutions

  • You only need to use Set for Objects. LevelName is a string variable. All it needs is LevelName = "Links" although LET LevelName = "Links" is also allowable.
    I think, your next line needs to be :
    Set LinksLevel = ActiveDesignFile.Levels("Links").IsDisplayed
    But Jon's code shows how some queries can throw an error and you need to use Error Handling routines to avoid ending up in the editor with some yellow highlighted text.

    Charles (Chuck) Rheault
    CADD Manager

    MDOT State Highway Administration

    • MicroStation user since IGDS, InRoads user since TDP.
    • AutoCAD, Land Desktop and Civil 3D, off and on since 1996
  • Hey Jon,
    I looked and your VBA and copied it verbatim in order to see how it worked (this one:

    Public Function IsValidLevelName(ByVal levelName As String) As Boolean
    IsValidLevelName = False
    On Error GoTo err_IsValidLevelName
    Dim oLevel As Level
    Set oLevel = ActiveDesignFile.Levels(levelName)
    If oLevel Is Nothing Then
    IsValidLevelName = False
    Else
    IsValidLevelName = True
    End If
    Set oLevel = Nothing
    Exit Function

    err_IsValidLevelName:
    Select Case Err.Number
    Case 5:
    ' Level not found
    Resume Next
    Case Else
    MsgBox "IsValidLevelName failed"
    End Select
    End Function




    But nothing happens when I run it. I mean that literally. In the VBA Project Manager I highlight your VBA, I click the Macros button to the left of the Start Record button, and then when the next window comes up (the Macros window) there is no VBA available to run. Is this a problem you have seen before? Or do I need to change the code in some way to make it available to run?

    Best,
    Jeff
  • In VBA, there are macros, sub procedures and functions. The only difference between a macro and a sub procedure is that a macro can have no arguments. And sub with no arguments (variables in parentheses) can be a macro, but does not necessarily. Macros can call other subs and functions. Functions expect to return a value or object. Subs cannot. Jon's example is a function that you can place in a macro to call when it is needed.
    These are things that are not necessarily spelled out to newcomers to VBA.

    Charles (Chuck) Rheault
    CADD Manager

    MDOT State Highway Administration

    • MicroStation user since IGDS, InRoads user since TDP.
    • AutoCAD, Land Desktop and Civil 3D, off and on since 1996
  • Hey Jon,
    I've been tinkering with an old post of yours in order to create a program that can 1) Check to see if a level exists, 2) Creates a level if it doesn't, 3) Sets that level as active, 4) Parses out text from an element, 5) And then inserts that text into a URL, which is attached to the selected element. In the following code, objectives 4 and 5 work, but 1-3 will not. Any advice?


    ' Function is reponsible for creating a level, if it doesn't exist,
    ' or using an existing level
    Function CreateOrFindLevel(ByVal levelName As Level) As Level
    If LevelExists(levelName) Then
    Set CreateOrFindLevel = ActiveDesignFile.Levels.Find(levelName)
    Else
    Set CreateOrFindLevel = ActiveDesignFile.AddNewLevel(levelName)
    ActiveDesignFile.Levels.Rewrite
    End If
    End Function
    ' Subroutine is reponsible for setting the active level
    Sub SetActiveLevel(ByVal levelName As Level)
    Set ActiveSettings.Level = CreateOrFindLevel(levelName)
    End Sub
    Sub Init()
    Dim ee As ElementEnumerator
    Set ee = ActiveModelReference.GetSelectedElements
    Do While ee.MoveNext
    Dim oElement As Element
    Set oElement = ee.Current
    If oElement.IsTextElement Then
    Dim oTextElement As TextElement
    Set oTextElement = oElement.AsTextElement
    Debug.Print "Text=" & oTextElement.Text
    End If
    Loop
    Dim content As String
    content = oTextElement.Text
    CreateLink content
    End Sub
    Sub CreateLink(ByVal content As String)
    Dim strURL As String
    strURL = "http://www.google.com" & "/" & content
    Debug.Print "My URL=" & strURL
    CadInputQueue.SendKeyin "ELEMENT CREATE LINK URL " & strURL
    End Sub
  • How do you test function CreateOrFindLevel? Have you written a test wrapper...

    Sub TestCreateOrFindLevel ()
      Const NewLevelName As String = "Be Communities"
      Debug.Print "Create or find level '" & NewLevelName & "'"
      CreateOrFindLevel NewLevelName   
    End Sub

    What happens in the Level Manager window?  What do you find when you step through that code line-by-line (Function key F8)?

     
    Regards, Jon Summers
    LA Solutions

  • Hey Jon,
    Test wrappers are a bit of a new concept to me. This is the definition I'm going by:

    A wrapper is data that precedes or frames the main data or a program that sets up another program so that it can run successfully.

    Based on that, I would assume for a test wrapper (like the one you wrote above) to be the first sub in a macro. But based on line 4 referencing CreateOrFindLevel, I would have to assume that my code should then instead look something like this:

    ' Function is responsible for creating a level, if it doesn't exist,
    ' or using an existing level
    Function CreateOrFindLevel(ByVal levelName As Level) As Level
    If LevelExists(levelName) Then
    Set CreateOrFindLevel = ActiveDesignFile.Levels.Find(levelName)
    Else
    Set CreateOrFindLevel = ActiveDesignFile.AddNewLevel(levelName)
    ActiveDesignFile.Levels.Rewrite
    End If
    End Function

    Sub TestCreateOrFindLevel()
    Const NewLevelName As String = "levelName"
    Debug.Print "Create or find level '" & NewLevelName & "'"
    CreateOrFindLevel NewLevelName
    End Sub

    ' Subroutine is reponsible for setting the active level
    Sub SetActiveLevel(ByVal levelName As Level)
    Set ActiveSettings.Level = CreateOrFindLevel(levelName)
    End Sub

    Sub Init()
    Dim ee As ElementEnumerator
    Set ee = ActiveModelReference.GetSelectedElements
    Do While ee.MoveNext
    Dim oElement As Element
    Set oElement = ee.Current
    If oElement.IsTextElement Then
    Dim oTextElement As TextElement
    Set oTextElement = oElement.AsTextElement
    Debug.Print "Text=" & oTextElement.Text
    End If
    Loop
    Dim content As String
    content = oTextElement.Text
    CreateLink content
    End Sub

    Sub CreateLink(ByVal content As String)
    Dim strURL As String
    strURL = "http://www.google.com" & "/" & content
    Debug.Print "My URL=" & strURL
    CadInputQueue.SendKeyin "ELEMENT CREATE LINK URL " & strURL
    End Sub



    But what would the test wrapper be doing that isn't already done? Should I add an extra boolean statement and a loop to take the code back to the top if the level I am trying to create does not yet exist at that point? Also, the wrapper is causing a Type Mismatch Compile Error. Debug mode has highlighted line 1 and "NewLevelName" of line 4.
  • Hi Jeff,

    I rewrite your original VBA code as below. It works for me.

    Sub CreateOrActiveLevel()
       Dim LevelName As String
       Dim oLevel As Level
    
       LevelName = "Links"
       Set oLevel = ActiveDesignFile.Levels.Find(LevelName)
       If oLevel Is Nothing Then
           Set oLevel = ActiveDesignFile.AddNewLevel(LevelName)
           ActiveDesignFile.Levels.Rewrite
       End If
       ActiveSettings.Level = oLevel
    End Sub

    HTH, YongAn



  • Unknown said:
    Test wrappers are a bit of a new concept to me.

    A test wrapper, just like the example I provided, is a simple procedure that has but one goal.  In the world of software engineering they are termed unit tests. It's purpose is to test some other procedure(s), with no other side-effects.

    Unknown said:
    I would assume for a test wrapper to be the first sub in a macro

    A curious assumption.  A test wrapper can be anywhere you like.  I prefer to place them next to the procedure they're intended to test, make them Private, and assign them a wacky name so their purpose is clear.

     
    Regards, Jon Summers
    LA Solutions