Help for Tune Smithy Koch snowflake icon.gif

How to show a dialog full screen

From Tune Smithy

(Difference between revisions)
Jump to: navigation, search
(Dialog template method)
 
(One intermediate revision not shown)
Line 119: Line 119:
   MENUBARINFO mbi;
   MENUBARINFO mbi;
   mbi.cbSize=sizeof(mbi);
   mbi.cbSize=sizeof(mbi);
-
   get_pGetMenuBarInfo_if_nec();
+
   GetMenuBarInfo(hDlg,OBJID_MENU,0/*menu itself*/,&mbi);
-
  if(pGetMenuBarInfo)
+
  menu_height=mbi.rcBar.bottom-mbi.rcBar.top;
-
  {
+
-
    pGetMenuBarInfo(hDlg,OBJID_MENU,0/*menu itself*/,&mbi);
+
-
    menu_height=mbi.rcBar.bottom-mbi.rcBar.top;
+
-
  }
+
   }
   }
   {
   {
Line 194: Line 190:
</source>
</source>
 +
If you want it to be backwards compatible with Win 95, use:
 +
 +
<source lang="c">
 +
#define OBJID_MENU 0xFFFFFFFD
 +
 +
typedef struct tagMENUBARINFO {
 +
  DWORD cbSize;
 +
  RECT  rcBar;
 +
  HMENU hMenu;
 +
  HWND  hwndMenu;
 +
  BOOL  fBarFocused:1;
 +
  BOOL  fFocused:1;
 +
} MENUBARINFO, *PMENUBARINFO;
 +
 +
PROC pGetMenuBarInfo;
 +
 +
HINSTANCE hUser32;
 +
 +
void get_pGetMenuBarInfo_if_nec(void)
 +
{
 +
static char did_this;
 +
if(did_this==0&&!pGetMenuBarInfo)
 +
{
 +
    if(!hUser32)
 +
  hUser32=LoadLibrary(TEXT("User32.dll"));
 +
    if(hUser32)
 +
  pGetMenuBarInfo=(PROC)GetProcAddress(hUser32,"GetMenuBarInfo");
 +
}
 +
}
 +
 +
...
 +
 +
  if(pGetMenuBarInfo)
 +
  {
 +
    pGetMenuBarInfo(hDlg,OBJID_MENU,0/*menu itself*/,&mbi);
 +
    menu_height=mbi.rcBar.bottom-mbi.rcBar.top;
 +
  }
 +
 +
</source>
If you want to do this in C++ then [http://msdn.microsoft.com/en-us/library/ms632626%28VS.85%29.aspx Full Screen Display and Implementing Drag to Move Dialogs] may be helpful. That's where I learnt about WM_GETMINMAXINFO in fact.
If you want to do this in C++ then [http://msdn.microsoft.com/en-us/library/ms632626%28VS.85%29.aspx Full Screen Display and Implementing Drag to Move Dialogs] may be helpful. That's where I learnt about WM_GETMINMAXINFO in fact.
The clip to window technique used here can also be used to make a dialog that is draggable without a title bar - clip in the same way, and make it draggable using the HTCAPTION trick - see [[How to make any part of a window or dialog draggable]]
The clip to window technique used here can also be used to make a dialog that is draggable without a title bar - clip in the same way, and make it draggable using the HTCAPTION trick - see [[How to make any part of a window or dialog draggable]]

Latest revision as of 11:06, 23 February 2010

C-code

First, the problem. It is easy to show a normal window full screen. One way to do that is to create it using CreateWindowEx(WS_EX_TOOLWINDOW,..) then resize it to fill the screen.

But for a dialog created using e.g. CreateDialog, then the problem is that it normally gets created with a border and title bar and you can't use the WS_EX_TOOLWINDOW approach. It may have a menu as well. So how to show it full screen without any border, title bar or menu?

Dialog template method

Idea is to have an alternative dialog template for the full screen version of each dialog. Just do a separate version of each template without menu, borders or title bar.

That should work. But it's a nuisance if your program has perhaps dozens or even hundreds of dialog templates. So another approach, perhaps you could work with the dialog templates themselves in memory. You would investigate CreateDialogIndirect and the DLGTEMPLATE structures to work with the template and dynamically remove the title bar, borders and menu from the template when you go to full screen - and reload the original template when you go back to normal size. I haven't tried this method, so it may have "gotchas" to deal with.

Resize and clip method

This has the advantage that the menu is still available via Alt keyboard shortcuts. This is how I did it so I know it works.

The idea is to resize the dialog so that its edges and title bar go outside the monitor. Then - so that you can't see the extra bits of it on a multiple monitor setup - clip the window to the client area.

First of all, we need to tell Windows that our dialog can be resized larger than the screen, otherwise it will helpfully resize it to fit the screen whenever we try.

So - in the dialog proc for the window use:

BOOL bFullScreen; // somewhere in the program this gets set when you go to full screen for the dialog
 
BOOL bIsFullScreenDialog(HWND hDlg)
{
 // simple case where you have just the one dialog
 // - write your own custom proc here
 return bFullScreen;
}
 
...
 switch(message)
 {
 case WM_GETMINMAXINFO:
  // bIsFullScreenDialog(hDlg)
  // returns TRUE if the 
  // dialog has been set to full screen
  if(!bIsFullScreenDialog(hDlg))
   break;
  {
   // http://msdn.microsoft.com/en-us/library/ms632626%28VS.85%29.aspx
   // see also http://msdn.microsoft.com/en-us/magazine/cc188910.aspx
   MINMAXINFO *pmmi=(MINMAXINFO *)lParam;
   // Pointer to a MINMAXINFO structure that contains the default maximized position and dimensions, and the default minimum and maximum tracking sizes. An application can override the defaults by setting the members of this structure.
   // Now work out the border size - this will depend on your dialog - and you can over estimate
   // since this is only for situation bIsFullScreenDialog(hDlg);
   int xborder=GetSystemMetrics(SM_CXEDGE)+GetSystemMetrics(SM_CXSIZEFRAME);
   int yborder=GetSystemMetrics(SM_CYEDGE)+GetSystemMetrics(SM_CYSIZEFRAME);
   int ytitle_bar=GetSystemMetrics(SM_CYCAPTION)+(GetMenu(hDlg)?GetSystemMetrics(SM_CYMENU):0);
   // The maximum tracking size is the largest window size that can be produced by using the borders to size the window. The minimum tracking size is the smallest window size that can be produced by using the borders to size the window.
   pmmi->ptMaxTrackSize.x=GetSystemMetrics(SM_CXVIRTUALSCREEN)+xborder*2;
   pmmi->ptMaxTrackSize.y=GetSystemMetrics(SM_CYVIRTUALSCREEN)+yborder*2+ytitle_bar;
   pmmi->ptMaxPosition.x=-xborder;
   pmmi->ptMaxPosition.y=-yborder;
  }
  break;
 }

Now resize the dialog so that it is larger than the screen. To take account of multiple monitors, detect which monitor the dialog is in first

BOOL hide_start_menu_for_full_screen=TRUE;
BOOL do_full_screen_dialogs_topmost=TRUE;
BOOL clip_to_monitor_for_full_screen_dialog=TRUE;
 
BOOL bHideMenuForFullScreenDialog(hDlg)
{
 return TRUE;// Whatever
}
 
...
 switch(message)
 {
 case WM_INITDIALOG:
  if(bIsFullScreenDialog(hDlg))
  {
   RECT rcmon;
   char do_topmost=0;
   RECT rcNewWin;
   rcNewWin=rcmon=rcRectMonitorForRect(&rcWas,MONITOR_WORKAREA);
   if(hide_start_menu_for_full_screen)
   {
    rcmon=rcRectMonitorForRect(&rcWas,MONITOR_AREA);
    if(!SameRect(&rcmon,&rcNewWin))
     do_topmost=1;// Must be topmost anyway to hide the start menu
    rcNewWin=rcmon;
   }
   // What you have here depends on the style of dialog.
   // But not to worry, will check it is okay and resize a second
   // time if nec.
   rcNewWin.top-=GetSystemMetrics(SM_CYSIZEFRAME);
   rcNewWin.bottom+=GetSystemMetrics(SM_CYSIZEFRAME);
   rcNewWin.left-=GetSystemMetrics(SM_CXSIZEFRAME);
   rcNewWin.right+=GetSystemMetrics(SM_CXSIZEFRAME);
   rcNewWin.top-=GetSystemMetrics(SM_CYEDGE);
   rcNewWin.bottom+=GetSystemMetrics(SM_CYEDGE);
   rcNewWin.left-=GetSystemMetrics(SM_CXEDGE);
   rcNewWin.right+=GetSystemMetrics(SM_CXEDGE);
   rcNewWin.top-=GetSystemMetrics(SM_CYCAPTION);
   if(GetMenu(hDlg)&&bHideMenuForFullScreenDialog(hDlg))
   {
    int cy_menu=GetSystemMetrics(SM_CYMENU);
    rcNewWin.top-=cy_menu;
   }
   MoveWindow(hDlg,rcNewWin.left,rcNewWin.top,rcNewWin.right-rcNewWin.left,rcNewWin.bottom-rcNewWin.top,bTF);
  // Check that it is as expected, with the client area filling the monitor.
  // - you don't need to do this bit normally, but it may help in some rare situations such as
  // menu wrapping over several lines - or if unsure of the size of the dialog borders
  {
   int menu_height=GetSystemMetrics(SM_CYMENU);
   // check the menu height now that the window has been made
   // - needs to be done after the MoveWindow(..)
   // The previous GetSystemMetrics(SM_CYMENU);
   // will give wrong height if the menu wraps over more than one line
   MENUBARINFO mbi;
   mbi.cbSize=sizeof(mbi);
   GetMenuBarInfo(hDlg,OBJID_MENU,0/*menu itself*/,&mbi);
   menu_height=mbi.rcBar.bottom-mbi.rcBar.top;
  }
  {
   RECT rcClient,rcWin;
   GetWindowRect(hDlg,&rcWin);
   GetClientRect(hDlg,&rcClient);
   ClientToScreenRect(hDlg,&rcClient);
   if(GetMenu(hDlg)&&!full_screen_dialog_hide_menu[sw_window])
    rcClient.top-=menu_height;
   if(!SameRect(&rcmon,&rcClient))
   {
    int left_margin=rcClient.left-rcWin.left;
    int top_margin=rcClient.top-rcWin.top;
    int right_margin=rcWin.right-rcClient.right;
    int bottom_margin=rcWin.bottom-rcClient.bottom;
    rcNewWin.left=rcmon.left-left_margin;
    rcNewWin.top=rcmon.top-top_margin;
    rcNewWin.right=rcmon.right+right_margin;
    rcNewWin.bottom=rcmon.bottom+bottom_margin;
    if(GetMenu(hDlg)&&bHideMenuForFullScreenDialog(hDlg))
     rcNewWin.top-=menu_height;
    MoveWindow(hDlg,rcNewWin.left,rcNewWin.top,rcNewWin.right-rcNewWin.left,rcNewWin.bottom-rcNewWin.top,bTF);
   }
  }
  // Now clip to the monitor
   if(clip_to_monitor_for_full_screen_dialog)
   {
    // Clip by edges showing outside the window
    HRGN hRgn=CreateRectRgn(rcmon.left-rcNewWin.left,rcmon.top-rcNewWin.top,rcmon.right-rcNewWin.left
     ,rcmon.bottom-rcNewWin.top);
    SetWindowRgn(hDlg,hRgn,FALSE);
   }
   if(do_topmost&&do_full_screen_dialogs_topmost)
    SetWindowPos(hwnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
   break;
  }
 }

Now just need to supply the extra routines we need for boilerplate type stuff - there's only one, to detect the size and position of the monitor nearest to the dialog:

#include <multimon.h>
#define MONITOR_WORKAREA 0x0002        // use monitor work area
#define MONITOR_AREA     0x0000        // use monitor entire area
 
RECT rcRectMonitorForRect(RECT *prc,DWORD flags)
{
 HMONITOR hMonitor;
 MONITORINFO mi;
 RECT        rc;
 // get the nearest monitor to the passed rect.
 //
 hMonitor = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
 
 //
 // get the work area or entire monitor rect.
 //
 mi.cbSize = sizeof(mi);
 GetMonitorInfo(hMonitor, &mi);
 
 if (flags & MONITOR_WORKAREA)
     rc = mi.rcWork;
 else
     rc = mi.rcMonitor;
 return rc;
}

If you want it to be backwards compatible with Win 95, use:

#define OBJID_MENU 0xFFFFFFFD
 
typedef struct tagMENUBARINFO {
  DWORD cbSize;
  RECT  rcBar;
  HMENU hMenu;
  HWND  hwndMenu;
  BOOL  fBarFocused:1;
  BOOL  fFocused:1;
} MENUBARINFO, *PMENUBARINFO;
 
PROC pGetMenuBarInfo;
 
HINSTANCE hUser32;
 
void get_pGetMenuBarInfo_if_nec(void)
{
 static char did_this;
 if(did_this==0&&!pGetMenuBarInfo)
 {
     if(!hUser32)
   hUser32=LoadLibrary(TEXT("User32.dll"));
     if(hUser32)
   pGetMenuBarInfo=(PROC)GetProcAddress(hUser32,"GetMenuBarInfo");
 }
}
 
...
 
   if(pGetMenuBarInfo)
   {
    pGetMenuBarInfo(hDlg,OBJID_MENU,0/*menu itself*/,&mbi);
    menu_height=mbi.rcBar.bottom-mbi.rcBar.top;
   }

If you want to do this in C++ then Full Screen Display and Implementing Drag to Move Dialogs may be helpful. That's where I learnt about WM_GETMINMAXINFO in fact.

The clip to window technique used here can also be used to make a dialog that is draggable without a title bar - clip in the same way, and make it draggable using the HTCAPTION trick - see How to make any part of a window or dialog draggable

Personal tools
Namespaces
Variants
Actions
Navigation
How to use the wiki
More
Toolbox