Create Context Menus

Context menus are used in IFS Applications to provide contextual actions to the end user. The framework provides source, edit and navigation default actions and the Application extends the default menus with the application specific actions.

In Application Forms there are several types of controls that can provide a context menu. Besides the window types (form, table and dialog), context menus can be set to child tables, tree views and picture controls. Throughout this page there will be general references to control which you should translate into the type of control that you are current developing for.

Contents

Design a context menu

A normal APF context menu is driven by Commands. Hence, in order to design the menu to meet your requirements, you shall use the Command Manager Designer.

Extend the default menu

All APF standard controls has a default menu defined. This is implemented by the framework and an application developer doesn't have to do anything to provide the standard menu. Figure 2 shows a context menu for an overview window. The menu items below the line are the default items provided by the framework for a table control. The items above the line is provided by the application for this specific table window.

 

Figure 2: Extended menu for a table window control.

In order to extend a default menu, create a context menu containing only the items to extend with and connect it to the control. By default, this will extend the default menu with those items.

  1. From the Visual Studio Solution Explorer, open the window that should be designed.
  2. Open the Command Manager Designer

    In the components pane, select the Command Manager and from the context menu select Designer...

  3. Add commands for the menu items to be added.
  4. In the Action Tree select the Context menus node and click the add button to add a context menu.
  5. Select the created context menu and add the menu items
  6. Connect the commands to the created menu items by assigning the menu item Command property to the command.
  7. Close the Command Manager Designer.
  8. On the design surface, select the control and assign the created menu to the ContextMenuStrip property.

Tip: If  you drag a command and drop it on the context menu, the editor will create the menu item and connect the command directly. The created menu item is also created with a name matching the command.

Note: All menu items (also sub menus) must be connected to commands to be translatable since it's the Caption property on the command that is translated, not the Text property on the menu item.

Replace the default menu

In some rare occasions it might be needed to completely replace the default menu. This is done much in the same way as extending the menu. The context menu publishes a property called IncludeStandardContextMenu. Set this property to false to remove all default items from the menu.

Add Context Menus Programmatically to a Control

Adding a context menu to a control can be done both at design time and programmatically at runtime. If a control has one designed menu that should be used, then this menu is connected to the control's ContextMenuStrip property at design time. Some controls will have more than one menu that is switched between depending on application logic. For these controls the context menu is set programmatically to the ContextMenuStrip property.

this.ContextMenuStrip = this.contextMenuReleased;

Figure 1: Programmatically set the context menu property for a control

Ported Menu Classes

When an application is ported with the IcePorter tool, context menus on a window are translated into nested menu classes. Figure 2 shows an example where the nested menu class menuFrmMethod is generated for the frmPickReport window class. Developing context menus in Application Forms is built around the commands concept as described earlier on this page. The commands concept is built around a standard context without nested menu classes. This means that there is quite a difference in how the ported source looks like compared to the one from a new developed window.

When Customizing a ported window, extending a ported menu can be done either by manually editing the menu classes or making use of the commands concept. The two concepts can co-exist and merge together in a layer principle. The limitation for a combined menu is that it is only possible to extend. It is not possible to control the placement of an added item and for obvious reasons you can not use the command editor to edit existing menu classes. So when customizing a ported window with nested menu classes it may be a good idea to convert the menu classes.

Convert Ported Menu

  1. In Visual Studio Solution Explorer, select the starting point for the conversion.
    You can convert menus for a single window, project of an entire solution. Select the starting point in the Solution Explorer and all child windows that contains a ported menu will be converted.
  2. Open the Context Menu for the selected item and select Convert Named Menus
  3. Verify and fix generated Event Handlers

Refactor Event Handlers

This conversion of the menus is not analyzing and changing any source logic. It simply refactors the ported classes into what it would look like if it would have been done with the Command Manager editor. This means that the result is expected to work out of the box. However, the typical implementation of a CTD menu action and inquire is to call UserMethod and when this implementation is copied the result becomes unnecessary complex.. It is recommended that handlers like these are refactored so that the actual implementation is placed directly in the event handler. Figure 2 show an example of how a converted event handler may look like. Figure 3 displays how the implementation is moved as recommended.

/// <summary>
/// Menu Actions
/// </summary>
private menuItem__Customer_Inquire(object sender, Ifs.Fnd.Windows.Forms.FndCommandInquireEventArgs e)
{
    // TODO: Rewrite command event handler not to user UserMethod.
    ((FndCommand)sender).Enabled = Sal.SendMsg(Sys.hWndForm, 
               Ifs.Fnd.ApplicationForms.Const.PM_UserMethod,
               Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire,
               ((SalString)Const.MENU_CustOrd_1).ToHandle());
    return;
}

public new SalBoolean UserMethod(SalNumber nWhat, SalString sMethod)
{
    using (new SalContext(this))
    {
        if(nWhat == Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire)
        {
            if (sMethod == Const.MENU_CustOrd_1) 
            {
                // Menu choice enabled only when an order is connected
                if (colsOrderNo.Text == Ifs.Fnd.ApplicationForms.Const.strNULL) 
                    return false;

                return nSourceState == Ifs.Fnd.ApplicationForms.Const.DATASOURCE_Query &&
                   ((bool)Sal.SendMsg(Sys.hWndForm, Ifs.Fnd.ApplicationForms.Const.PM_DataSourceCreateWindow, 
                Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire, ((SalString)"frmCustomerOrder").ToHandle()));
            }
        }
    }
    return false;
}

Figure 2: Example of how a converted menu event handler can make use of the UserMethod call.

/// <summary>
/// Menu Actions
/// </summary>
private menuItem__Customer_Inquire(object sender, Ifs.Fnd.Windows.Forms.FndCommandInquireEventArgs e)
{
       // Menu choice enabled only when an order is connected
       if (colsOrderNo.Text == Ifs.Fnd.ApplicationForms.Const.strNULL) 
       {
            ((FndCommand)sender).Enabled = false;
       }
       else
       {
            ((FndCommand)sender).Enabled = (nSourceState == Ifs.Fnd.ApplicationForms.Const.DATASOURCE_Query && 
                ((bool)Sal.SendMsg(Sys.hWndForm, Ifs.Fnd.ApplicationForms.Const.PM_DataSourceCreateWindow, 
            Ifs.Fnd.ApplicationForms.Const.METHOD_Inquire, ((SalString)"frmCustomerOrder").ToHandle())));
       }
}

Figure 3: Example of how implementation in Figure 2 should be refactored.

Multiple menus

In Applications there are places where there are multiple menus defined. Then ContextMenuNameGet method is overridden so that different menus are launched depending of some check. 

public new SalString ContextMenuNameGet()
{
    if (!(Ifs.Fnd.ApplicationForms.Int.QualifiedItemNameGet(i_hWndParent) == 
        "dlgPurchaseCharSearchWizard")) 
    {
        return "menuTbwMethodsPurPart";
    }
}

Figure 4: Example of logic that programmatically chose a CTD Context Menu class.

ContextMenuNameGet returns the name of a CTD menu to create. This is a Named Menu Class of the old style and should not be confused with the APF context menus. So when menus is converted for a window the converted menu classes is also removed.

If ContextMEnuNameGet returns an empty string or a string not matching an existing menu class no menu is displayed. No menu is displayed even if there is an APF context menu attached to the window or the control. I.e. if ContextMenuNameGet is overridden returning “menuTbwMethodsPurPart” when this class is converted and no longer existing then no menu will appear even if the converted menu, now an APF context menu with name menuTbwMethodsPurPart exist and are connected to the tbw.ContextMenuStrip property.

Above example now should be changed to something like this. Instead of returning the name of the menu that you want an instance of (again CTD menu) the ContextMenuStrip property is assigned the context menu to show. To get the menu to show at all we can’t just return anything. So here we also return the base implementation.

public new SalString ContextMenuNameGet()
{
    if (!(Ifs.Fnd.ApplicationForms.Int.QualifiedItemNameGet(i_hWndParent) == 
        "dlgPurchaseCharSearchWizard")) 
    {
        this.ContextMenuStrip = menuTbwMethodsPurPart;
        return base.ContextMenuNameGet(); 
    }
}

Figure 5: Example of how the implementation in Figure 4 can be changed to support the APF context menus.