Thread: Control parent/child relationship

  1. #1
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70

    Control parent/child relationship

    This program is only my second foray into GUI programming. The first was a homework assignment with FLTK. This one is a personal project in Win32. This is raw Win32, no visual this, visual that, .net this, .net that, etc. This is hand written code in Code::Blocks. So I'm learning Win32 as I go. Admittedly, this is a bit of a large project to be doing as a second jump into GUI and first jump into Win32. But I couldn't help it, and I'm having fun.

    Anyway, when I need to look something up or ask a question about something, I've been googling. Sometimes I come across MSDN pages describing the components, sometimes I end up somewhere else like stackoverflow or a forum. Microsoft's own site, MSDN, may as well be encrypted in some English version of Navajo code talk half the time whenever I end up there. Win32 info elsewhere is, for some reason, actually kind of hard to come across. Definitely hard to get good explanations of certain things.

    So what I'd like is some help explaining the parent/child relationship, and how a sort of tree of these relationships affects tab-key behavior and other things like focusing, etc. I'm finding the information online hard to decipher and sift through. Can anyone explain this stuff very simply? I think part of the reason for the confusion is the whole Window vs window thing, so can we just call buttons, boxes, etc controls instead for this discussion?

    I have the following window.

    The parent/child hierarchy is as follows:

    Code:
    main window
            main window wndproc (handles the tab switching, acts by hiding the main static control on each tab, which hides all controls for that tab
            tab control
                    tab1 (static control; wndproc for this tab is tied to this control - (SetWindowLongPtr(tab1, blahblah) )
                            all controls being shown on this tab
                    tab2 (static control; wndproc for this tab is tied to this control - (SetWindowLongPtr(tab2, blahblah) )
                            all controls being shown on this tab
                    tab3 etc...
    So the tab control is a child of the main window (the whole entire window itself). Each tab has a static control with nothing displayed (the tutorial I read said this was one of the ways to get a tabbed interface to work), where each control being shown on that tab's visual space is a child of that static control. So clicking one one of the tabs is handled in the main windows wndproc, and it acts by hiding that static control, and with it, all of it's children seem to automatically get hidden. So going between the interface tabs is fine. Each tab also has it's own wndproc to handle messages from all of the controls shown on it's space. This in the SetWindowLongPtr function used to swap out a wndproc, the first parameter is the static control on that tab.

    First of all, I'd like to know if this is fine so far with the parent/child relationship tree.

    Second, I'm having a problem trying to get the tab key to work right. I've googled around and tried all that I found (and could make sense of). I have the IsDialogMessage in the message loop, and all of the controls have WS_TABSTOP. But depending on what I try, the tab key either only cycles through all of the controls on a tab's area, or it cycles through all of the tabs themselves in the interface at the top. I can't get it to do both. Like, go through all of the controls on a tab, then at the end, jump up to the tabs, then when going to the next tab, jump from IT, down to that tab's controls. Is this even possible? Does it make sense to do this?

    Third, when I switch between the tabs, I'd like any control on the tab being switched away from to lose focus. Because even though that tab is now hidden because it was switched away from, if something has focus, keyboard presses are still acting on the hidden controls. I couldn't really find a way to do this, even after lots of googling. There's a set focus, but it seems that the kill focus isn't something that was supposed to be used manually? I don't know, but I couldn't get this to work without doing something hokey like just manually changing the focus to the first control on the new tab being shown.

    I know this is long, but I hope it makes sense and I appreciate any help.

  2. #2
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by Sorin View Post
    Microsoft's own site, MSDN, may as well be encrypted in some English version of Navajo code talk half the time
    MSDN does take sometime to get to grips with. Deciphering it is a skill you learn…

    Quote Originally Posted by Sorin View Post
    So what I'd like is some help explaining the parent/child relationship, and how a sort of tree of these relationships affects tab-key behavior and other things like focusing, etc.
    In WIN32 everything is considered a window. Think of this like C++ inheritance, with the window as the base class and everything else as a derived class.

    The parent -> child relationship (primarily) is used in WIN32 to reflect / direct messages to the right place to be processed by your app.

    Quote Originally Posted by Sorin View Post
    So the tab control is a child of the main window (the whole entire window itself).
    Good, windows will send notification messages to the TABs parent via WM_NOTIFY.

    Quote Originally Posted by Sorin View Post
    snip
    I understand, not the way I used to do it, I used a individual dialog for each TAB (when they were as complex as yours).

    My memory says static controls do not get / generate all WIN32 messages so may not work, but I am not 100% if this is an issue (not having tested it).

    Quote Originally Posted by Sorin View Post
    But depending on what I try, the tab key either only cycles through all of the controls on a tab's area, or it cycles through all of the tabs themselves in the interface at the top. I can't get it to do both.
    TABSTOP is releative to the parent window, so the numbers can be repeated on different windows.

    Only the window that has focus is processing TAB stop messages.

    [try; clicking on the main window (to give it focus). Now the TAB will move between the tabs interface at the top. Click on the TAB control (or one of its controls), now the TAB key will move you through the controls on the tab area.]

    You would need to process the WM_GETDLGCODE msg and activate the next / previous tab group and set focus to the first control when the last control (highest / lowest TABSTOP number) was reached. GetNextDlgTabItem() or GetNextGroupTabItem()

    Quote Originally Posted by Sorin View Post
    Does it make sense to do this?
    No (IMO).

    You are changing a basic UI (or UX) principle.

    You would remove the users ability to TAB between pages (whole groups).

    Quote Originally Posted by Sorin View Post
    manually changing the focus to the first control on the new tab being shown.
    Sound like you need to set a control as the default, but nothing wrong with manually setting focus (IMO).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  3. #3
    Registered User
    Join Date
    Nov 2008
    Location
    Phoenix
    Posts
    70
    Quote Originally Posted by novacain View Post
    I understand, not the way I used to do it, I used a individual dialog for each TAB (when they were as complex as yours).
    Since I'm still new to Win32, could you spell this out a little more for me? At the moment, each tab has a static control on its visual space. All of the controls on a given tab are a child of that static control. That parent static control is also used to swap out the main window's wndproc for a different wndproc specifically to handle all of the controls on that specific tab.

    Did you mean something different than this?

    And is there a better way to handle this setup than to have everything on a tab childed from a static control representing that tab?

    Quote Originally Posted by novacain View Post
    My memory says static controls do not get / generate all WIN32 messages so may not work, but I am not 100% if this is an issue (not having tested it).
    Maybe that's why my tabstops don't work.

    My message loop looks like this:

    Code:
        while(GetMessage(&msg, NULL, 0, 0) > 0)
        {
            if((!hDlgCurrent) || (!IsDialogMessage(hDlgCurrent, &msg))) 
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    The main wndproc and each tab's wndproc look like this:

    Code:
    switch(msg)
        {
            //...
            case WM_ACTIVATE:
                if(wParam == 0)
                    hDlgCurrent = NULL;
                else
                    hDlgCurrent = hwnd;
                break; //or "return false" too;
            //...
    But the tabbing doesn't seem to work. Regardless of which control on a tab has focus, pressing the tab key makes the focus jump to the actual little tab item near the top of the interface.

    In the message loop, if I swap out the first parameter of IsDialogMessage from hDlgCurrent to the name of the main static control for a particular tab (say, hTabGenerals), then tabbing works (although obviously only for that specific tab). Clearly, this won't fly because then only one tab's viewspace could ever have tabbing work.

    From my understanding, I believe the point of the above code was to create exactly this functionality, but to work regardless of which tab is currently active. I think I'm obviously missing something somewhere or I misunderstood what I read.

    Quote Originally Posted by novacain View Post
    Sound like you need to set a control as the default, but nothing wrong with manually setting focus (IMO).
    Ok, it sounded ghetto to me at first, but that's purely from lack of knowledge and experience. So I'll just go with manual-focus on tab-selection-change part.
    Last edited by Sorin; 03-18-2013 at 02:06 AM.

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by Sorin View Post
    Since I'm still new to Win32, could you spell this out a little more for me? At the moment, each tab has a static control on its visual space. All of the controls on a given tab are a child of that static control. That parent static control is also used to swap out the main window's wndproc for a different wndproc specifically to handle all of the controls on that specific tab.

    Did you mean something different than this?

    And is there a better way to handle this setup than to have everything on a tab childed from a static control representing that tab?
    In WIN32 there are usually a dozen right ways to do something...

    For a settings page as complex as yours I would create a new dialog for all the controls you have grouped under each static control.
    Each dialog wwould be loaded when the app started (but not shown), made visible when needed (its TAB index selected), hidden when not needed (any other TAB selected) and cleaned up when the app closed.

    When a dialog was required (or the app got a WM_MOVE) I would use SetWindowPos() to position the dlg in the TABs client area (and call ShowWindow()).

    I would pass a pointer to the struct containing the settings into the dlg using the CREATESTRUCT (or similar, depending if it was C or C++ / MFC).

    You can also use GetParent() etc to pass this pointer.


    Quote Originally Posted by Sorin View Post
    But the tabbing doesn't seem to work. Regardless of which control on a tab has focus, pressing the tab key makes the focus jump to the actual little tab item near the top of the interface.
    Don't get too distracted by 'minor' functionality. Make sure everything major works (ie settings are passed in and set in the right controls and any changes are reflected back to the parent).


    Quote Originally Posted by Sorin View Post
    In the message loop, if I swap out the first parameter of IsDialogMessage from hDlgCurrent to the name of the main static control for a particular tab (say, hTabGenerals), then tabbing works (although obviously only for that specific tab).
    I assume hDlgCurrent is a global HWND?

    Try....

    Set hDlgCurrent to the first static control's HWND in WM_CREATE (the one you seelct when the dlg first opens).

    When you get a TAB change msg (WM_NOTIFY), set the hDlgCurrent to the selected TAB's static control's HWND.

    (ie when the TAB changes, change the HWND IsDialogMessage() is using)

    You will need a function to convert the selected TTAB index into the correct static controls HWND.

    OR

    Hold the static HWNDs in an array.

    Code:
    //not tested, written from memory...
    //declare array to hold static ctrls HWNDS (must be static of will lose values)
    static HWND hTABStatics[numTABs] = {0};
    
    case WM_CREATE:
    	hTABStatics[0] = GetDialogItem(hwnd, IDC_STATIC1);//the static for TAB index 0 
    	hTABStatics[1] = GetDialogItem(hwnd, IDC_STATIC2);//the static for TAB index 1 
    	hTABStatics[2] = GetDialogItem(hwnd, IDC_STATIC3);//the static for TAB index 2 
    	hTABStatics[3] = GetDialogItem(hwnd, IDC_STATIC4);//the static for TAB index 3 
    	//set the global HWND to the static we want to have focus
    	hDlgCurrent = hTABStatics[0];
    //etc....
    
    
    break;
    case WN_NOTIFY:
    	NMHDR *nmptr = (LPNMHDR) lParam;
    	int tabNumber = -1; //the TAB controls selected TAB index
    	if(nmptr->code == TCN_SELCHANGE)
    	{
    		tabnumber = TabCtrl_GetCurSel((HWND)nmptr->hwndFrom);
    		hDlgCurrent = hTABStatics[tabnumber];
    
    //etc....
    
    break;

    BTW I remembered that static controls never can have focus (be selected) and so do not generate focus or loss of focus message. Should not be too much of an issue as the TAB index is generating these for you.
    Last edited by novacain; 03-18-2013 at 08:56 PM. Reason: error in code
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. IPC communication between parent and child
    By Durango2011 in forum Linux Programming
    Replies: 1
    Last Post: 10-23-2012, 04:36 AM
  2. Have Child, Got Parent, need Sibling
    By motocross1 in forum C++ Programming
    Replies: 9
    Last Post: 02-11-2010, 03:06 PM
  3. calling parent from child
    By Aemon07 in forum Windows Programming
    Replies: 5
    Last Post: 07-25-2007, 09:27 PM
  4. C# parent child
    By BraneMxm in forum C# Programming
    Replies: 2
    Last Post: 06-02-2007, 10:24 AM
  5. Creating Child with parent of Child
    By SilkySmooth in forum Windows Programming
    Replies: 3
    Last Post: 06-28-2003, 12:15 PM