Thread: superclassed treeview

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    superclassed treeview

    i created a class that encapsulated a superclassed tree view control. however, i can only use the class once in my program. i have added a check so that it doesn't try to Register the class again, and the error occurs during CreateWindowEx. here is the class:

    Code:
    class cTree
    {
    private:
    protected:
    public:
    	cTree(HWND parent=NULL);
    	~cTree();
    	static LRESULT CALLBACK cTreeProcA(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
    	virtual LRESULT CALLBACK cTreeProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
    	HWND Create(HWND parent,HINSTANCE hInstance);
    	HTREEITEM Add(char *chText);
    	HTREEITEM Add(char *chText,HTREEITEM parent);
    	//HTREEITEM Add(char *chText,HTREEITEM parent,HTREEITEM insAfter);
    
    	HWND hTree;
    	HWND hParent;
    	WNDPROC wpPrev;
    	HINSTANCE hInst;
    	HIMAGELIST hList;
    	int left,top,width,height;
    
    	static BOOL classReg;
    };
    
    HWND cTree::Create(HWND parent,HINSTANCE hInstance)
    {
    	WNDCLASSEX wcx;
    	
    	hInst=hInstance;
    	hParent=parent;
    
    	if (classReg!=1)
    	{
    		GetClassInfoEx(NULL,WC_TREEVIEW,&wcx);
    	
    		wpPrev=wcx.lpfnWndProc;
    		wcx.lpfnWndProc=(WNDPROC)cTreeProcA;
    		wcx.hInstance=hInst;
    		wcx.lpszClassName="cTree";
    		wcx.cbSize=sizeof(wcx);
    		if (!RegisterClassEx(&wcx))
    			return NULL;
    	}
    	classReg=1;
    
    	hTree=CreateWindowEx(WS_EX_CLIENTEDGE,"cTree",NULL,
    					WS_BORDER | WS_CHILD | WS_VISIBLE | 
    					TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT,
    					left,top,width,height,hParent,NULL,hInst,(void *)this);
    	if (!hTree)
    		return NULL;
    
    	return hTree;
    }
    the way that i implement the class is this:

    Code:
    hwnd=CreateWindowEx(//parent window);
    ShowWindow(...);
    UpdateWindow(...);
    
    cTree *ct1=new cTree(hwnd);
    //set dimensions
    ct1->Create(hwnd,hInst);
    
    cTree *ct2=new cTree(hwnd);
    //set dimensions
    ct2->Create(hwnd,hInst);
    only the first tree view control is created. for any subsequent attempts, CreateWindowEx returns NULL. i cant remember ever using a superclass twice in a program, but surely it's possible.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    Registered User johnnie2's Avatar
    Join Date
    Aug 2001
    Posts
    186
    What does GetLastError() have to say about it? What does the WM_CREATE portion of your procedure look like?
    "Optimal decisions, once made, do not need to be changed." - Robert Sedgewick, Algorithms in C

  3. #3
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I'm not sure what you mean about the WM_CREATE area, but it looks normal to me, just as if I tried to create a child control but it didn't work.

    GetLastError returns 1400 or "Invalid Window Handle". I thought perhaps that it meant the parent window handle I passed in was invalid, but that's not possible, as the erroneous control is created directly after the working control, with the same code, and in the same function.

    I think the problem is to do with the super class I created. Somehow, it only wants to be used once in a program.

    I'll post my project up, maybe it will be easier that way.
    Actually it's too big to post. Hmmm... I'll just create a new project with only this feature in it.

    -------A little later--------

    Ok, nobody will believe me, but in the new project I created it actually works fine. I hate it when that happens.

    Probably the problem is related to the fact that the original project contained about 50 different, interlinked classes. I guess I'll just have to sort through the mess and maybe remove some of the complexity.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  4. #4
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I believe I've narrowed the problem down a bit. There is only trouble when I try to create more than one control WHEN I am using a class-encapsulated application. Ken Fitlike, I think this is where I need your help the most.

    I should probably describe the class:

    Code:
    class CppProtoWnd
    {
    private:
    	char chClassName[100];
    	char chCaption[256];
    protected:
    	HWND hParent;
    	HINSTANCE hInst;
    public:
    			     CppProtoWnd();
    	virtual      ~CppProtoWnd();
    	virtual HWND Create(HWND hParent=NULL);
    	char *		 GetCaption();
    	char *		 GetClassName();
    	virtual void SetInstance(HINSTANCE hInstance);
    	virtual void SetCaption(char *chCap);
    	virtual void SetClassName(char *chClass);
    
    	int left,top,width,height;
    	int nStyle,nExStyle;
    	int nID;
    };
    
    class CppAppWnd:public CppProtoWnd
    {
    private:
    	int nClassStyle;
    protected:
    public:
    	CppAppWnd();
    	~CppAppWnd();
    	virtual HWND Create(HWND hParent=NULL);
    	static LRESULT CALLBACK CppAppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
    	HWND hAppWnd;
    };
    All credit for that class goes to Ken Fitlike. I don't want to be accused of plaguarism.

    Code:
    CppProtoWnd::CppProtoWnd()
    {
    hInst=NULL;
    nExStyle=0;    //no extended wnd style
    nID=0;         //no identifier or menu
    }
    
    CppProtoWnd::~CppProtoWnd()
    {
    }
    
    HWND CppProtoWnd::Create(HWND hParent)
    {
    HWND hwnd;    //the wnd handle
    hwnd=CreateWindowEx(nExStyle,                 //more or 'extended' styles
                        chClassName,              //the 'class' of window to create
                        chCaption,                //the window title
                        nStyle,                   //window style: how it looks
                        left,                     //window position: left
                        top,                      //window position: top
                        width,                    //window width
                        height,                   //window height
                        hParent,                  //parent window handle
                        (HMENU)nID,               //handle to wnd menu or cntrl ID
                        hInst,                    //application instance
                        (VOID*)this);             //store pointer to class
    return hwnd;
    }
    
    char *CppProtoWnd::GetCaption()
    {
    	return chCaption;
    }
    
    char *CppProtoWnd::GetClassName()
    {
    	return chClassName;
    }
    
    void CppProtoWnd::SetInstance(HINSTANCE hInstance)
    {
    	hInst=hInstance;
    }
    
    void CppProtoWnd::SetCaption(char *chCap)
    {
    	if ((chCap)&&(lstrlen(chCap)<256))
    		lstrcpy(chCaption,chCap);
    }
    
    void CppProtoWnd::SetClassName(char *chClass)
    {
    	if ((chClass)&&(lstrlen(chClass)<100))
    		lstrcpy(chClassName,chClass);
    }
    
    CppAppWnd::CppAppWnd()
    {
    	SetCaption("Window Caption");
    	SetClassName("myWindowClass");
    	hAppWnd=NULL;
    	left=GetSystemMetrics(SM_CXSCREEN)/4;
    	top=GetSystemMetrics(SM_CYSCREEN)/4;
    	width=GetSystemMetrics(SM_CXSCREEN)/2;
    	height=GetSystemMetrics(SM_CYSCREEN)/2;
    
    	nStyle=WS_OVERLAPPEDWINDOW;
    	nClassStyle=CS_HREDRAW|CS_VREDRAW;
    
    
    }
    
    CppAppWnd::~CppAppWnd()
    {
    }
    
    //************************************************************************************
    //===============================App Window Procedure=================================
    //************************************************************************************
    LRESULT CALLBACK CppAppWnd::CppAppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
    {
    	switch (msg)
    	{
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case 1:
    			return DefWindowProc(hwnd,msg,wParam,lParam);
    
    		default:
    			return DefWindowProc(hwnd,msg,wParam,lParam);
    		}
    	case WM_CLOSE:
    		DestroyWindow(hwnd);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	
    	default:
    		return DefWindowProc(hwnd,msg,wParam,lParam);
    	}
    
    	return DefWindowProc(hwnd,msg,wParam,lParam);
    }
    
    HWND CppAppWnd::Create(HWND hParent)
    {
    	
    	WNDCLASSEX wcx;      //this structure is used for storing information about the wnd 'class'
    	wcx.cbSize           = sizeof(WNDCLASSEX);                //byte size of WNDCLASSEX struct
    	wcx.style            = nClassStyle;                       //ensure wnd is always redrawn
    	wcx.lpfnWndProc      = (WNDPROC)CppAppWndProc;            //pointer to the Window Procedure
    	wcx.cbClsExtra       = 0;                                 //Extra bytes for 'class' wnds
    	wcx.cbWndExtra       = 0;                                 //Extra bytes for this wnd
    	wcx.hInstance        = hInst;                             //Application instance
    	wcx.hIcon            = LoadIcon(hInst,(LPCTSTR)IDI_APP);    //Application icon
    	wcx.hCursor          = LoadCursor(NULL,IDC_ARROW);        //Cursor for wnd
    	wcx.hbrBackground    = (HBRUSH)(COLOR_BTNFACE+1);         //Background wnd colour
    	wcx.lpszMenuName     = NULL;                              //Name of wnd menu
    	wcx.lpszClassName    = GetClassName();                 //Name of this wnd 'class'
    	wcx.hIconSm          = NULL;                              //Icon in top-left corner of wnd
    	
    	if (!RegisterClassEx(&wcx))
    		return NULL;
        
    	HWND hwnd;
    	hwnd=CppProtoWnd::Create();
    	if (!hwnd)
    		return NULL;
    
    	if (!hAppWnd)
    		hAppWnd=hwnd;
    
    	ShowWindow(hwnd,SW_SHOW); 
    	UpdateWindow(hwnd);
    
    	cTree *ct=new cTree(hwnd);
    	ct->width=200;
    	ct->height=300;
    	ct->Create(hwnd,hInst);
    	
                    cTree *ct2=new cTree(hwnd);
    	ct2->left=205;
    	ct2->width=200;
    	ct2->height=300;
    	ct2->Create(hwnd,hInst);
    
    	return hwnd;
    }
    The second cTree attempted bombs out during CreateWindowEx, causing an "Invalid Window Handle" error. I've tried placing other cTrees in different parts of the program, but wherever it is, if it's not the first cTree, it won't work.

    Maybe the problem is something to do with instances of classes, static stuff, etc?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  5. #5
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    >>All credit for that class goes to Ken Fitlike. I don't want to be accused of plaguarism.<<

    ...which reminds me, I really need to dump that stuff and update it...

    >>using a superclass twice in a program, but surely it's possible.<<

    It should be, although there is at least one control (scrollbar) that apparently shouldn't be superclassed because windows uses its class name for default processing.

    I can't see much wrong with what you're doing but it's interesting to note the intermittent nature of the creation problem (new project caused it to 'work' again).

    Things to try, if you haven't already: ensure all variables are intialised prior to use. Msvc sometimes 'works' with a debug build and then dies on a 'release' build because of this. Also ensure that you if you handle the WM_CREATE for your superclassed control that you use CallWindowProc for default system creation of the control.

    If you still have problems then you can either zip up and attach your project here (makes it a lot easier for me since I can 'step' through the code ) or....just subclass the control instead.

    Sorry I can't be of more help at this time.

  6. #6
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Hmmm.... subclass... I've almost forgotten how to do that with all the superclassing happening. In this case, I can't see why I shouldn't subclass. Only, it's nice to sort of put one's own signature on a control, ie, it will appear to be of class "cTree" and not "SysTreeView32". Well, if you could give my project a compile, that'd be awfully kind of you.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  7. #7
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    Sorry...I should have seen the problem from what you had already posted...but it's still easier when you can 'step' through the code.

    The first time you create your superclassed control you retain a copy of the original control wndproc from the WNDCLASSEX retrieved from GetClassInfoEx. The second time you attempt to create a superclassed control it dies because that class object doesn't have a copy of the original wndproc so it calls garbage.

    A modification of your wnd registration is required to eliminate this. I suggest putting the call to GetClassInfoEx in your class constructor and intialising class variables and modifying the WNDCLASSEX struct from there. You can always add functions later to modify any of the WNDCLASSEX struct parameters later if you need them.

    Hope that helps.

  8. #8
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Are you saying that I have to re-register my class every time I want to use it? I thought that wasn't possible.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  9. #9
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I'm currently the happiest man alive. Do you know that I spent 3 days on this problem? I didn't do ANY other programming whatsoever. And it was such a simple problem. The original window procedure was only set for the first instance, because in every subsequent instance, that part of the code was skipped over. All I did then, was move the GetClassInfoEx and wpPrev=wcx.lpfnWndProc lines out of the conditional block, so that they always run. Thanks Ken!
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Drag & Drop using TreeView
    By Devils Child in forum C# Programming
    Replies: 1
    Last Post: 03-24-2009, 07:02 AM
  2. How can I list processes in a treeview?
    By hileci555 in forum Windows Programming
    Replies: 0
    Last Post: 07-21-2008, 04:22 AM
  3. TreeView and Database
    By x64 in forum C# Programming
    Replies: 2
    Last Post: 01-14-2007, 07:42 PM
  4. Treeview Custom Draw
    By mobazr in forum Windows Programming
    Replies: 1
    Last Post: 06-21-2005, 02:51 AM
  5. Problem showing item in TreeView
    By Garfield in forum Windows Programming
    Replies: 4
    Last Post: 03-18-2004, 01:58 AM