{"id":105889,"date":"2021-11-10T07:00:00","date_gmt":"2021-11-10T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105889"},"modified":"2021-11-10T06:45:57","modified_gmt":"2021-11-10T14:45:57","slug":"20211110-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211110-00\/?p=105889","title":{"rendered":"How am I supposed to create children of the Win32 tab control?"},"content":{"rendered":"<p>Say you&#8217;re trying to write your own tabbed dialog. You start with the Win32 tab control to get the tabs at the top of the window. But how do you position the controls that belong to each of the pages?<\/p>\n<p>Let&#8217;s say, for simplicity, that your tab control has three tabs, and they let you select among three different edit controls.<\/p>\n<p>Your natural inclination would be to make the page controls children of the tab control:<\/p>\n<table style=\"text-align: center;\" title=\"Main dialog at the top, with a single child: the tab control. The tab control has three children: Edit 1, Edit 2, and Edit 3.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"5\">Main dialog<\/td>\n<\/tr>\n<tr>\n<td colspan=\"5\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"5\">Tab control<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\">Edit 1<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">Edit 2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">Edit 3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When the user is on a particular tab, you show that tab&#8217;s edit control and hide the other two.<\/p>\n<p>The problem with this arrangement is that navigating the dialog with the <kbd>Tab<\/kbd> key doesn&#8217;t work.<\/p>\n<p>Recall that the windows which participate in the tab order are those which are immediate children of the dialog frame, but with the special rule that if any of those children is marked <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code>, then it disappears from the tab order, replaced by its children. The <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code> rule applies recursively, so if any of the children have the style, then they too disappear and are replaced by their own children.<\/p>\n<p>In the above diagram, if we assume that none of the windows is marked as <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code>, then the tab order consists only of the tab control itself. You can&#8217;t tab into the children.<\/p>\n<table style=\"text-align: center;\" title=\"Same structure as above, with the tab control highlighted in blue. The edit controls are not highlighted.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"5\">Main dialog<\/td>\n<\/tr>\n<tr>\n<td colspan=\"5\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\" colspan=\"5\">Tab control<\/td>\n<\/tr>\n<tr style=\"color: gray;\">\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr style=\"color: gray;\">\n<td style=\"border: solid 1px gray;\">Edit 1<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray;\">Edit 2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray;\">Edit 3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>I&#8217;ve highlighted in blue the windows that participate int he tab order. By default, the windows that participate in the tab order are the immediate children of the main dialog, which in this case is the tab control.<\/p>\n<p>On the other hand, if you mark the tab control as <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code>, then you can tab into the children, but the tab control itself disappears from the tab order. The <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code> style means &#8220;Replace me in the tab order with my children.&#8221;<\/p>\n<table style=\"text-align: center;\" title=\"Same structure as above, with the tab control no longer highlighted. Instead, the edit controls are highlighted.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"5\">Main dialog<\/td>\n<\/tr>\n<tr>\n<td colspan=\"5\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"5\">Tab control<br \/>\n<span style=\"font-size: 60%;\">(WS_EX_CP)<\/span><\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 1<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Win32 doesn&#8217;t let a control and its children all participate in the tab order. You have to choose whether you want the control or its children, but not both.<\/p>\n<p>The solution is to take the page controls out from under the shadow of the tab control.<\/p>\n<table style=\"text-align: center;\" title=\"Main dialog at the top, but the tab control and edit controls are all direct children of the main dialog and are all highlighted.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"7\">Main dialog<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Tab control<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 1<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 2<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Edit 3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Setting the controls as siblings of each other gets them all into the tab order. We set the edit controls at a higher z-order, so they draw on top of the tab control.<\/p>\n<p>In practice, you probably have a lot of controls on each page, not a single edit control. Hiding and showing all of those controls can be a hassle, but you can use <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code> to save you some effort:<\/p>\n<table style=\"text-align: center;\" title=\"Main dialog at the top, with four children: tab control and three pages. The tab control is highlighted. The pages are labeled WS_EX_CP and are not highlighted. The pages each have two children, and those children are highlighted.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"13\">Main dialog<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"3\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"3\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"3\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">Tab control<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"3\">Page 1<br \/>\n<span style=\"font-size: 60%;\">(WS_EX_CP)<\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"3\">Page 2<br \/>\n<span style=\"font-size: 60%;\">(WS_EX_CP)<\/span><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\" colspan=\"3\">Page 3<br \/>\n<span style=\"font-size: 60%;\">(WS_EX_CP)<\/span><\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">1a<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">1b<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">2a<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">2b<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">3a<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black; background-color: #eaf7fb;\">3b<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Wrap each group of controls inside a parent page control, and mark the page control as <code>WS_<wbr \/>EX_<wbr \/>CONTROL\u00adPARENT<\/code>. This is easy to arrange by making the page controls themselves be dialog boxes. That also gives you the convenience of having a separate dialog procedure for each page.<\/p>\n<p>You can then hide or show an entire group of controls by hiding or showing the page parent. This also has the advantage of preserving the hide\/show states of the inner controls, so if the rule was that control 1b is shown only if control 1a is checked, then hiding page 1 will hide everything, and then showing page 1 will bring them back in the same state they were in as they were previously.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>They look like children, but they&#8217;re not.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-105889","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>They look like children, but they&#8217;re not.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105889","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=105889"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105889\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=105889"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105889"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105889"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}