dcsimg
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12

Thread: WS_TABSTOP not working with Combobox

  1. #1
    Join Date
    Oct 2011
    Posts
    12

    WS_TABSTOP not working with Combobox

    I am writing an app in c++ using Windows APIs. I created a COMBOBOX edit control and another regular edit control. The COMBOBOX has the WS_TABSTOP style set but when I try to tab from it, I get a beep.
    My COMBOBOX is created as so:
    m_hRecipients = CreateWindow(L"COMBOBOX",NULL,WS_VISIBLE | WS_CHILD | WS_BORDER | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_TABSTOP, rc.right/2 - 120, 37, 400, 20,m_hWnd,(HMENU) IDC_EDIT_EMAIL_RECIPIENTS,NULL,NULL);

    My callback function is looking for the tab but it never gets called:

    switch (uMsg)
    {
    case WM_CHAR:
    // Here I need to check if the user pressed tab and if so, to move the focus to the next tab stop
    if( wParam == VK_TAB )
    {
    // THIS NEVER GETS CALLED

    return true;
    }
    return true;

    If I use the style EM_SETTABSTOPS instead of WS_TABSTOP, the tab works but then I can't edit the Combobox at all. I'm not sure what I'm doing wrong.

    I would apprecaite any help.

    Thank you,
    Rakefet

  2. #2
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    What you're writing is not correct. Of course the WS_TABSTOP window style works with your combo box. If you have another control in your dialog then TAB will move the caret between the controls and it will stop for sure at your combo box (that's what WS_TABSTOP is supposed to do).

    What you're wondering about is, that the WM_CHAR message with the TAB key never reaches your combo box callback function. The reason for that is, that the dialog's message handler handles the TAB key alredy.

    You can use a windows hook for getting the TAB key message, or you can handle the WM_GETDLGCODE message and set the return value to a bit combination including DLGC_WANTTAB.

    With regards
    PA

  3. #3
    Join Date
    Jan 2011
    Posts
    101

    Re: WS_TABSTOP not working with Combobox

    I had same problem. Someone told me to set this up in my main loop:
    Code:
    while(GetMessage(&msg, NULL, 0, 0))
    	{
    		if(IsDialogMessage(main->GetHwnd(), &msg))
    		{
    			continue;
    		}
    
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    and now switching between controls works.

  4. #4
    Join Date
    Oct 2011
    Posts
    12

    Re: WS_TABSTOP not working with Combobox

    Thank you very much both ProgramArtist and Borko1980 for responding. I had to add the code Borko1980 wrote to my main loop for me to start receiving WM_GETDLGCODE messages in my "onReceiveMessage" method. When I try to Tab from my combo box I get the WM_GETDLGCODE message and I return DLGC_WANTTAB, as ProgramArtist suggested, as so:

    switch (uMsg)
    {
    case WM_GETDLGCODE :
    return DLGC_WANTTAB;
    break;

    I am still not able to Tab from the Combo Box to the next control which is an Edit Box. The interesting thing is that I have 2 controls: Combo Box, Edit Box and Button. For both the Edit Box and Button when I enter Tab, I get the WM_GETDLGCODE message in their respective process handlers and I return the DLGC_WANTTAB and the Tab works fine.

    For the Combo Box when I attempt to Tab, the WM_GETDLGCODE goes to the "onReceiveMessage" method and not to the process handler for the Combo Box. I believe this is why the Tab is still not working for the Combo Box.

    Is there anything else you can suggest that I try? I have been working on this for days and days and am really frustrated.

    Thanks very much,
    Rakefet

  5. #5
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    Dear rzdybel,

    What do you want to achieve?

    Do you want the TAB key to be working as normal or do you want to handle it by yourself for some special action?

    If you now get the WM_GETDLGCODE and you return DLGC_WANTTAB then you are telling Windows that it should send the WM_CHAR message to your handler instead of using it to change the caret/focus.

    If your original posted code is still:
    Code:
    switch (uMsg)
    	{
    	case WM_CHAR:
    		// Here I need to check if the user pressed tab and if so, to move the focus to the next tab stop
    		if( wParam == VK_TAB )
    		{
    			// THIS NEVER GETS CALLED
    
    			return true; 
    		}
    		return true;
    then of course TABbing can not work correctly.

  6. #6
    Join Date
    Oct 2011
    Posts
    12

    Re: WS_TABSTOP not working with Combobox

    Dear PA,

    Thank you for responding. I don't care if I have to manage the TAB key manually or let it work as normal. But neither are working. In the onReceive method I get the WM_GETDLGCODE and return the DLGC_WANTTAB but the focus is not set on the next control.

    I have 2 other controls other than the ComboBox. I have a regular EditBox and a Button. I made the ComboBox a regular EditBox and then I did receive the WM_GETDLGCODE in my handler and everything worked correctly. But as soon as I turned it into a ComboBox I no longer received the WM_GETDLGCODE in the ComboBox handler. Instead I received it in the onReceive method. But the EditBox and the Button receive it in their handlers.

    I'm really sorry it's so confusing.

    This is my code to create the 3 controls:

    HRESULT TB03PreviewSend::OnCreate()
    {
    m_hRecipients = CreateWindow(L"COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_TABSTOP,
    rc.right/2 - 120, 7, 400, 20, m_hWnd, (HMENU) IDC_EDIT_EMAIL_RECIPIENTS, NULL, NULL);
    Edit_SetCueBannerTextFocused( m_hRecipients, L"Enter recipient email addresses" , true );
    // set recipients ("To") handler
    DefEditRecipientsProc = (WNDPROC)SetWindowLong(m_hRecipients, GWL_WNDPROC, (long)EditRecipientsProc);

    m_hSubject = CreateWindow(L"EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
    rc.right/2 - 120, 37, 400, 20, m_hWnd, (HMENU)IDC_EDIT_EMAIL_SUBJECT, NULL, NULL);
    Edit_SetCueBannerTextFocused( m_hSubject, L"Enter a subject for your message", true );
    // set Subject handler
    DefEditSubjectProc = (WNDPROC)SetWindowLong(m_hSubject, GWL_WNDPROC, (long)EditSubjectProc);

    m_hBtnSend = CreateWindow(L"BUTTON", L"Send", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY,
    rc.right/2 - 260, 6, 70, 55, m_hWnd, (HMENU)IDC_BUTTON_SEND, NULL, NULL);
    // set Send Button handler
    DefBtnSendProc = (WNDPROC)SetWindowLong(m_hBtnSend, GWL_WNDPROC, (long)BtnSendProc);

    Then in each of their handlers I added the code to get the WM_GETDLGCODE message and return the DLGC_WANTTAB.

    So for the EditBox ("Subject") handler the code looks like:

    // Process Handler for the subject edit box
    LRESULT EditSubjectProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    switch (uMsg)
    {
    case WM_GETDLGCODE :
    return DLGC_WANTTAB;
    break;
    case ...

    and everything works fine. The WM_GETDLGCODE message is received and I am able to tab normally to the next (or previous) control.

    I have the same code in the handler for the ComboBox but it never gets called. Instead the WM_GETDLGCODE is sent to the Windows message handler in the onReceive method as so:

    LRESULT TB03PreviewSend::OnReceiveMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    switch (uMsg)
    {
    case WM_GETDLGCODE :
    return DLGC_WANTTAB;
    break;
    case ....
    }

    But the tab does not work. What happens is the context of the EditBox part of the ComboBox is cleared out and there is no cursor anywhere.

    I must be doing something very wrong but I'm not sure what.

    I would really appreciate any suggestions.

    Thank you,
    Rakefet Zdybel

    p.s. I tried using the <Code></Code> tags around my code but they didn't work. Is there something different I should do?

  7. #7
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    Quote Originally Posted by rzdybel View Post
    p.s. I tried using the <Code></Code> tags around my code but they didn't work. Is there something different I should do?
    Yes, you shoud use the [ ] brackets. It's not html here.

  8. #8
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    Dear rzdybel,

    It is still not clear what the problem is.

    By default the TAB keyboard message (and SHIFT+TAB the same way) is handled by the dialog's window procedure.
    If you want to handle TAB messages by your own (e.g. you have a custom control) you have to respond to the WM_GETDLGCODE message to tell the dialog that you don't want the TAB message(s) to be handled by the dialog's window procedure.

    So now you are handling the WM_GETDLGCODE message by returning DLGC_WANTTAB, which means that the normal dialog's behaviour (TAB switches between control's having the WS_TABSTOP style set) should not work anymore.

    I ask you again: Do you need to handle the TAB key messages of your own? Or do you want the normal behaviour? If the latter is the case: Please do not handle the WM_GETDLGCODE message(s) at all.

    I don't know why your combo box message handler does not receive the WM_GETDLGCODE message. In my test program (a dialog called from a MDI (MFC) app) a combo box behaves as expected. Via spy++ (http://msdn.microsoft.com/en-us/library/aa264396(v=vs.60).aspx) I can clearly see that the combo box and the edit box part of the combo box both receive and handle the WM_GETDLGCODE message(s).

    What is the base class of TB03PreviewSend?
    What kind of project is your app?
    Can you write a much more simple test app that produces the same behaviour you're describing that we can debug it?
    Are you doing any special message handling before the dialog's handler is called (e.g. in your main loop, in a PreTranslateMessage function or by hooking some messages)?

    With regards
    PA

  9. #9
    Join Date
    Oct 2011
    Posts
    12

    Re: WS_TABSTOP not working with Combobox

    Dear PA,
    Thank you again for all your help.

    I'm not sure what kind of project this is. I inherited it. I think it's just a regular Windows app. I'm very new to Windows programming and I hope that's the reason I'm having so much trouble.

    Here is the code for the base class of TB03PreviewSend:
    Code:
    #include "wincontrol.h"
    #include "BaseWindow.h"
    #include "resource.h"
    
    BaseWindow::BaseWindow() : m_hwnd( NULL ), m_hInstance( NULL )
    {
    }
    
    
    /**
     * BaseWindow::Register
     * Registers the window class.
     */
    HRESULT BaseWindow::Register()
    {
    	WNDCLASSEX wcex;
    
    	wcex.cbSize			= sizeof( WNDCLASSEX );
    	wcex.style			= CS_HREDRAW | CS_VREDRAW;
    	wcex.lpfnWndProc	= WindowProc;
    	wcex.cbClsExtra		= 0;
    	wcex.cbWndExtra		= 0;
    	wcex.hInstance		= m_hInstance;
    	wcex.hCursor		= LoadCursor( NULL, IDC_ARROW );
    	wcex.hbrBackground	= (HBRUSH) GetStockObject( BLACK_BRUSH );
    	wcex.lpszMenuName	= MenuName();
    	wcex.lpszClassName	= ClassName();
    	wcex.hIcon			= (HICON) LoadImage( GetInstance(), MAKEINTRESOURCE( IDI_ICON1 ), IMAGE_ICON, 32, 32, LR_DEFAULTSIZE );
    	wcex.hIconSm		= (HICON) LoadImage( GetInstance(), MAKEINTRESOURCE( IDI_ICON1 ), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE );
    
    	ATOM atom = RegisterClassEx( &wcex );
    
    	if( atom == 0 )
    	{
    		return __HRESULT_FROM_WIN32( GetLastError() );
    	}
    	else
    	{
    		return S_OK;
    	}
    }
    
    /**
     * BaseWindow::Create
     * Creates an instance of the window.
     */
    HRESULT BaseWindow::Create( HINSTANCE hInstance )
    {
    	m_hInstance = hInstance;
    
    	HRESULT hr = Register();
    	if( SUCCEEDED(hr) )
    	{
    		HWND hwnd = CreateWindow( ClassName(), WindowName(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, m_hInstance, this );
    
    		if( hwnd == 0 )
    		{
    			hr =  __HRESULT_FROM_WIN32(GetLastError());
    		}
    	}
    
    	return hr;
    }
    
    /**
     * BaseWindow::Show
     * Show or hide the window.
     */
    HRESULT BaseWindow::Show( int nCmdShow )
    {
    	ShowWindow( m_hwnd, nCmdShow );
    	UpdateWindow( m_hwnd );
    	return S_OK;
    }
    
    
    /**
     * BaseWindow::WindowProc
     * Window procedure
     */
    LRESULT CALLBACK BaseWindow::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
    	BaseWindow *pWin = NULL;
    
    	if( uMsg == WM_NCCREATE )
    	{
            // When we create the window, we pass in a pointer to this class 
            // as part of the CREATESTRUCT structure.
    		LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam;
    		pWin = (BaseWindow*)lpcs->lpCreateParams;
    		
            // Set the window handle.
            pWin->m_hwnd = hwnd;
    
            // Set the pointer to the class as user data.
            _SetWindowLongPtr( hwnd, GWLP_USERDATA, pWin );
    	} 
    	else 
    	{
            // Get the pointer to the class.
    		pWin = _GetWindowLongPtr<BaseWindow*>( hwnd, GWLP_USERDATA );
    	}
    
    	if( pWin ) 
    	{
    		return pWin->OnReceiveMessage( uMsg, wParam, lParam );
    	} 
    	else 
    	{
    		return DefWindowProc( hwnd, uMsg, wParam, lParam );
    	}
    }
    
    /**
     * BaseWindow::OnReceiveMessage
     * Handle window messages other than WM_NCCREATE.
     */
    LRESULT BaseWindow::OnReceiveMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
    	switch( uMsg )
    	{
    	case WM_NCDESTROY:
    		SetWindowLongPtr( m_hwnd, GWLP_USERDATA, 0 );
    		return DefWindowProc( m_hwnd, uMsg, wParam, lParam );
    
    	case WM_PAINT:
    		OnPaint();
    		return 0;
    
    	}
    	return DefWindowProc( m_hwnd, uMsg, wParam, lParam );
    }
    Like I said, I have 3 controls: ComboBox; EditBox; Button. When I tab I want to go to the next control. When I shift-tab, I want to go to the previous control. The code is working for the EditBox and Button. Each of them handle the Tab key in their respective handlers. I removed all the WM_GETDLGCODE handling. But no tabbing works unless the controls' handlers handle the tab key themselves. For example for the Subject handler:

    Code:
    // Process Handler for the subject edit box
    LRESULT EditSubjectProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch (uMsg)
    	{
    	case WM_CHAR:
    		// Here I need to check if the user pressed tab and if so, to move the focus to the next tab stop
    		if( wParam == VK_TAB )
    		{
    			int iShiftKeyState;
    
    			iShiftKeyState = GetKeyState(VK_SHIFT);
    			if( iShiftKeyState < 0 )
    			{
    				EnumChildWindows( GetParent( hWnd ), EnumChildProc, IDC_EDIT_EMAIL_RECIPIENTS ); // shift->tab
    			}
    			else
    			{
    				EnumChildWindows( GetParent( hWnd ), EnumChildProc, IDC_BUTTON_SEND ); // tab
    			}
    			return true; // Don't continue to the CallWindowProc or we will get a Beep
    		}
    		return CallWindowProc(DefEditSubjectProc, hWnd, uMsg, wParam, lParam);
    		
    	default:
    		return CallWindowProc(DefEditSubjectProc, hWnd, uMsg, wParam, lParam);
    	}
    
    	return FALSE;
    }
    If I comment that out, the Tab will not work in the Subject EditBox either.

    The same code is in place in the ComboBox handler. The tab works for the EditBox ("Subject") and for the Button ("Send"). For the ComboBox, it doesn't work. The handler never receives the WM_CHAR with wParam == VK_TAB. The EditBox and the button do.

    I will work on making a simplified app as you suggested. Maybe that will be more straightforward to debug.

    Thank you again for all your help. I really appreciate it.

    Regards,
    Rakefet

    p.s. Thanks for the tip on the code brackets and the link to using Spy++.

  10. #10
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    Quote Originally Posted by rzdybel View Post
    I'm not sure what kind of project this is. I inherited it. I think it's just a regular Windows app. I'm very new to Windows programming and I hope that's the reason I'm having so much trouble.
    It is very hard to help if you are not able to answer questions like: What kind of project do you have?

    If you so new to Windows programming: Are you sure that handling WM_TAB in a form or a dialog or a window similar to one of them by your own is the right idea for start learning Windows programming?

    I've been developing Windows software for many years. The first 5 of them (approx.) I never cared of the TAB key in connection with switching from one control to another. That is one of the things Windows should do for me.

    Only in very special cases (in my case it was a custom control written by myself that needed a special handling of the TAB key(s)) I had to deal with these things.

    You call
    Code:
    EnumChildWindows( GetParent( hWnd ), EnumChildProc, IDC_BUTTON_SEND ); // tab
    when you're recognizing a TAB key. What does your EnumChildProc do?

  11. #11
    Join Date
    Oct 2011
    Posts
    12

    Re: WS_TABSTOP not working with Combobox

    Dear PA,

    I have a simple Win32 app. I have a window with 3 controls that I want to be able to tab to with the Tab key. Very simple. I do not want to handle the Tab myself if Windows can do it for me.

    I wrote a small sample app with 2 EditBox controls. I can't get the Tab to work between those 2 controls if I don't do any handling on my own. If I don't capture the tab key, nothing happens. I am looking for some sample code that has this working but I can't find any on line. Do you know where I might find some c++ Win32 sample code?

    The enumChildWindows proc finds the window handle of the desired window to Tab to.
    Code:
    BOOL CALLBACK EnumChildProc( HWND hWnd, LPARAM lParam )
    {
    	if( GetDlgCtrlID( hWnd ) == lParam )
    	{
    		SetFocus( hWnd );
    		return false;
    	}
    	else
    	{
    		return true;
    	}
    
    }
    I am new to Windows programming but have a lot of experience programming in other languages. I need to be able to Tab from control to control in the app I am working on. I don't want to have to do it myself but it doesn't work if I let Windows handle it.

    Thank you again for all your help and patience.
    Rakefet

  12. #12
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: WS_TABSTOP not working with Combobox

    I created a Win32 test application and added 3 controls (using your posted code):

    Code:
    m_hRecipients = CreateWindow(L"COMBOBOX", NULL, WS_VISIBLE | WS_CHILD |  WS_BORDER | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_TABSTOP,
    		rc.right/2 - 120, 7, 400, 20, m_hWnd, (HMENU) IDC_EDIT_EMAIL_RECIPIENTS, NULL, NULL);
    	Edit_SetCueBannerTextFocused( m_hRecipients, L"Enter recipient email addresses" , true );
            // set recipients ("To") handler
    	DefEditRecipientsProc = (WNDPROC)SetWindowLong(m_hRecipients, GWL_WNDPROC, (long)EditRecipientsProc);
    
    	m_hSubject = CreateWindow(L"EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
    		rc.right/2 - 120, 37, 400, 20, m_hWnd, (HMENU)IDC_EDIT_EMAIL_SUBJECT, NULL, NULL);
    	Edit_SetCueBannerTextFocused( m_hSubject, L"Enter a subject for your message", true );
            // set Subject handler
    	DefEditSubjectProc = (WNDPROC)SetWindowLong(m_hSubject, GWL_WNDPROC, (long)EditSubjectProc);
    
    	m_hBtnSend = CreateWindow(L"BUTTON", L"Send", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY,
    		rc.right/2 - 260, 6, 70, 55, m_hWnd, (HMENU)IDC_BUTTON_SEND, NULL, NULL);
            // set Send Button handler
    	DefBtnSendProc = (WNDPROC)SetWindowLong(m_hBtnSend, GWL_WNDPROC, (long)BtnSendProc);
    (I did change some variables since m_hRecipients didn't exist in my test app).

    After adding the suggestion of borko1980 (not calling TranslateMessage/DispatchMessage/TranslateAccelerator if IsDialogMessage returns TRUE) all I had to do was adding the WS_TABSTOP to all the controls:

    Code:
    m_hRecipients = CreateWindow(L"COMBOBOX", NULL, WS_VISIBLE | WS_CHILD |  WS_BORDER | CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_TABSTOP,
     		rc.right/2 - 120, 7, 400, 20, m_hWnd, (HMENU) IDC_EDIT_EMAIL_RECIPIENTS, NULL, NULL);
     	Edit_SetCueBannerTextFocused( m_hRecipients, L"Enter recipient email addresses" , true );
             // set recipients ("To") handler
     	DefEditRecipientsProc = (WNDPROC)SetWindowLong(m_hRecipients, GWL_WNDPROC, (long)EditRecipientsProc);
     
     	m_hSubject = CreateWindow(L"EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP,
     		rc.right/2 - 120, 37, 400, 20, m_hWnd, (HMENU)IDC_EDIT_EMAIL_SUBJECT, NULL, NULL);
     	Edit_SetCueBannerTextFocused( m_hSubject, L"Enter a subject for your message", true );
             // set Subject handler
     	DefEditSubjectProc = (WNDPROC)SetWindowLong(m_hSubject, GWL_WNDPROC, (long)EditSubjectProc);
     
     	m_hBtnSend = CreateWindow(L"BUTTON", L"Send", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP,
     		rc.right/2 - 260, 6, 70, 55, m_hWnd, (HMENU)IDC_BUTTON_SEND, NULL, NULL);
             // set Send Button handler
     	DefBtnSendProc = (WNDPROC)SetWindowLong(m_hBtnSend, GWL_WNDPROC, (long)BtnSendProc);
    There is no need to handle WM_GETDLGCODE. No need to handle WM_CHAR/VK_TAB. No need to change the focus manually.

    And by the way: You're trying to set the focus to a certain control via EnumChildWindow. If you'll encounter a situation where you need to set the focus to the next tabbed item please use GetNextDlgTabItem function.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width




On-Demand Webinars (sponsored)