title: Approaches for reducing rate of resource file growth as dialogs are enhanced/extended
body: <p>As part of extending the functionality of a dialog in an old Windows WinAPI GUI application written in C, I was faced with once more adding multiple check boxes for each line of a six line data entry dialog. I just could not bear the hassle of repetitive resource file and source code file changes and decided to borrow a UI design approach from Java UIs of building up a UI from panes.</p>
  
  <p>The Visual Studio tools, at least Visual Studio 2005, seem to discourage this approach and in this case I hand edited the resource file. Perhaps the resource editing tools of Visual Studio 2017 are more flexible.</p>
  
  <p><em>My question is what is an alternative to this approach that would seem to be as easy to do and would better fit with the Visual Studio philosophy.</em></p>
  
  <p>I am also wondering about the downside of this approach.</p>
  
  <p>This approach seems unusual for a Visual Studio C WinAPI GUI application which troubles me. I can't claim to be especially innovative so I wonder what I am missing as the approach seems to work well at least when doing hand edits of the resource file.</p>
  
  <p>I am considering doing another iteration in which I move the list of controls for each line that is repeated into the modeless dialog box template as well and just having the original dialog be a stack of 6 static windows, one for each line.</p>
  
  <p>Benefits of this approach was fewer defines and being able to reuse defines. It was also easier to insert the new functionality into the existing dialog behavior source code though that was mostly because these were just simple auto check boxes.</p>
  
  <p>The one issue I see is using the Visual Studio tools after doing this change. However this particular application's resource file doesn't work well with the Visual Studio resource editing tools anyway.</p>
  
  <p>This approach has already had a payback when I needed to add some additional checkboxes to the modeless dialog template. The resource file changes I had to do to was to add the additional checkboxes to the new dialog template and adjust the original dialog size, the modeless dialog size, and the sizes of the static windows to make everything visible.</p>
  
  <p><strong>The Implementation</strong></p>
  
  <p>The alternative I have implemented is to:</p>
  
  <ul>
  <li>create a dialog template with the set of checkboxes</li>
  <li>modify the dialog template style of the modeless dialog to WS_CHILD</li>
  <li>create a static window on each of the six lines of the original dialog for the new dialog template</li>
  <li>place an instance of the modeless dialog box into the static window on each line</li>
  </ul>
  
  <p>The new version of the dialog looks like <a href="https://i.stack.imgur.com/FYph8.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/FYph8.png" alt="screen shot of new version of the data entry dialog"></a>
  When the original dialog is displayed, the handler for the init dialog message creates a set of six modeless dialogs, one for each of the newly added static windows with the parent window for the dialogs being the static window. This places the modeless dialog into the static window and when the static window moves so does the modeless dialog.</p>
  
  <p>All six of the modeless dialogs use the same dialog message handler. The message handler doesn't handle any messages itself.</p>
  
  <p>The modeless dialog template is:</p>
  
  <pre><code>IDD_A170_DAYS DIALOG DISCARDABLE  0, 0, 240, 20
  STYLE  WS_CHILD | WS_VISIBLE
  FONT 8, "MS Sans Serif"
  BEGIN
      CONTROL         "Ovr",IDD_A170_STR1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,1,25,10
      CONTROL         "AND",IDD_A170_STR2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,35,1,40,10
      CONTROL         "S",IDD_A170_CAPTION1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,1,20,10
      CONTROL         "M",IDD_A170_CAPTION2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,100,1,20,10
      CONTROL         "T",IDD_A170_CAPTION3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,1,20,10
      CONTROL         "W",IDD_A170_CAPTION4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,1,20,10
      CONTROL         "T",IDD_A170_CAPTION5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,175,1,20,10
      CONTROL         "F",IDD_A170_CAPTION6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,195,1,20,10
      CONTROL         "S",IDD_A170_CAPTION7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,220,1,20,10
  END
  </code></pre>
  
  <p>and the main dialog with the static windows is:</p>
  
  <pre><code>IDD_A170 DIALOG DISCARDABLE  2, 17, 530, 190
  STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
  CAPTION "Set Sales Code Restriction Table of PLU (AC 170)"
  FONT 8, "MS Sans Serif"
  BEGIN
      LTEXT           "Address              (PLU Sales Code)",IDD_A170_CAPTION1,14,10,64,20
      LTEXT           "Date",IDD_A170_CAPTION2,86,14,28,12
      LTEXT           "Day of week",IDD_A170_CAPTION3,115,10,33,21
      LTEXT           "Start hour",IDD_A170_CAPTION4,153,10,20,18
      LTEXT           "Minute",IDD_A170_CAPTION5,182,14,26,12
      LTEXT           "End hour",IDD_A170_CAPTION6,217,10,20,18
      LTEXT           "Minute",IDD_A170_CAPTION7,245,14,26,12
      LTEXT           "Override/Type",IDC_STATIC,290,14,50,12
      LTEXT           "Days To Restrict",IDC_STATIC,390,14,100,12
      LTEXT           "",IDD_A170_STR1,8,34,74,12                     // first control on line 1
      EDITTEXT        IDD_A170_DATE1,87,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_DATESPIN1,104,33,8,12,SBS_VERT
      EDITTEXT        IDD_A170_WEEK1,119,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_WEEKSPIN1,136,33,8,12,SBS_VERT
      EDITTEXT        IDD_A170_SHOUR1,151,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_SHOURSPIN1,168,33,8,12,SBS_VERT
      EDITTEXT        IDD_A170_SMINUTE1,183,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_SMINUTESPIN1,200,33,8,12,SBS_VERT
      EDITTEXT        IDD_A170_EHOUR1,214,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_EHOURSPIN1,231,33,8,12,SBS_VERT
      EDITTEXT        IDD_A170_EMINUTE1,246,33,18,12,ES_AUTOHSCROLL
      SCROLLBAR       IDD_A170_EMINUTESPIN1,263,33,8,12,SBS_VERT
      LTEXT           "D1",IDD_A170_DAYS_1,281,33,240,20             // static window to contain the modeless dialog box from the template IDD_A170_DAYS above
      //   .. repeated sequence for 5 more lines
      CONTROL         "MDC 298 - Sales Restriction Type is AND",IDD_A170_MDC_PLU5_ADR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,140,170,9
      LTEXT           "[Address :  1 - 6, Date : 0 - 31",IDD_A170_CAPTION8,9,154,99,9
      LTEXT           "Day of week : 0 - 7 (1 - Sunday, 7 - Saturday)]",IDD_A170_CAPTION9,110,154,167,9
      LTEXT           "[Hour : 0 - 24, Minute : 0 - 59 (For 0:00, enter 24:00)]",IDD_A170_CAPTION10,9,168,167,9
      PUSHBUTTON      "&amp;Ok",IDOK,285,154,48,20
      PUSHBUTTON      "&amp;Cancel",IDCANCEL,345,154,48,20
  END
  </code></pre>
  
  <p>You may notice that I just reused some of the defines in the new modeless dialog box that were already being used in the original dialog box. I was able to do so because control identifiers are specific to the dialog box itself. So using the same define in different dialog boxes does not cause a problem since the use of <code>GetDlgItem()</code> to obtain the window handle of a control within a dialog box requires the window handle of a specific dialog instance.</p>
  
  <p>I then created a set of helper functions that handle an instance of the modeless dialog.</p>
  
  <pre><code>static struct {
      int   iId;
      HWND  hWnd;
  } A170DlgTabs[10] = { {0, 0} };
  
  // modeless dialog box message handler which has nothing to do but the
  // WinAPI requires it.
  BOOL    WINAPI  A170DlgChildProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  {
          return FALSE;
  }
  
  void  A170ModeLessChildDialogClear ()
  {
      memset (A170DlgTabs, 0, sizeof(A170DlgTabs));
  }
  
  HWND A170ModeLessChildDialog (HWND hParentWnd, int nCmdShow, int iId)
  {
      int   i;
      HWND  hWnd = DialogCreation(hResourceDll/*hActInst*/,  //RPH 4-23-03 Multilingual
                                     MAKEINTRESOURCEW(IDD_A170_DAYS),
                                     hParentWnd,
                                     A170DlgChildProc);
      hWnd &amp;&amp; ShowWindow (hWnd, nCmdShow);
      for (i = 0; i &lt; sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
          if (A170DlgTabs[i].hWnd == 0) {
              A170DlgTabs[i].iId = iId;
              A170DlgTabs[i].hWnd = hWnd;
              break;
          }
      }
  
      return hWnd;
  }
  
  HWND A170ModeLessChildDialogFind (int iId)
  {
      int   i;
      HWND  hWnd = NULL;
  
      for (i = 0; i &lt; sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
          if (A170DlgTabs[i].iId == iId) {
              hWnd = A170DlgTabs[i].hWnd;
              break;
          }
      }
  
      return hWnd;
  }
  
  USHORT A170ModeLessChildDialogSettings (int iId)
  {
      int     i;
      USHORT  iBits = 0, kBits = 1;
      HWND hWnd = A170ModeLessChildDialogFind (iId);
  
      // least significant byte contains the bit mask for the days of the week.
      // the next higher byte contains the indicators for the override type or
      // whether MDC 298 is to be overriden or not.
      for (i = IDD_A170_CAPTION1; i &lt;= IDD_A170_CAPTION7; i++, (kBits &lt;&lt;= 1)) {
          iBits |= IsDlgButtonChecked (hWnd, i) ? kBits : 0;
      }
  
      iBits |= iBits ? RESTRICT_WEEK_DAYS_ON : 0;
  
      iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR1) ? KBITS_RESTRICT_OVERRIDE_ANDOR : 0;
      iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR2) ? KBITS_RESTRICT_OVERRIDE_AND : 0;
  
      return iBits;
  }
  
  
  USHORT A170ModeLessChildDialogSettingsSetMask (int iId, USHORT  usMask)
  {
      int     i;
      USHORT  k = 1;
      HWND    hWnd = A170ModeLessChildDialogFind (iId);
  
      CheckDlgButton(hWnd, IDD_A170_STR1, (usMask &amp; KBITS_RESTRICT_OVERRIDE_ANDOR) ? TRUE : FALSE);
      CheckDlgButton(hWnd, IDD_A170_STR2, (usMask &amp; KBITS_RESTRICT_OVERRIDE_AND) ? TRUE : FALSE);
  
      for (i = IDD_A170_CAPTION1; i &lt;= IDD_A170_CAPTION7; i++, (k &lt;&lt;= 1)) {
          CheckDlgButton(hWnd, i, (usMask &amp; k) ? TRUE : FALSE);
      }
  
      return usMask;
  }
  </code></pre>