billroper: (Default)
[personal profile] billroper
MFC does not have wonderful support for changing languages on the fly in a running application by reloading the underlying resource DLL. It turns out that this is something you can do -- you just have to get your incantations straight.

There are two places where loaded menus should reside in an MFC application -- in the CFrameWnd that represents the application's mainframe window and in any CMultiDocTemplate classes that belong to the app, assuming that you're running an MDI application. When you reload the resources, you need to unload and replace the menu and accelerator resources for the mainframe and for each of the CMultiDocTemplates.

That's fairly easy to do. The CWinApp owns the templates (via the CDocManager class), so it should be responsible for unloading the menus there:


void CMyDocTemplate::ReloadTemplate()
{
// Destroy the resources from CDocTemplate
m_strDocStrings.Empty();

// Delete OLE resources from CDocTemplate
if (m_hMenuInPlace != NULL) {
::DestroyMenu(m_hMenuInPlace);
m_hMenuInPlace = NULL;
}

if (m_hAccelInPlace != NULL) {
::FreeResource(m_hAccelInPlace);
m_hAccelInPlace = NULL;
}

if (m_hMenuEmbedding != NULL) {
::DestroyMenu(m_hMenuEmbedding);
m_hMenuEmbedding = NULL;
}

if (m_hAccelEmbedding != NULL) {
::FreeResource(m_hAccelEmbedding);
m_hAccelEmbedding = NULL;
}

if (m_hMenuInPlaceServer != NULL) {
::DestroyMenu(m_hMenuInPlaceServer);
m_hMenuInPlaceServer = NULL;
}

if (m_hAccelInPlaceServer != NULL) {
::FreeResource(m_hAccelInPlaceServer);
m_hAccelInPlaceServer = NULL;
}

// Delete shared components from CMultiDocTemplate
if (m_hMenuShared != NULL) {
::DestroyMenu(m_hMenuShared);
m_hMenuShared = NULL;
}

if (m_hAccelTable != NULL) {
::FreeResource((HGLOBAL)m_hAccelTable);
m_hAccelTable = NULL;
}

// Now, reload the resources
LoadTemplate();
}


This function unloads all of the menu resources that are normally loaded by a CMultiDocTemplate, then calls LoadTemplate to reload them.

Once your app has fixed up the templates, you need to fix up the MainFrame. You probably want to define and send a message indicating a locale change, for example, WM_LOCALE_CHANGE. You can catch that message in the CMainFrame and execute a code snippet like so:


CMenu* pOldMenu = GetMenu();
CMenu NewMenu;
NewMenu.LoadMenu( IDR_MAINFRAME );
SetMenu( &NewMenu );
m_hMenuDefault = NewMenu.Detach();

SendMessageToDescendants( WM_LOCALE_CHANGE );

pOldMenu->DestroyMenu();


Your CMDIChildWnd-derived classes will need to catch this message and reset their copies of the menu handles:


LRESULT CMyMDIChildWnd::OnLocaleChanged( WPARAM wParam, LPARAM lParam )
{
CDocument* pDoc = GetActiveDocument();

if ( pDoc ) {
CMultiDocTemplate* pTemplate = dynamic_cast< CMultiDocTemplate* >( pDoc->GetDocTemplate());

if ( pTemplate ) {
m_hMenuShared = pTemplate->m_hMenuShared;
m_hAccelTable = pTemplate->m_hAccelTable;
}
}

return 1;
}


And that'll do it. Unless you have some modeless dialogs hanging around...

Profile

billroper: (Default)
billroper

June 2025

S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 2728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 28th, 2025 08:03 am
Powered by Dreamwidth Studios