Building an MDI Application

About this chapter

This chapter describes how to build a Multiple Document Interface (MDI) application in PowerBuilder.

About MDI

Multiple Document Interface (MDI) is an application style you can use to open multiple windows (called sheets) in a single window and move among the sheets. To build an MDI application, you define a window whose type is MDI Frame and open other windows as sheets within the frame.

Most large-scale Windows applications are MDI applications. For example, PowerBuilder is an MDI application: the PowerBuilder window is the frame and the painters are the sheets.

If you expect your users to want to open several windows and easily move from window to window, you should make your application an MDI application.

Using the Template Application feature

When you create a new application, you can select the Template Application Start wizard and then choose to create an SDI or MDI application. If you select MDI application, PowerBuilder generates the shell of an MDI application that includes an MDI frame (complete with window functions that do such things as open or close a sheet), a sheet manager object and several sheets, an About dialog box, menus, toolbars, and scripts.

MDI frame windows

An MDI frame window is a window with several parts: a menu bar, a frame, a client area, sheets, and (usually) a status area, which can display MicroHelp (a short description of the current menu item or current activity).

The frame

The MDI frame is the outside area of the MDI window that contains the client area. There are two types of MDI frames:

  • Standard

  • Custom

Standard frames

A standard MDI frame window has a menu bar and (usually) a status area for displaying MicroHelp. The client area is empty, except when sheets are open. Sheets can have their own menus, or they can inherit their menus from the MDI frame. Menu bars in MDI applications always display in the frame, never in a sheet. The menu bar typically has an item that lists all open sheets and lets the user tile, cascade, or layer the open sheets.

Custom frames

Like a standard frame, a custom frame window usually has a menu bar and a status area. The difference between standard and custom frames is in the client area: in standard frames, the client area contains only open sheets; in custom frames, the client area contains the open sheets as well as other objects, such as buttons and StaticText. For example, you might want to add a set of buttons with some explanatory text in the client area.

Client area

In a standard frame window, PowerBuilder sizes the client area automatically and the open sheets display within the client area. In custom frame windows containing objects in the client area, you must size the client area yourself. If you do not size the client area, the sheets will open, but may not be visible.

The MDI_1 control

When you build an MDI frame window, PowerBuilder creates a control named MDI_1, which it uses to identify the client area of the frame window. In standard frames, PowerBuilder manages MDI_1 automatically. In custom frames, you write a script for the frame's Resize event to size MDI_1 appropriately.

Displaying information about MDI_1

You can see the properties and functions for MDI_1 in the Browser. Create a window of type MDI and select the Window tab in the Browser. Select the MDI frame window and select Expand All from the pop-up menu. MDI_1 is listed as a window control, and you can examine its properties, functions, and so forth in the right pane of the Browser.

MDI sheets

Sheets are windows that can be opened in the client area of an MDI frame. You can use any type of window except an MDI frame as a sheet in an MDI application. To open a sheet, use either the OpenSheet or OpenSheetWithParm function.

Toolbars

Often you want to provide a toolbar for users of an MDI application. You can have PowerBuilder automatically create and manage a toolbar that is based on the current menu, or you can create your own custom toolbar (generally as a user object) and size the client area yourself.

For information on providing a toolbar, see the section called “Working with Menus and Toolbars” in Users Guide. For more information on sizing the client area, see Sizing the client area.

Building an MDI frame window

When you create a new window in PowerBuilder, its default window type is Main. Select mdi! or mdihelp! on the General property page to change the window to an MDI frame window.

Using menus

When you change the window type to MDI, you must associate a menu with the frame. Menus usually provide a way to open sheets in the frame and to close the frame if the user has closed all the sheets.

About menus and sheets

A sheet can have its own menu but is not required to. When a sheet without a menu is opened, it uses the frame's menu.

Using sheets

In an MDI frame window, users can open windows (sheets) to perform activities. For example, in an electronic mail application, an MDI frame might have sheets that users open to create and send messages and read and reply to messages. All sheets can be open at the same time and the user can move among the sheets, performing different activities in each sheet.

About menus and sheets

A sheet can have its own menu but is not required to. When a sheet without a menu is opened, it uses the frame's menu.

Opening sheets

To open a sheet in the client area of an MDI frame, use the OpenSheet function in a script for an event in a menu item, an event in another sheet, or an event in any object in the frame.

For more information about OpenSheet, see the section called “OpenSheet” in PowerScript Reference.

Opening instances of windows

Typically in an MDI application, you allow users to open more than one instance of a particular window type. For example, in an order entry application, users can probably look at several different orders at the same time. Each of these orders displays in a separate window (sheet).

Listing open sheets

When you open a sheet in the client area, you can display the title of the window (sheet) in a list at the end of a drop-down menu. This menu lists two open sheets:

To list open sheets in a drop-down menu:

  • Specify the number of the menu bar item in which you want the open sheets listed when you call the OpenSheet function. Typically you list the open sheets in the Windows menu. In a menu bar with four items in the order File, Edit, Windows, and Help, you specify the Windows menu with the number 3.

    If more than nine sheets are open at one time, only nine sheets are listed in the menu and More Windows displays in the tenth position. To display the rest of the sheets in the list, click More Windows.

Arranging sheets

After you open sheets in an MDI frame, you can change the way they are arranged in the frame with the ArrangeSheets function.

To allow arrangement of sheets

To allow the user to arrange the sheets, create a menu item (typically on a menu bar item named Window) and use the ArrangeSheets function to arrange the sheets when the user selects a menu item.

Maximizing sheets

If sheets opened in an MDI window have a control menu, users can maximize the sheets. When the active sheet is maximized:

  • If another sheet becomes the active sheet, that sheet is maximized (the sheet inherits the state of the previous sheet).

  • If a new sheet is opened, the current sheet is restored to its previous size and the new sheet is opened in its original size.

Closing sheets

Active sheet

To close the active window (sheet), users can press ctrl+f4. You can write a script for a menu item that closes the parent window of the menu (make sure the menu is associated with the sheet, not the frame.) For example:

Close(ParentWindow)

All sheets

To close all sheets and exit the application, users can press alt+f4. You can write a script to keep track of the open sheets in an array and then use a loop structure to close them.

Providing MicroHelp

MDI provides a MicroHelp facility that you can use to display information to the user in the status area at the bottom of the frame. For example, when the user selects a menu item, the MicroHelp facility displays a description of the selected item in the status area.

You can define MicroHelp for menu items and for controls in custom frame windows.

Providing MicroHelp for menu items

You specify the text for the MicroHelp associated with a menu item on the General property page in the Menu painter. To change the text of the MicroHelp in a script for a menu item, use the SetMicroHelp function.

Providing MicroHelp for controls

You can associate MicroHelp with a control in a custom frame window by using the control's Tag property. For example, say you have added a Print button to the client area. To display MicroHelp for the button, write a script for the button's GetFocus event that sets the Tag property to the desired text and then uses SetMicroHelp to display the text. For example:

cb_print.Tag="Prints information about current job"
w_genapp_frame.SetMicroHelp(This.Tag)

You can also set a control's Tag property in the control's property sheet.

In the LoseFocus event, you should restore the MicroHelp:

w_genapp_frame.SetMicroHelp("Ready")

Using toolbars in MDI applications

This section describes some techniques you can use to customize the behavior of your toolbars and save and restore toolbar settings. For information about how to define and use menus and toolbars, see the section called “Working with Menus and Toolbars” in Users Guide.

Customizing toolbar behavior

Disabling toolbar buttons

To disable a toolbar button, you need to disable the menu item with which it is associated. Disabling the menu item disables the toolbar button automatically.

To disable a menu item, you need to set the Enabled property of the menu item:

m_test.m_file.m_new.Enabled = FALSE

Using alternate icons

The enabled and disabled states of each toolbar button are normally indicated by a pair of contrasting color and greyscale icons. For greater contrast between the enabled and disabled states, you can apply an alternate set of icons to the toolbar buttons, by setting the PBTOOLBARDISABLEMODE environment variable on your system to 1 .

Hiding toolbar buttons

To hide a menu item, you set the Visible property of the item:

m_test.m_file.m_open.Visible = FALSE

Hiding a menu item does not cause its toolbar button to disappear or be disabled. To hide a toolbar button, you need to set the ToolbarItemVisible property of the menu item:

m_test.m_file.m_open.ToolBarItemVisible = FALSE

Hiding a menu item does not cause the toolbar buttons for the drop-down or cascading menu items at the next level to disappear or be disabled. You need to hide or disable these buttons individually.

Setting the current item in a drop-down toolbar

When a user clicks on a toolbar button in a drop-down toolbar, PowerBuilder makes the selected button the current item. This makes it easy for the user to perform a particular toolbar action repeatedly. You can also make a particular button the current item programmatically by setting the CurrentItem property of the MenuCascade object. For example, to set the current item to the toolbar button for the New option on the File menu, you could execute this line in a script:

m_test.m_file.currentitem = m_test.m_file.m_new

In order for this to work, you would need to specify MenuCascade as the object type for the File menu in the Menu painter.

Testing for whether a toolbar is moved

Whenever a toolbar moves in an MDI frame window, PowerBuilder triggers the ToolBarMoved event for the window. In the script for this event, you can test to see which toolbar has moved and perform some processing. When the user moves the FrameBar or SheetBar, the ToolbarMoved event is triggered and the Message.WordParm and Message.LongParm properties are populated with values that indicate which toolbar was moved:

Property

Value

Meaning

Message.WordParm

0

FrameBar moved

1

SheetBar moved

Message.LongParm

0

Moved to left

1

Moved to top

2

Moved to right

3

Moved to bottom

4

Set to floating


Saving and restoring toolbar settings

You can save and restore the current toolbar settings using functions that retrieve information about your toolbar settings, and you can modify these settings.

GetToolbar and GetToolbarPos allow you to retrieve the current toolbar settings. SetToolbar and SetToolbarPos allow you to change the toolbar settings. The syntax you use for the GetToolbarPos and SetToolbarPos functions varies depending on whether the toolbar you are working with is floating or docked.

Floating toolbars

The position of a floating toolbar is determined by its x and y coordinates. The size of a floating toolbar is determined by its width and height.

When you code the GetToolbarPos and SetToolbarPos functions for a floating toolbar, you need to include arguments for the x and y coordinates and the width and height.

Docked toolbars

The position of a docked toolbar is determined by its docking row and its offset from the beginning of the docking row. For toolbars at the top or bottom, the offset for a docked toolbar is measured from the left edge. For toolbars at the left or right, the offset is measured from the top.

By default, the docking row for a toolbar is the same as its bar index. If you align the toolbar with a different border in the window, its docking row may change depending on where you place it.

When you code the GetToolbarPos and SetToolbarPos functions for a docked toolbar, you need to include arguments for the docking row and the offset.

Example

The example below shows how to use a custom class user object to manage toolbar settings. The user object has two functions, one for saving the current settings and the other for restoring the settings later on. Because the logic required to save and restore the settings is handled in the user object (instead of in the window itself), this logic can easily be used with any window.

The sample code shown below supports both fixed and floating toolbars.

Script for the window's Open event

When the window opens, the following script restores the toolbar settings from an initialization file. To restore the settings, it creates a custom class user object called u_toolbar and calls the Restore function:

// Create the toolbar NVO
u_toolbar = create u_toolbar
// Restore the toolbar positions
u_toolbar.Restore(this, "toolbar.ini", this.ClassName())

Script for the window's Close event

When the window closes, the following script saves the toolbar settings by calling the Save function. Once the settings have been saved, it destroys the user object:

// Save the toolbar 
stateu_toolbar.Save(this, "toolbar.ini", ClassName())
// Destroy the toolbar NVO
destroy u_toolbar

Script for the Save function

The Save function has three arguments:

  • Win -- provides the window reference

  • File -- provides the name of the file where the settings should be saved

  • Section -- identifies the section where the settings should be saved

The Save function uses the GetToolbar and GetToolbarPos functions to retrieve the current toolbar settings. To write the settings to the initialization file, it uses the SetProfileString function.

The Save function can handle multiple toolbars for a single menu. It uses the bar index to keep track of information for each toolbar:

// Store the toolbar settings for the passed window
integer index, row, offset, x, y, w, h
boolean visible
string visstring, alignstring, title
toolbaralignment alignment
FOR index = 1 to 16 

// Try to get the attributes for the bar.   
IF win.GetToolbar(index, visible, alignment, &
   title)= 1 THEN   
   // Convert visible to a string
   CHOOSE CASE visible
   CASE true
      visstring = "true"
   CASE false   
      visstring = "false"   
   END CHOOSE

   // Convert alignment to a string
   CHOOSE CASE alignment      
   CASE AlignAtLeft!      
      alignstring = "left"
   CASE AlignAtTop!
      alignstring = "top"      
   CASE AlignAtRight!      
      alignstring = "right"      
   CASE AlignAtBottom!      
      alignstring = "bottom"      
   CASE Floating!      
      alignstring = "floating"
   END CHOOSE

   // Save the basic attributes   
   SetProfileString(file, section +  &      
      String(index), "visible", visstring)         
   SetProfileString(file, section +  &
      String(index), "alignment", alignstring)   
   SetProfileString(file, section +  &
      String(index), "title", title)

   // Save the fixed position
   win.GetToolbarPos(index, row, offset)
   SetProfileString(file, section +  &
      String(index), "row", String(row))
   SetProfileString(file, section +  &
      String(index), "offset", String(offset))

   // Save the floating position
   win.GetToolbarPos(index, x, y, w, h)
   SetProfileString(file, section +  &
      String(index), "x", String(x))
   SetProfileString(file, section +  &
      String(index), "y", String(y))
   SetProfileString(file, section +  &
      String(index), "w", String(w))

   SetProfileString(file, section +  &
      String(index), "h", String(h))
   END IF
NEXT

Script for the Restore function

The Restore function has the same three arguments as the Save function. It uses the ProfileString function to retrieve toolbar settings from the initialization file. Once the settings have been retrieved, it uses the SetToolbar and SetToolbarPos functions to restore the toolbar settings.

Like the Save function, the Restore function can handle multiple toolbars for a single menu. It uses the bar index to keep track of information for each toolbar:

// Restore toolbar settings for the passed window

integer index, row, offset, x, y, w, h
boolean visible
string visstring, alignstring, title
toolbaralignment alignment

FOR index = 1 to 16
  // Try to get the attributes for the bar.
  IF win.GetToolbar(index, visible, alignment, &
    title)= 1 THEN
    // Try to get the attributes from the .ini file
    visstring = ProfileString(file, section +  &
      String(index), "visible", "")
    IF visstring > "" THEN
      // Get all of the attributes
      alignstring = ProfileString(file, section +  &
        String(index), "alignment", "left")
      title = ProfileString(file, section +  &
        String(index), "title", "(Untitled)")
      row = Integer(ProfileString(file, section +  &
        String(index), "row", "1"))
      offset = Integer(ProfileString(file, &
        section + String(index), "offset", "0"))
      x = Integer(ProfileString(file, section +  &
        String(index), "x", "0"))
      y = Integer(ProfileString(file, section +  &
        String(index), "y", "0"))
      w = Integer(ProfileString(file, section +  &
        String(index), "w", "0"))
      h = Integer(ProfileString(file, section +  &
        String(index), "h", "0"))

      // Convert visstring to a boolean
      CHOOSE CASE visstring
      CASE "true"
        visible = true
      CASE "false"
        visible = false
      END CHOOSE

      // Convert alignstring to toolbaralignment
      CHOOSE CASE alignstring
      CASE "left"
        alignment = AlignAtLeft!
      CASE "top"
        alignment = AlignAtTop!
      CASE "right"
        alignment = AlignAtRight!
      CASE "bottom"
        alignment = AlignAtBottom!
      CASE "floating"
        alignment = Floating!
      END CHOOSE

      // Set the new position
      win.SetToolbar(index, visible, alignment, title)
      win.SetToolbarPos(index, row, offset, false)
      win.SetToolbarPos(index, x, y, w, h)
    END IF
  END IF
NEXT

Sizing the client area

PowerBuilder sizes the client area in a standard MDI frame window automatically and displays open sheets unclipped within the client area. It also sizes the client area automatically if you have defined a toolbar based on menu items, as described in the preceding section.

However, in a custom MDI frame window where the client area contains controls in addition to open sheets PowerBuilder does not size the client area; you must size it. If you do not size the client area, the sheets open but may not be visible and are clipped if they exceed the size of the client area.

If you plan to use an MDI toolbar with a custom MDI frame, make sure the controls you place in the frame's client area are far enough away from the client area's borders so that the toolbar does not obscure them when displayed.

Scroll bars display when a sheet is clipped

If you selected HScrollBar and VScrollBar when defining the window, the scroll bars display when a sheet is clipped. This means not all the information in the sheet is displayed. The user can then scroll to display the information.

When you create a custom MDI frame window, PowerBuilder creates a control named MDI_1 to identify the client area of the frame. If you have enabled AutoScript, MDI_1 displays in the list of objects in the AutoScript pop-up window when you create a script for the frame.

To size or resize the client area when the frame is opened or resized:

  • Write a script for the frame's Open or Resize event that:

    • Determines the size of the frame

    • Sizes the client area (MDI_1) appropriately

    For example, the following script sizes the client area for the frame w_genapp_frame. The frame has a series of buttons running across the frame just below the menu, and MicroHelp at the bottom:

    int li_width, li_height
    
    //Get the width and height of the frame's workspace.
    //
    //Note that if the frame displays any MDI toolbars,
    //those toolbars take away from the size of the
    //workspace as returned by the WorkSpaceWidth and
    //WorkSpaceHeight functions. Later, you see how to
    //to adjust for this.
    //
    li_width = w_genapp_frame.WorkSpaceWidth()
    
    li_height = w_genapp_frame.WorkSpaceHeight()
    
    //Next, determine the desired height of the client
    //area by doing the following:
    //
    // 1) Subtract from the WorkSpaceHeight value: the
    //   height of your control and the Y coordinate of
    //   the control (which is the distance between the
    //   top of the frame's workspace -- as if no
    //   toolbars were there -- and the top of your
    //   control).
    //
    // 2) Then subtract: the height of the frame's
    //   MicroHelp bar (if present)
    //
    // 3) Then add back: the height of any toolbars that
    //   are displayed (to adjust for the fact that the
    //   original WorkSpaceHeight value we started with
    //   is off by this amount). The total toolbar
    //   height is equal to the Y coordinate returned 
    //   by the WorkspaceY function.
    
    li_height = li_height - (cb_print.y + cb_print.height)
    
    li_height = li_height - MDI_1.MicroHelpHeight
    
    li_height = li_height + WorkspaceY()
    
    //Now, move the client area to begin just below your
    //control in the workspace.
    
    mdi_1.Move (WorkspaceX (), cb_print.y +  & 
       cb_print.height)
    
    //Finally, resize the client area based on the width
    //and height you calculated earlier.
    
    mdi_1.Resize (li_width, li_height)

About MicroHelpHeight

MicroHelpHeight is a property of MDI_1 that PowerBuilder sets when you select a window type for your MDI window. If you select MDI Frame, there is no MicroHelp and MicroHelpHeight is 0; if you select MDI Frame with MicroHelp, MicroHelpHeight is the height of the MicroHelp.

About keyboard support in MDI applications

PowerBuilder MDI applications automatically support arrow keys and shortcut keys.

Arrow keys

In an MDI frame, how the pointer moves when the user presses an arrow key depends on where focus is when the key is pressed:

Key

If focus is in

Focus moves to

Left

The menu bar

The menu item to the left of the item that has focus

The first menu bar item

The control menu of the active sheet

The control menu of the active sheet

The control menu of the frame

The control menu of the frame

The last menu item

Right

The menu bar

The menu item to the right of the item that has focus

The last menu bar item

The control menu of the frame

The control menu of the frame

The control menu of the active sheet

The control menu of the active sheet

The first item in the menu bar

Down

A drop-down or cascading menu

The menu item below the current item

The last menu item in the drop-down or cascading menu

The first item in the menu

Up

A drop-down or cascading menu

The menu item above the current item

The first menu item in a drop-down or cascading menu

The last item in the menu


Shortcut keys

PowerBuilder automatically assigns two shortcut keys to every MDI frame:

Key

Use to

Ctrl+F4

Close the active sheet and make the previous sheet active. The previous sheet is the sheet that was active immediately before the sheet that was closed.

Ctrl+F6

Make the previous sheet the active sheet.