MapInfo Pro Developers User Group

Examples of search and modify by frame type in a Layout

  • 1.  Examples of search and modify by frame type in a Layout

    Pitney Bowes
    Posted 11-26-2018 16:03

    Layouts can have multiple pages so to search for frames of a specific type, or by name, you need to make sure to iterate over all pages as well as all frames on each page. This discussion shows a few different ways that you can search for items in a Layout.

    Let's say we have a multi-page Layout window and we need to find a map frame with the name "Map of Sales Targets".  Perhaps we need to find this specific map to get its Window identifier needed for other map related processing.

    There's actually a single function that will do all this work for you.

     

    Dim layoutWinId as Integer
    Dim mapWinId as Integer
    layoutWinId = FrontWindow()
    mapWinId = LayoutItemWinID(layoutWinId, "Map of Sales Targets", LAYOUT_ITEM_TYPE_MAPPER)
    
     

    LayoutItemWinID will search all pages of the layout window for the first map named "Map of Sales Targets" and return its MapBasic Window identifier. It returns the first map frame it finds that matches the name string argument. The name of any frame item in a Layout can be assigned by you (using Alter Designer Frame), so make sure it is unique.

    LayoutItemWinID only works for frames that represent MapInfo windows like maps, browsers, and legends. What if you needed to search for all your logos and replace them with a new image?  You need to be able to iterate over all pages and all frame types to do this.

    We can get the total number of pages using LayoutInfo.

     

    Dim numPages as Integer
    numPages = LayoutInfo(layoutWinId, LAYOUT_INFO_NUM_PAGES)


     Next we need variables for iterating the pages and getting the number of frames on each page.

     

    Dim curPage, numFrames as Integer
    For curPage = 1 to numPages
      Set Layout Page curPage
      numFrames = LayouttemInfo(layoutWinId, -1, LAYOUT_ITEM_INFO_NUM_ITEMS_ON_PAGE)
      …
    Next

     

    The 2rd argument to LayouttemInfo is ignored when the attribute is LAYOUT_ITEM_INFO_NUM_ITEMS_ON_PAGE, so passing -1 is OK.

     

    A note about working with the page's frame collection:
    Frame ids are unique, but dynamic. The first frame added has id 1, the next has id 2 and so on. When you add a frame it's always added to the end of the page's frame collection so its id is always one more than the id of the last frame added. However, when you remove a frame, the ids of the remaining frames are adjusted. For example, removing the first frame which has id 1 means that the frame with id 2 now has id 1.

     

    You probably won't have more than one logo per page. If this is always true, then you should simply exit your For loop after you find the first logo frame to be removed and replaced. Otherwise you will be modifying the underlying collection of frames while iterating it and that can have unintended consequences.

    In this code sample though, we're going to make sure we replace all image frames on each page with a new image. To make sure we only remove the old images we'll keep track of the frames ids that need to be removed, and remove them after the iteration for the frames is done. So we'll create an array to store the ids of the old image frames as we find them, then remove them later.

    We'll also add variables to make sure we place the new image at the same position and with similar size so it occupies roughly the same area on the page.

    Also, because we are modifying page contents we need to make the Layout move to each page before we modify it. We do that using Set Layout Page.

    Here's the final loop.

     

    Dim layoutWinId as Integer
    Dim numPages, numFrames as Integer
    Dim curPage, curFrame as Integer
    Dim posX, posY, width, height as Float
    
    ' Declare array of frame ids to be removed
    Dim frame_ids() as Integer
    Dim i as Integer
    
    layoutWinId = FrontWindow()
    Set CoordSys Layout Units "in"
    
    numPages = LayoutInfo(layoutWinId, LAYOUT_INFO_NUM_PAGES)
    
    For curPage = 1 to numPages
    
      ' Make curpage the current page to modify it
      Set Layout Page curPage 
    
      ' Get number of frames on the current page to loop through them
      numFrames = LayoutItemInfo(layoutWinId, -1, LAYOUT_ITEM_INFO_NUM_ITEMS_ON_PAGE)
    
      ' Set the size of our array and initialize it to non-valid frame id
      Redim frame_ids(numFrames)
      For i = 1 To numFrames
        frame_ids(i) = 0
      Next
    
    
      For curFrame = 1 to numFrames
    
        ' Look for image frames only
        If (LayoutItemInfo(layoutWinId,  curFrame, LAYOUT_ITEM_INFO_TYPE) =               
          LAYOUT_ITEM_TYPE_IMAGE)  Then
    
          ' Get position of this image
          posX = LayoutItemInfo(layoutWinId, curFrame, LAYOUT_ITEM_INFO_POS_X)
          posY = LayoutItemInfo(layoutWinId, curFrame, LAYOUT_ITEM_INFO_POS_Y)
    
          ' You may want to get width/height to confine the aspect ratio of the
          ' new image to the same area occupied by previous one
          width = LayoutItemInfo(layoutWinId, curFrame, LAYOUT_ITEM_INFO_WIDTH)
          height = LayoutItemInfo(layoutWinId, curFrame, LAYOUT_ITEM_INFO_HEIGHT)
    
          ' Mark frame id in array to remove it later
          frame_ids(curFrame) = curFrame
    
          ' Place new image at same position; this adds to the end of the
          ' frame collection so it is not included in numFrames
          Add Image Frame Position (posX, posY) Units "in" Width width Units "in" Height 
            height Units "in" From File "C:\Layout Images\PBLogoNew.png"
    
        End If
      Next
    
      ' Now it's safe to remove the old image frames from the current page
      ' Do this in reverse order to remove only the marked frames
      For i = numFrames To 1 Step -1
        If (frame_ids(i) <> 0) Then
          Remove Designer Frame Id frame_ids(i)
        End If
      Next
    Next
    

     

    There is another function named LayoutPageItemInfo that returns the same information about a specific item as LayoutItemInfo, but takes a page as an argument. If you don't need to modify page contents, but just want to iterate to search or get information, use LayoutPageItemInfo because it won't force the layout to change its current page and will therefore be more efficient.

    For example, to count the number of text frames in the Layout

    Dim numText as Integer
    
    numText = 0
    
    For curPage = 1 to numPages
    
      ' Get number of frames on the current page to loop through them
    
      numFrames = LayoutPageItemInfo(layoutWinId,curPage, -1, 
        LAYOUT_ITEM_INFO_NUM_ITEMS_ON_PAGE)
    
      For curFrame = 1 to numFrames
    
        ' count this frame if it is text
    
        If (LayoutPageItemInfo(layoutWinId, curPage, curFrame, LAYOUT_ITEM_INFO_TYPE)
    
          = LAYOUT_ITEM_TYPE_TEXT) Then
    
          numText = numText + 1
    
        End If
    
      Next
    
    Next

     

     

     

     

     



    ------------------------------
    Michele Buselli
    Senior Principal Software Engineer
    Pitney Bowes Software
    ------------------------------