Thread: Building a tab custom control

  1. #1

    Building a tab custom control

    [edit:] Alrighty, I've almost got the drawing working ... but there is one problem at least at the moment.

    I'm passing a pointer to the struct TABSTRUCT.

    In the drawing code, it has to loop through an array of LPSTR vars in the struct and write the text. But for some reason it only writes 1 (out of the 2 in the array ) and then it crashes ( on the next WM_PAINT )!

    [edit2:] If I reduce the number of tabs ( an int variable nTabs ) to 1 it will do it fine until the control is resized! At this point the text becomes garbled!
    Last edited by Mithoric; 03-02-2004 at 09:07 AM.

  2. #2
    int ids[1] = { 100 };
        LPSTR titles[1] = { "test" };
        TABSTRUCT *ts = GetTabs( tabs.hSelf );
        ts->nTabs = 1;
        ts->ids = ids;
        ts->titles = titles;
        ts->set = true;
    That's how I set the struct. GetTabs retrieves the pointer from the control set previously ( on create ) with setwindowlong().

    And this is where the trouble is --
    for( int i = 0; i < ts->nTabs; i++ ) {
           tab.right += 84;
           ins.left = tab.left+8;
           ins.right = tab.right-2;
           ins.bottom = tab.bottom-2;
           FillRect( dc, &tab, red );
           DrawText( dc,ts->titles[i], -1, &ins, DT_LEFT|DT_VCENTER );
           tab.left = tab.right + 2;
    it's passed as a pointer into the function which eventually uses it in the above.. ( ts is the pointer to the TABSTRUCT )...

  3. #3
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    From your sample code, it maybe that titles is going out of scope.

    case WM_CREATE:
       LPSTR titles[1] = { "test" };
    If the code is like that, the titles array will not be available in WM_PAINT.

  4. #4
    I see, well it's actually in the WM_CREATE like you said ( although it's the WM_CREATE of my application window not the tab control. ) but scope is still probably the problem. How can I fix this?

  5. #5
    Registered User dalek's Avatar
    Join Date
    May 2003
    Make it static
    static LPSTR titles[1] = { "test" };
    And do this at the beginning of the windows procedure, prior to processing any messages. If you declare the variable in the switch block, it will only be available within the block that it is declared.
    Last edited by dalek; 03-02-2004 at 08:55 PM.

  6. #6
    It works!

  7. #7
    Okely dokes, next problem.

    I've defined a message --

    #define MCC_TABSEL WM_USER+3000

    And when you select a tab it sends that message to the parent window.

    Currently I am sending the title of the newly selected tab as the WPARAM. But I'd like to send more than that, problem is I can't get the MAKEWPARAM and MAKELPARAM to work. I want to send in the WPARAM -

    LOWORD - Title (it's a LPSTR)
    HIWORD - index (it's an int)

    But how do I do it??

    Thanks so far.

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    sizeof(LPSTR) == sizeof(WPARAM). Therefore a LPSTR is not going to fit in half a WPARAM.

    If you need to pass more than what you can fit in WPARAM and LPARAM pass a pointer to a struct. Remember, that the sender of the message is responsible for memory cleanup. The message may not be handled on the receiving end.

  9. #9
    Yeah I had thought passing a struct would be more efficient.

    Thanks for the tip..

  10. #10
    It's working well so far, all I need to do is -

    adding tabs
    removing tabs
    moving tabs
    displaying data
    context menu on right click tab

    Now, adding and removing tabs shouldn't be so much of a problem... And it's not my main focus right now.

    To the point -- how should I display data? Using winspector I can see that dev-c++ uses a window the size of the display area for each tab and the tab data is just displayed on that window, then on tab change it's hidden and the corresponding one is shown... I think..

    Is this a good way to go about it?

  11. #11
    Registered User Dante Shamest's Avatar
    Join Date
    Apr 2003
    I wrote a custom tab control recently, and that's basically what I did.

    If you notice, Dev-C++ also allows you to drag individual tabs to change their position. I also implemented that feature, and you might want to do that too.

  12. #12
    Yeah, that's on my list to do ... and closing a tab when you use the middle mouse button ( usually push down on scroll wheel or whatever ) .. Firefox uses that on it's tabs and I couldn't live without it.

    [edit:] Yahoo!! It's working! I attached 2 giant buttons to 2 pages ( out of 4 ) and it works.

    It's a long way from done but it's working. Thanks guys.

    [edit2:] With an exception -- When I resize my whole app window ( the tabs get stretched ) the button dissapears on the page control.. If you click where it supposed to be it shows up again however.. What should I do? I've tried invalidating and updating both the page and tab on resize but that didn't do it.
    Last edited by Mithoric; 03-03-2004 at 09:32 AM.

  13. #13
    Registered User Dante Shamest's Avatar
    Join Date
    Apr 2003
    How are you handling the WM_SIZE event of your window?

    I suspect its something to do with the Z-order of your button.

  14. #14
    So I should enumerate the chlid windows of the page control and set them to be above the page control??

    I have this in my tab wndproc to handle resizing of the client pages.

    case WM_SIZE: {
              RECT rc;
              GetClientRect( hwnd, &rc );
              for ( int i = 0; i < ts->nTabs; i++ ) {
                MoveWindow( ts->pages[i], 1,28,rc.right-2,rc.bottom-29,FALSE);
                InvalidateRect( ts->pages[i], NULL, TRUE );
                UpdateWindow( ts->pages[i] );
              //InvalidateRect( hwnd, NULL, FALSE );
              //UpdateWindow( hwnd );
              return 0;
    I added those invalidates and updates to see if it'd fix the problem but it doesn't seem to have..

    [edit:] Alrighty, previously I just had separate functions and you to get the struct pointer and set the values yourself to setup the tabs. Now however it does it all through messages. It creates and deletes page controls and assigns them to specific tabs, re-orders tabs, closes tabs on middle button down and even sends a message to the parent when you right click a tab... So it's coming along pretty well ( thanks to you guys )

    But apart from that drawing problem I've mentioned I have 1 more problem.

    Seeing as now everything is done in the window procedure the static variables are also created there ... I sized them into arrays with malloc and use realloc to remove and add tabs so my problem is how to I free them if they were declared in the WM_NCCREATE message??

    They are declared as follows --

    static int * ids;
    static LPSTR *titles;
    static HWND *pages;
    Last edited by Mithoric; 03-04-2004 at 04:02 AM.

  15. #15
    I know I must be getting really annoying by now ... so I'm sorry but here's yet another problem, well actually a few.

    Firstly this is my struct which is refered to as ts (TABSTRUCT*) in most of the code below, it's loaded before going into the switch msg thingy.

    typedef struct {
        LPSTR *titles;
        int *ids;
        HWND *pages;
        bool set;
        int nTabs;
        int active;
        int rfocus;
    Everything is initialised like this --
    case WM_NCCREATE: {
              ts = (TABSTRUCT*)malloc( sizeof( TABSTRUCT ) );
              if ( ts == NULL ) {
                MessageBox ( NULL, "wm_nc.tabproc Failed!", "Error", MB_ICONERROR );
                return FALSE;
              SetTabs( hwnd, ts );
              static int *ids;
              static LPSTR *titles;
              static HWND *pages;
              ts->ids = ids;
              ts->titles = titles;
              ts->pages = pages;
              ts->nTabs = ts->rfocus = 0;
              ts->active = -1;
              return TRUE;
    1. After a while the program crashes when making new tabs.

    CODE --
    case MCC_ADDTAB: {
              if( ts->active >= 0 ) {
                ShowWindow( ts->pages[ts->active], SW_HIDE );
              ts->ids = (int*)realloc( ts->ids, ts->nTabs*sizeof(int) );
              ts->titles = (LPSTR*)realloc( ts->titles, ts->nTabs*sizeof(LPSTR) );
              ts->pages = (HWND*)realloc( ts->pages, ts->nTabs*sizeof(HWND) );
              ts->ids[ts->nTabs-1] = lParam;
              ts->titles[ts->nTabs-1] = (LPSTR)wParam;
              ts->pages[ts->nTabs-1] = CreatePage( hwnd, 1 );
              ts->set = true;
              SendMessage( GetParent( hwnd ), MCC_TABSEL, (WPARAM)ts->titles[ts->active],(LPARAM)ts->active );
              InvalidateRect( hwnd, NULL, FALSE );
              UpdateWindow( hwnd );
              return 0;
    I think that's enough to get started with. And thanks so far, you people are far extremely helpful ... and tolerant ..
    Last edited by Mithoric; 03-04-2004 at 08:47 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Button handler
    By Nephiroth in forum Windows Programming
    Replies: 8
    Last Post: 03-12-2006, 06:23 AM
  2. Tab order in Tab Control
    By Halloko in forum Windows Programming
    Replies: 2
    Last Post: 05-08-2005, 11:08 PM
  3. tab control
    By tyouk in forum Windows Programming
    Replies: 6
    Last Post: 02-07-2005, 11:47 PM
  4. Custom Tab Control
    By PrivatePanic in forum Windows Programming
    Replies: 8
    Last Post: 07-19-2002, 01:23 PM
  5. Tab Controls - API
    By -KEN- in forum Windows Programming
    Replies: 7
    Last Post: 06-02-2002, 09:44 AM