2009-08-24 8 views
2

Je voudrais mettre en œuvre une fonction avec le prototypeComment trouver l'élément de menu (le cas échéant) qui ouvre un HMENU donné lorsqu'il est activé?

/* Locates the menu item of the application which caused the given menu 'mnu' to 
* show up. 
* @return true if the given menu 'mnu' was opened by another menu item, false 
* if not. 
*/ 
bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx); 

Compte tenu d'une poignée HMENU, je voudrais être en mesure de savoir quel élément de menu (le cas échéant) dans l'application ouvrit. C'est essentiellement l'inverse de la fonction GetSubMenu.

Mon approche actuelle est de regarder dans chaque HMENU des fenêtres de niveau supérieur de l'application et de vérifier si je peux trouver un élément de menu qui ouvrirait le sous-menu donné lorsqu'il est activé. Je le fais de manière récursive, en utilisant GetMenuItemCount/GetSubMenu.

Ceci est cependant plutôt inefficace et échoue pour les menus qui sont ouverts par les éléments du menu contextuel. Par conséquent, je me demande:

Est-ce que quelqu'un a une bonne idée comment trouver l'élément de menu (le cas échéant) qui ouvre un HMENU donné lorsqu'il est activé?

MISE À JOUR: Une idée qui m'est venue à l'esprit; il devrait être possible (en utilisant la fonction SetWindowsHookEx) d'installer un hook qui sera averti de tous les événements d'entrée qui se sont produits dans un menu. Chaque fois qu'une activation d'élément de menu est détectée, mémorisez l'élément de menu (identifié par une paire (HMENU, int)) et le HMENU qui sera ouvert par l'élément de menu dans une carte globale. La fonction getParentMenuItem ci-dessus pourrait alors simplement effectuer une recherche dans la carte. L'idée d'accrochage décrite dans la mise à jour ci-dessus ne fonctionnera pas car elle ne reconnaîtra bien sûr que les éléments de menu -> associations de menu pour les éléments qui ont été activés à un moment ou à un autre. Cela me semble un peu moche car cela me demande de garder beaucoup d'état (la carte); Y a-t-il des possibilités plus faciles?

Répondre

1

Vous pouvez essayer de régler MENUINFO.dwMenuData à la poignée de menu parent pour tous les menus que vous créez dans votre application:

MENUINFO mi; 
mi.cbSize = sizeof(MENUINFO); 
mi.dwMenuData = (ULONG_PTR)<parent HMENU if this is a sub menu> 
mi.fMask = MIM_MENUDATA; 

SetMenuInfo(hCreatedMenu, &mi); 

Ensuite il vous suffit d'interroger ce champ dwMenuData dans votre fonction:

bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx) 
{ 
    MENUINFO mi; 
    mi.cbSize = sizeof(MENUINFO); 
    mi.fMask = MIM_MENUDATA; 

    if (!GetMenuInfo(mnu,&mi) || mi.dwMenuData == 0) 
     return false; 

    *parentMenu = (HMENU)mi.dwMenuData; 

    // not sure how or why you need the parentMenuIdx, but you should be able 
    // to derive that from the parent HMENU 

    return true; 
} 

Modifier: Si vous n'avez aucun contrôle sur la façon dont tous les menus sont créés, vous pouvez utiliser un WH_CALLWNDPROC crochet à piéger lors de la création d'un menu. (avec le code source) décrit comment cela peut être fait - vous pourriez alors essayer d'injecter le parent HMENU dans le menu créé en utilisant la méthode décrite ci-dessus.

+0

Bonne idée! Malheureusement, je n'ai souvent pas accès au code qui crée les menus, donc je ne peux pas le faire dans de nombreux cas. Je cherche une fonction qui n'exige pas de telles modifications au code source existant. Vous indiquez une bonne option pour les cas où je * peux * modifier le code source, donc +1 pour vous! –

+0

Accepter cette réponse pour obtenir la valeur de la prime complète. Malheureusement, établir une prime pour cette question n'a pas donné de nouvelles idées. : - / –

1

Je viens de trouver le même besoin. J'ai mes menus définis dans le fichier .rc, et je veux griser l'accès à un menu contextuel si tous ses sous-éléments sont grisés. (Vous pouvez argumenter que cela inhibe la découverte, mais, dans ce cas particulier, c'est ce dont nous avons besoin).

Comme les répondants précédents l'ont mentionné, si vous créez des menus par programme, vous pouvez stocker le handle de menu parent d'un élément en tant qu'information auxiliaire. Mais pour les menus définis dans le fichier de ressources à l'aide du mot-clé POPUP, vous ne pouvez pas associer un ID à un POPUP et vous ne pouvez pas grimper facilement dans l'arborescence de menus par programmation.Vous devez rechercher récursivement un élément de menu et suivre les parents.

J'ai écrit le code suivant pour ce faire. EnableSubmenuItem fonctionne comme EnableMenuItem pour activer ou désactiver un élément de menu par ID. Il vérifie ensuite le menu parent de l'élément. Si tous les éléments de son menu parent sont désactivés, le parent est désactivé. Inversement, si un sous-élément est activé, le parent est activé.

(BTW la question d'origine, comment trouver parent hMenu d'un élément de menu, est adressée par la routine FindParentMenu, dans le code suivant).

//////////////////////////////////////////////////////////////////////////////// 
// MenuContainsID - return TRUE if menu hMenu contains an item with specified ID 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuContainsID (HMENU hMenu, UINT id) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 
    MENUITEMINFO mf; 

    ZeroMemory(&mf, sizeof(mf));      // request just item ID 
    mf.cbSize = sizeof(mf); 
    mf.fMask = MIIM_ID; 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;)   // enumerate menu items 
     if (GetMenuItemInfo(hMenu, (UINT) pos, TRUE, &mf)) // if we find the ID we are looking for return TRUE 
      if (mf.wID == id) 
       return TRUE; 

    return FALSE; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// MenuItemIsSubmenu - returns TRUE if item # pos (position) of menu hMenu is a 
// submenu. Sets phSubMenu to menu handle if so 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuItemIsSubmenu (HMENU hMenu, UINT pos, HMENU *phSubMenu) 
{ 
    MENUITEMINFO mf; 

    ZeroMemory(&mf, sizeof(mf));      // request just submenu handle 
    mf.cbSize = sizeof(mf); 
    mf.fMask = MIIM_SUBMENU; 

    if (! GetMenuItemInfo(hMenu, pos, TRUE, &mf))  // failed to get item? 
     return FALSE; 

    *phSubMenu = mf.hSubMenu;       // pass back by side effect 
    return (mf.hSubMenu != NULL);      // it's a submenu if handle is not NULL 
} 

//////////////////////////////////////////////////////////////////////////////// 
// MenuItemIsEnabled - returns true if item # pos (position) of menu hMenu is 
// enabled (that is, is not disabled or grayed) 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL MenuItemIsEnabled (HMENU hMenu, UINT pos) 
{ 
    return ! (GetMenuState(hMenu, pos, MF_BYPOSITION) & (MF_GRAYED | MF_DISABLED)); 
} 

//////////////////////////////////////////////////////////////////////////////// 
// FindParentMenu - returns handle of the submenu of menu bar hMenu that contains 
// an item with id "id". Position of this submenu is passed by side effect to 
// pParentPos, and /its/ parent menu is passed by side effect through phGrandparentMenu. 
//////////////////////////////////////////////////////////////////////////////// 

static HMENU FindParentMenu (HMENU hMenu, UINT id, HMENU *phGrandparentMenu, UINT *pparentPos) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 
    HMENU hSubMenu, hx; 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;) { 
     if (MenuItemIsSubmenu(hMenu, (UINT) pos, &hSubMenu)) { 
      if (MenuContainsID(hSubMenu, id)) { 
       *phGrandparentMenu = hMenu;   // set grandparent info by side effect 
       *pparentPos = (UINT) pos; 
       return hSubMenu;      // we found the item directly 
      } 

      if ((hx = FindParentMenu(hSubMenu, id, phGrandparentMenu, pparentPos)) != NULL) 
       return hx;       // we found the item recursively (in a sub-sub menu). It set grandparent info 
     } 
    } 

    return NULL; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// AllSubitemsAreDisabled - returns TRUE if all items in a submenu are disabled 
//////////////////////////////////////////////////////////////////////////////// 

static BOOL AllSubitemsAreDisabled (HMENU hMenu) 
{ 
    int pos;           // use signed int so we can count down and detect passing 0 

    for (pos = GetMenuItemCount(hMenu); --pos >= 0;) 
     if (MenuItemIsEnabled(hMenu, (UINT) pos)) 
      return FALSE;        // finding one enabled item is enough to stop 

    return TRUE; 
} 

//////////////////////////////////////////////////////////////////////////////// 
// EnableSubMenuItem - like EnableMenuItem, enables or disables a menu item 
// by ID (only; not position!) where hMenu is top level menu. 
// Added bonus: If the item is in a pop-up menu, and all items in the popup are 
// now disabled, we disable the popup menu itself. 
// 
// Example: 
//  EnableSubMenuItem(hMainMenu, IDM_CONFIGURE, MF_GRAYED); 
// 
//////////////////////////////////////////////////////////////////////////////// 

void EnableSubMenuItem (HMENU hMenu, UINT id, UINT enable) 
{ 
    HMENU hParentMenu, hGrandparentMenu; 
    UINT parentPos; 

    // EnableMenuItem does its job recursively and takes care of the item 
    EnableMenuItem(hMenu, id, enable | MF_BYPOSITION); 

    // But popup menus don't have IDs so we have find the parent popup menu, and its parent (the 
    // grandparent menu), so we can enable or disable the popup entry by position 

    if ((hParentMenu = FindParentMenu(hMenu, id, &hGrandparentMenu, &parentPos)) != NULL) 
     EnableMenuItem(hGrandparentMenu, parentPos, 
      MF_BYPOSITION | (AllSubitemsAreDisabled(hParentMenu) ? MF_GRAYED : MF_ENABLED)); 
} 
Questions connexes