Click here to Skip to main content
Click here to Skip to main content

Dynamic Items

By , 24 Feb 2003
 

with System Tray

without dynamic item with dynamic items read from a file With new items read from the same modified file

with Modal Application

without dynamic item with dynamic items read from a file With new items read from the same modified file

Introduction

Creating menus can be done by building the menu statically in resources, or creating it fully dynamically using CMenu creation operation. But how dynamically add items at runtime to a menu created in resource? And how handle these items dynamically (if we don't know by advance how much they are)? Here is an easy solution.

My sample code shows you how to dynamically add and handle items to a menu in system tray, grabbing these items from an external file. The main advantage is that you can share this file on a network or else, and modify at runtime all items who are in this file.

Method

  1. First of all, create your menu in resources, including only the static items :
    Menu in resources 
    If you want no static items, but a full dynamic menu, just create a menu in the resource with dummy item like this:
    Dummy Menu in resources 
  2. Then, use the ClassWizard to create command handlers for the static items if they are some (ON_COMMAND, and ON_UPDATE_COMMAND_UI if you want to do menu enabling).

    e.g. from my sample code you have in the message map of the "MainFrame.cpp" file :

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	//{{AFX_MSG_MAP(CMainFrame)
    	ON_WM_CREATE()
    	ON_COMMAND(ID_POPUP_OPTIONS, OnPopupOptions)
    	...
    	ON_COMMAND(ID_POPUP_EXIT, OnClose)
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()

     

  3. In the "Mainframe.h" header file, add the followings:
      #include "DynamicItems.h" to use DynamicItems class manager, and
      CDynamicItems m_DynamicItems;

    Still in "MainFrame.h", modify the message map as follow:

    	//}}AFX_MSG
    	afx_msg void OnExecuteDynamicMenu( UINT nID );
    	DECLARE_MESSAGE_MAP()

    Now, imagine you want the ability to manage a maximum of 10 dynamic items.
    (This number is a maximum, you can have less items if you want, but no more than 10 will be handled :

    In the "Mainframe.cpp" file, Add the following line in the message map:

    //}}AFX_MSG_MAP
      	ON_COMMAND_RANGE( ID_POPUP_DynCmd01, <BR>                           ID_POPUP_DynCmd01 + CDynamicMenu::GetNbMaxItems(), <BR>                           OnExecuteDynamicMenu )
    END_MESSAGE_MAP()
    

    The CDynamicMenu::GetNbMaxItems() method returns Maximum numbers of items which can be handled simultaneously on the menu.
    You can increase this static value to be sure your menu will manage a very high number of items, but don't forget you'll be limited by the size of your screen !

    This is a rarely-used but very powerful alternative message map macro, and this is the foundation of my dynamic items management.


  4. Then, in CMainframe.cpp, add for example the following code:
    void CMainFrame::OnExecuteDynamicMenu( UINT nID )
    {
            	// Retrieve the selected dynamic item Name
    
            	CString strItemText = m_DynamicItems.GetItemText(nID);
    
            	// Retrieve the selected dynamic item associated comment
            	CString strItemComment = m_DynamicItems.GetItemComment(nID);
    
            	// Display it.
    
            	CString strMsg="";
    
            	strMsg.Format("Cmd %d selected.\nItem Name: %s\nAssociated data: %s",
            		     (nID -ID_POPUP_DynCmd01), strItemText, <BR>                        strItemComment );
    
            	AfxMessageBox(strMsg);
    }
    
    This method is called every time you click on a dynamic item in the menu.
    So, you have to modify this method to handle menu as you wish.
    In the sample code, you'll see that this method contains an easy but very powerful item management... Use it and enhance it as you want !

  5. When and where do I tell my app to load my items from a file?

    Loading items from the file and updating menu in memory is done in one line of code :

      UpdateDynamicItems(strFullFilePath);
    I have decorrellated the process of physically updating data by reading a file, and the process of displaying menu for a much easier use. You can call this method when you think it is necessary : using a timer to refresh items every X minutes, or when the user right-click on an icon in the system-tray, etc... Whenever you want !
  6. Sample Code:

    There is two sample codes :

    1. A sample code which uses the system tray :
      It checks a file every X minutes using a timer to update menu (the file to check and the duration between each check is customizable). You can modify at runtime the selected file, or using the 2 files given with the sample named "TestFile.txt" and "TestFile2.txt". The header of these tests files have comments on how to format datas. When you click on an item, a message box is displayed, telling you what is the name of the selected item, and what is its associated comment.
      Usage example: An application which use a shared file containing Name/Email of present people connected to internet ;-)

    2. A modal application which allow you to load/remove menus when you want:
      This sample code shows you how to load/remove menu items on a modal application. It is very easy to implement, and very easy to use, just have a look at the code...
      Usage example: Plugin list applications which can be enhanced just by modifying the menu, or sharewares where you can add more menu items when user is regged by sending him the regged menu file, etc...

Conclusion

That's all. Your menu has now the ability to add and manage very easily dynamic items. Just have a look to the samples code to have an idea on how it works.

Use and enjoy. Please let me know of any bugs/mods/improvements that you have found/implemented and I will fix/incorporate them into this class in a future release.

Acknowledgements

Thanks to Lionel Grenier for ideas on improving this code.

Thanks Chris Maunder for his CSystemTray class used in this sample code.

History

Version 1.1 (01/27/2003):
Enhanced and easiest way to manage dynamic items.
Added new sample code with modal app.
Now the menu is really dynamic !


Version 1.0 (05/17/2001):
First "public" version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

David Excoffier
Team Leader
France France
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalafxwin1.inl line: 1038 debug assertion failedmemberRocky B.27 Aug '07 - 21:22 
Is there anybody else encountering this error?
 
possible pointer to a menu popup error?
 
I have narrowed the problem to the ff:
 
in DynamicItems.cpp Line 315:
pSubMenu->InsertMenu(.. MF_POPUP.. )
 
SystemTray.cpp line 865, 869:
TrackPopupMenu();
 
Thanks in advance.
GeneralRe: afxwin1.inl line: 1038 debug assertion failedmemberkimtaehee25 Mar '08 - 15:45 
See this subject
MDI Bug/Assert Fix Sully 14:10 27 Feb '03
GeneralDynamic change of MaxItemsmemberVjys29 Jul '07 - 20:25 
Hai,
Thanks for the article.....
I have small doubt, Can we change the MaxNo of items dynamically???
Hope got my doubt.waiting for solution..
And how can we get the selected menu item string.
I think you have stored some array type and doing it..
Can we get the selected menu item string..CMenu has many member functions like GetMenustring, GetMenuInfo soon..Can we use this functions here..If so how to use this to get it..
 


 

 
Regards
vijay

GeneralWindows 98: ON_UPDATE_COMMAND_UI_RANGE not calledmemberibthas17 Jun '07 - 23:02 
Hey!
First if all, thx for this great class!
 
Unfortunately, there seem to be some issues under Win98: Neither the Modal-Dialog example nor my own dialog-based app shows the dynamic menu items there.
 
In your modal-based example, the LoadMenu command doesn't show the file dialog. In my own app, the dynMenu items are just "shrinked", they show no text at all and have a very small line height. Futhermore, the ON_UPDATE_COMMAND_UI_RANGE macro seems to not call the associated handler method at all.
 
Both apps work great under XP. The Systray example works on Win 98 and XP.
 
I found several potential reasons for these issues on the WWW: Resource IDs over 3267 can cause problems on Win98, Modal-Dialog-Apps have problems to update menus without an ON_WM_INITMENUPOPUP handler (which your dialog-based example doesn't have neither?!) the update-ui-range macro fails if it's menu IDs have no subsequent values (no problem here), and there could be unicode problems.
 
As your example seems to be compiled under VC6 with no unicode support and the resources are in the same range as in your systray example - why does the first example fail, while the other one works?
 
Does anybody have any ideas?
 
ibthas
QuestionCan .Dat file be unicodemembernix32111 Jul '06 - 10:01 
Is it possible to upgrade this project to support unicode .Dat file so that resouce can be in forign lang.
Currently it is not
 
zcsfds
QuestionHow to color the menu barmemberAlex Evans20 Jun '04 - 14:44 
Under XP, can I pain the menu bar myself, ignoring the Windows theme on the PC
 
Hope someone can help
 
Thanks
Alex
GeneralMenu bar colouringmemberAlex Evans20 Jun '04 - 14:24 
How can I change the MENU BAR colour (assuming XP only) from whatever te Windows theme is
 
Hope someone can help
 
Thanks
Alex
Generald:\down\2_code\DynItems_modal_demo\DynItems_modal_demo\DynamicItems.h(146): fatal error C1083: Cannot open include file: 'fstream.h': No such file or directorymemberhaoshenghan17 Jun '04 - 2:05 
I was compling it with VS2003,then the error occurs,can
you help me?Smile | :)
GeneralRe: d:\down\2_code\DynItems_modal_demo\DynItems_modal_demo\DynamicItems.h(146): fatal error C1083: Cannot open include file: 'fstream.h': No such file or directorymemberBamaco29 Feb '05 - 5:06 
Use the new STL header filenames:
 
#include
 
Instead of the older way
 
#include
 
IMHO, "fstream.h" refers to "local project files", but most compilers will
lookup in files when "local" are not found.
AnswerRe: d:\down\2_code\DynItems_modal_demo\DynItems_modal_demo\DynamicItems.h(146): fatal error C1083: Cannot open include file: 'fstream.h': No such file or directorymemberRenny[RuS]10 Nov '05 - 22:46 
I cannot fight this error too Frown | :(
After changing --#include [fstream.h]-- to --#include [fstream]-- 13 error(s) and 3 warning(s) appear. Buildlog is saved to http://rapidshare.de/files/7474319/buildlog.html
VS2003EA.
 
-- modified at 6:11 Friday 11th November, 2005
GeneralRe: d:\down\2_code\DynItems_modal_demo\DynItems_modal_demo\DynamicItems.h(146): fatal error C1083: Cannot open include file: 'fstream.h': No such file or directorymemberpiotrmb25 Apr '06 - 4:44 
It's easy - just add
 
using namespace std;
 
and create ifstream object this way:
 
i_file = new ifstream (pFileName);
 

Although the question is 1 year old, maybe this answer would be useful to someone trying to use this code Smile | :)
GeneralRe: d:\down\2_code\DynItems_modal_demo\DynItems_modal_demo\DynamicItems.h(146): fatal error C1083: Cannot open include file: 'fstream.h': No such file or directorymemberRenny[RuS]25 Apr '06 - 23:08 
Hurrah! It works!!!
QuestionMemory leak?membersilorenz10 Mar '04 - 16:41 
After closing the notify program, the developer studio displays memory leaks.
Memory is allocated during the pushback in: void addItem(CMenuItem* item) { m_items.push_back(item); } but
is never been deleted.
Can somebody help me with this?
I really like this class and would like to use it, but without the memory leak Wink | ;)

AnswerRe: Memory leak?membersilorenz12 Mar '04 - 1:39 
Found the problem:
delete in function CPopMenuItem::clear() always calls the
CMenuItem destructor. This is wrong if the pointer which was passed
in addItem was pointing to a CPopMenuItem object.
Make the following changes in CPopMenuItem::clear():
 
void clear()
{
    std::vector< CMenuItem* >::iterator iter = m_items.begin();
    for ( ; iter != m_items.end(); ++iter )
    {
        (*iter)->clear();
        if ((*iter)->getType() == POPUP)
         {
               CPopMenuItem* pPopupCurrentItem = (CPopMenuItem*)(*iter);
               delete pPopupCurrentItem;
         }
         else

              delete (*iter);
    }
    m_items.clear();
}
GeneralChange the &quot;Dyn Menu&quot; entrymemberMichael Klim15 Jan '04 - 9:46 
Hi
 
Is it possible to rename "Dyn Menu" dynamicly in my running app?
 
greetz, Michael
GeneralRE: Location of TestFilesmemberltayyebi27 Aug '03 - 5:55 
Does anyone know where I can find these files: "TestFile.txt" and "TestFile2.txt". They were supposed to be included in the sample code.
GeneralRe: RE: Location of TestFilesmemberDavid Excoffier27 Aug '03 - 7:51 
Confused | :confused:
I assure you these two files are in the sample code, but not in the source zip.
These 2 files are in the system tray demo project, and in the modal app project...
Just download these zip files, open them, and you'll find them.
Best regards.
Blush | :O
 
David Excoffier
http://www.excoffier.com
GeneralMDI Bug/Assert FixmemberSully27 Feb '03 - 11:10 

The dynamic popup menu items need to detach, that is, they need to surrender ownership of the menu.
 
===[DynamicItems.cpp]======
 
void CDynamicItems::addDynamicPopupMenu(CMenu* pSubMenu, CPopMenuItem* menu_item)
{
//...
 
switch ( PopupCurrentItem->getType() )
{
case CMenuItem::POPUP:
{
CMenu* menu = new CMenu();
menu->CreatePopupMenu();
addDynamicPopupMenu(menu, (CPopMenuItem*)(PopupCurrentItem));
pSubMenu->InsertMenu( 0, MF_BYPOSITION | MF_POPUP,
(UINT)menu->GetSafeHmenu(),
PopupCurrentItem->getName().c_str() );
 
menu->Detach(); // <<----- ADD THIS! Dead | X|
 
delete menu;
}
break;

QuestionHot Coffee?memberJörgen Sigvardsson24 Feb '03 - 21:36 
Tea, Earl Grey, Hot!
 
http://www.tealand.com/Famous.htm[^]
 
Smile | :)
 
--
Say it darling
Doesn’t seem like you want that kind of honey, honey

AnswerRe: Hot Coffee?memberAndreas Saurwein24 Feb '03 - 23:12 
Jörgen, what exactly is your message here? Unsure | :~ Suspicious | :suss:
 

powerful binary resource reuse - another word for "no sources, you are stuck with a pain-in-the-a## COM component"
GeneralGreat Job & little improvementmemberlebios31 Dec '02 - 9:54 
Your code is great..I was looking for some ideas like this for a long time...And here is your code...
But..(sorry)...As it is not 100% dynamic since you fixed in advance the size of your "dynamic" menu I won't consider it as dynamic...But...I work around it to have it working 100% dynamic without changing a resource.h file..
Right now it is crappy code coding but i think people here can   start with that..
The idea is to have " ON_COMMAND_RANGE( ID_POPUP_DynCmd01, ID_POPUP_DynCmd20, OnExecuteDynamicMenu )" fully dynamic without any conflict with resource id. So I define a variable
in the Main frame
#define ID_DYN_MENU_FIRST_CMD 99000 (high unsual number to be sure it won't conflict to some id).
Then we have to know how many items we have in our menu.
So I create a public static variable in CDynamicItems (m_nb_items) that count the number of elemt in the menu.
And so now you can have :
     ON_COMMAND_RANGE( ID_DYN_MENU_FIRST_CMD, ID_POPUP_DynCmd01+CDynamicItems::m_nb_items, OnExecuteDynamicMenu )
Also don't forget to create a function in CDynamicItems to set the first ID (ie: m_DynamicItems.setFirstItem(ID_DYN_MENU_FIRST_CMD); )that is called in the mainframe constructor...
And now you can remove the line in the resource.h file that contains the "dynamic" items.
 
It works fine for me...I can have any number of plugin with menu item... And i don't care about the resource !!
 
Is that clear for everybody ? I can make a zip file with the   previous project with my added code...
But thanks at dexcoffier@yahoo.fr for his great first idea ...
 
Lionel Grenier
Virtual Environment Programmer

GeneralRe: Great Job & little improvementmemberEPHERE6 Feb '03 - 11:45 
Nice improvement! One comment through. When I tried 99000 in VC6.0 it wouldn't work normally. My guess is that UINT doesn't go that high. Visual Studio re-assigns new values (something around 34360+-). But otherwise this is very helpful because it saved me a lot of code.
 
10q
 
EPHERE
GeneralRe: Great Job & little improvementmemberDavid Excoffier7 Feb '03 - 0:57 
Wink | ;) The article will be updated soon with new sample codes (systray & modal app) including update v1.1 ...
Keep an eye on it !
GeneralPerformed an illegal operation...memberKing Coffee4 Nov '02 - 12:26 
When running this program under Win 98, I get the following error: "This program has performed an illegal operation and will be shut down." This error occurs then I try to highlight (fly-over) one of the file's menu choice.
 
Confused | :confused:
Thanks,
King
GeneralCool maismemberMoz29 May '01 - 22:07 
ou est passé Aziz ?
Wink | ;) Smile | :) Big Grin | :-D
 
Moz
Tunis - Tunisia

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 25 Feb 2003
Article Copyright 2001 by David Excoffier
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid