TabControl class

Started by
8 comments, last by Endurion 18 years, 3 months ago
Hey guys. I've been working on this project of mine lately yet I have encountered an issue that neither my friends nor myself could solve. I am trying to make a little (nothing too fancy) wrapper for a few controls that I need. My current problem is that when I create and use my TabControl class, it doesn't show the control on the main window. The tab just won't show up, which is quite annoying. I've tried using my error logger class but everything succeeds everywhere so I must have missed something important elsewhere :
///////////////////////////////////////////////////////////
//                                                       //
//                    Interface module                   //
//                                                       //
//  The interface module regroups many classes that      //
//  represent the different controls used in the         //
//  application.
//                                                       //
///////////////////////////////////////////////////////////


class IControl : public IMMObject
{
    protected:
        IControl();
        virtual ~IControl() { }
        
    public:
        virtual void OnResize() = 0;
        virtual void OnEnable() = 0;
        virtual void OnDisable() = 0;
    
    public:
        static HWND hwnd_top_parent;
        bool enabled;
        HWND hwnd_self;
        int control_id;
};


class TabControl : public IControl
{
    public:
        TabControl() { };
        
        void OnResize();
        void OnEnable();
        void OnDisable();
        
        void Create(HWND, const char*, int, RECT);
        void Create(HWND, const char*, int, int, int, int, int);
        void AddTab(LPTSTR, int);
        
    protected:
        struct Parent
        {
            Parent() : old_wnd_proc(0) { }
            std::list<SmartPtr<TabControl> > tab_list;
            WNDPROC old_wnd_proc;
        };
        
        static LRESULT CALLBACK SubWndProc(HWND, UINT, WPARAM, LPARAM);
        static std::map<HWND, Parent> tab_list;
        RECT display_area;
        bool active;
};
///////////////////////////////////////////////////////////
//                                                       //
//                   Interface module                    //
//                                                       //
///////////////////////////////////////////////////////////


// static variable
HWND IControl::hwnd_top_parent;

IControl::IControl() : enabled(true)
{
    INITCOMMONCONTROLSEX icc = { 0 };
    icc.dwSize  = sizeof(icc);
    icc.dwICC   = ICC_TAB_CLASSES;
    bool err = InitCommonControlsEx(&icc);
    
    ErrorLogger.WriteMessage(__FILE__,
                            __LINE__,
                            TL_FATAL,
                            err == true,
                            "Initializing common controls");
}


// static variable
std::map<HWND, TabControl::Parent> TabControl::tab_list;

void TabControl::OnResize()
{
    if(active && enabled) SendMessage(hwnd_self, 
                                        TCM_ADJUSTRECT, 
                                        (WPARAM)true, 
                                        (LPARAM)&display_area);
}

void TabControl::OnEnable()
{
    // Provided for compatibility with IControl
}

void TabControl::OnDisable()
{
    // Provided for compatibility with IControl
}

void TabControl::Create(HWND hwnd, const char* caption, int id, RECT rc)
{
    Parent* p = &tab_list[hwnd];

    hwnd_self = CreateWindowEx(0,
                                WC_TABCONTROL,
                                caption,
                                WS_CHILD | WS_VISIBLE,
                                rc.left,
                                rc.top,
                                rc.right - rc.left,
                                rc.bottom - rc.top,
                                hwnd,
                                NULL,
                                GetModuleHandle(0),
                                NULL);

    ErrorLogger.WriteMessage(__FILE__, 
                            __LINE__,
                            TL_WARNING,
                            hwnd_self != INVALID_HANDLE_VALUE,
                            "TabControl handle creation");
                            
    p->tab_list.push_back(this);
    if(p->old_wnd_proc == NULL)
    {
        p->old_wnd_proc = (WNDPROC)SetWindowLongPtr(hwnd_self, 
                                                    GWLP_WNDPROC, 
                                                    (LONG_PTR)SubWndProc);
                                                    
        ErrorLogger.WriteMessage(__FILE__,
                                __LINE__,
                                TL_WARNING,
                                p != NULL,
                                "TabControl sub-classing operation");
    }
    display_area = rc;
}

void TabControl::Create(HWND hwnd, const char* caption,
                        int id, int x, int y, int x2, int y2)
{
    RECT rc = { x, y, x2, y2 };
    Create(hwnd, caption, id, rc);
}

void TabControl::AddTab(LPTSTR txt, int id)
{
    TCITEM tci  = { 0 };
    tci.mask    = TCIF_TEXT;
    tci.pszText = txt;
    int err = TabCtrl_InsertItem(hwnd_self, id, &tci);
    
    ErrorLogger.WriteMessage(__FILE__, 
                            __LINE__,
                            TL_WARNING,
                            err != -1,
                            "TabControl adding a tab");
}

LRESULT CALLBACK TabControl::SubWndProc(HWND hwnd, UINT msg, WPARAM wprm, LPARAM lprm)
{
    Parent* p = &tab_list[hwnd];
    
    std::list<SmartPtr<TabControl> >::iterator iter;
    for(iter = p->tab_list.begin(); iter != p->tab_list.end(); iter++)
    {
        switch(msg)
        {
            case WM_DESTROY:
                MessageBox(0,0,0,0);
                break;
            default:
                return p->old_wnd_proc(hwnd, msg, wprm, lprm);
        }
    }
    return 0;
}
Quote:[core.cpp:326] -> Initializing common controls succeeded [main.cpp:54] -> Creating first tab succeeded [core.cpp:372] -> TabControl handle creation succeeded [core.cpp:385] -> TabControl sub-classing operation succeeded [core.cpp:408] -> TabControl adding a tab succeeded
Do you guys have any idea ? I want to answer it explicitly before anyone asks the odd and obvious question "did you call InitCommonControlsEx()", yes. It may not appear to you guys very easily since it's not that obvious but I placed it in IControl's empty constructor. Many thanks.
Advertisement
If CreateWindow fails it does not return INVALID_HANDLE_VALUE. A invalid HWND value is usually NULL (or 0 if you prefer).

Also, your check for the subclassing does not really check the success of the subclassing (which would be a return value different from NULL).

[Edited by - Endurion on January 1, 2006 12:14:55 AM]

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Changed it but it still says it succeeded.
Hmm, the next option: Use the Spy which comes with Visual Studio and look through the window list (or use the search feature of it). You ought to see if the tab control is out there somewhere and maybe it is just the RECT that is off.

Also, from your Create function: The function is set up to use the right and bottom coordinates, check if you didn't pass width and height instead (which might be smaller than the top left edge coordinates).

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Here's the WinMain code. That's all I can do right now since the connection I am working on right now is too slow to download programs, I'll download WinSpy when I get back home:
#include "core.hpp"#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){    WNDCLASSEX wc = { 0 };    wc.cbSize           = sizeof(wc);    wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW + 1);    wc.hIcon            = LoadIcon(0, IDI_WINLOGO);    wc.hIconSm          = LoadIcon(0, IDI_WINLOGO);    wc.hCursor          = LoadCursor(0, IDC_ARROW);    wc.lpfnWndProc      = WndProc;    wc.lpszClassName    = "blah";    RegisterClassEx(&wc);        HWND hwnd = CreateWindow("blah",                                 "",                                WS_OVERLAPPEDWINDOW,                                0,                                 0,                                 CW_USEDEFAULT,                                 CW_USEDEFAULT,                                HWND_DESKTOP,                                0,                                0,                                0);    UpdateWindow(hwnd);    ShowWindow(hwnd, true);        MSG msg;    while(GetMessage(&msg, 0, 0, 0) > 0)    {        TranslateMessage(&msg);        DispatchMessage(&msg);    }        return 0;}LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wprm, LPARAM lprm){    static SmartPtr<TabControl> tab;    switch(msg)    {        case WM_CREATE:            {                ErrorLogger.Create(true);                RECT rc = {0, 0, 100, 100};                tab = new TabControl;                                ErrorLogger.WriteMessage(__FILE__,                                        __LINE__,                                        TL_FATAL,                                        tab != NULL,                                        "Creating first tab");                                tab->Create(hwnd, "Tab, whatever", 0, rc);                tab->AddTab("blah", 2);            }            break;        case WM_DESTROY:            PostQuitMessage(0);            break;        default:            return DefWindowProc(hwnd, msg, wprm, lprm);    }    return 0;}
*Edit* Don't mind the SmartPtr<> class, I know it works as I want it to, I've tested it on dummy classes to make sure that it doesn't bother me later.
The size is passed allright. The tab control should be there.
You might want to get the Spy and find the thing.

Minor nitpick:

ErrorLogger.WriteMessage(__FILE__,
__LINE__,
TL_FATAL,
tab != NULL,
"Creating first tab");

does not check if the tab is created, it merely checks if the new call worked.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Well, I tried using WinSpy++ and it tells me that it found the tab control created. What does it mean ? I made sure to use the WS_VISIBLE and WS_CHILD styles and I even tried calling UpdateWindow() to no avail :\
A tab control controls other windows. Perhaps, you may not see anything because you haven't yet added windows for the tab control to control? Before checking all that, check if the window width and height are set. WinSpy++ should allow for that.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
I have somewhat spotted the error. I removed the code relative to the subclassing and it solved the problem; therefore, I simply decided not to subclass it since I didn't really have a good reason to do so. If you guys have any idea what was wrong with the subclassing code, though, I'd be very happy to know your opinion.
Hmm, i see some flaws, but i'm not sure they're the cause:

o You shouldn't call a window proc directly, use CallWindowProc instead

o If you have more than one tab control only the first tabs window proc will be stored in the old_wnd_proc. Moreso, if you have more than one tab, you iterate your tab list but return on the first entry (unless the message is WM_DESTROY). This should all work out but it seems weird.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

This topic is closed to new replies.

Advertisement