Controls don't change when tab selected (WM_NOTIFY ???)
|
|
Thread rating:  |
Greg Wilson - 30 Apr 2007 03:56 GMT Be advised I'm rather weak in the API programming department. The following code successfully selects the 3rd tab in a tab control on a dialog:-
Dim h1 As Long, h2 As Long, tc As Long h1 = FindWindowX(hWnd, ByVal 0, ByVal "MDIClient", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "DocAppWindow", ByVal vbNullString) h1 = FindWindowX(h2, ByVal 0, ByVal "WndAppWindow", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "#32770", ByVal vbNullString) tc = FindWindowX(h2, ByVal 0, ByVal "sysTabControl32", ByVal vbNullString) SendMessage tc, TMC_SETCURSEL, ByVal 3, ByVal 0&
However, the controls on the original tab don't hide and the ones on the selected tab don't appear. I believe I need to send a WM_NOTIFY message to the tab control's parent (???). To do so, I believe I need the tab control's ID (???). I downloaded Winspector which apparently only gives me only handles, classes, captions etc.
Hoping someone can confirm how to resolve the problem including the syntax. I believe I need to use SendMessage with the ID as the 3rd arg but not sure.
Very appreciative of your help.
Greg
mr_unreliable - 30 Apr 2007 18:09 GMT > To do so, I believe I need the tab control's > ID (???). Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Const GWL_ID = (-12) ID = GetWindowLong(hWnd, GWL_ID))
cheers, jw
Greg Wilson - 01 May 2007 04:45 GMT Hi jw,
Sorry to be a pain. I've tried for hours, googling and trying out just about every permutation of the below: ByVal, no ByVal, setting x to every handle encountered. Still no go.
Below is the actual code. It does a beautiful job of selecting the page (tab) and setting the text "hello" in an edit box. But no matter what, still fails at chaning the visible status of the controls. The variable "x" was equated to every possible parent handle with no luck. Hoping you would have a look:
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Const GWL_ID = (-12)
Sub SetPrecond(hWnd As Long) Dim h1 As Long, h2 As Long Dim tc As Long, ec As Long Dim id As Long, x As Long
h1 = FindWindowX(hWnd, ByVal 0, ByVal "MDIClient", ByVal vbNullString)
h2 = FindWindowX(h1, ByVal 0, ByVal "DocAppWindow", ByVal vbNullString)
h1 = FindWindowX(h2, ByVal 0, ByVal "WndAppWindow", ByVal vbNullString)
h2 = FindWindowX(h1, ByVal 0, ByVal "#32770", ByVal vbNullString)
x = h2
tc = FindWindowX(h2, ByVal 0, ByVal "sysTabControl32", ByVal vbNullString)
MsgBox Hex(GetParent(tc)) 'agrees with WinSpector listing of handle
h1 = FindWindowX(h2, ByVal 0, ByVal "Edit", ByVal vbNullString)
ec = FindWindowX(h2, h1, ByVal "Edit", ByVal vbNullString)
id = GetWindowLong(tc, GWL_ID)
SendMessage tc, TCM_SETCURSEL, ByVal 3, ByVal 0&
SendMessage x, WM_NOTIFY, ByVal id, ByVal 0&
SendMessage ec, WM_SETTEXT, ByVal 0&, ByVal "hello"
End Sub
Greg Wilson - 01 May 2007 05:34 GMT BTW, I have WM_NOTIFY equated to &H4E. Some of the previous post was irrelevant. I included it for completeness. Below is relevant code only.
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Const GWL_ID = (-12)
Sub SetPrecond(hWnd As Long) Dim h1 As Long, h2 As Long Dim tc As Long Dim id As Long, x As Long
h1 = FindWindowX(hWnd, ByVal 0, ByVal "MDIClient", ByVal vbNullString)
h2 = FindWindowX(h1, ByVal 0, ByVal "DocAppWindow", ByVal vbNullString)
h1 = FindWindowX(h2, ByVal 0, ByVal "WndAppWindow", ByVal vbNullString)
h2 = FindWindowX(h1, ByVal 0, ByVal "#32770", ByVal vbNullString)
x = h2
tc = FindWindowX(h2, ByVal 0, ByVal "sysTabControl32", ByVal vbNullString)
id = GetWindowLong(tc, GWL_ID)
SendMessage tc, TCM_SETCURSEL, ByVal 3, ByVal 0&
SendMessage x, WM_NOTIFY, ByVal id, ByVal 0&
End Sub
mr_unreliable - 01 May 2007 21:46 GMT This code, lifted from vbBox (Klaus Probst) cTabStripCtrl.cls code, may be of some help (with notifying the parent:
Private Type NMHDR hWndFrom As Long idFrom As Long code As Long End Type
' ' Call this method when you want to receive a ' TCN_SELCHANGING/TCN_SELCHANGED message to process ' and re-position child controls within the tabstrip ' area. Usually you'd use it within a Form_Resize event ' (or WM_SIZE message) ' Friend Sub SelReset(Optional ByVal hWndOwner As Long = 0, _ Optional ByVal SelIndex As Long = -1)
Dim unmhdr As NMHDR Dim lCurTab As Long
unmhdr.Code = TCN_SELCHANGE unmhdr.hwndFrom = m_hWnd
lCurTab = SelIndex If (lCurTab < 0) Then _ lCurTab = Me.CurSel
If (IsWindow(hWndOwner) = 0) Then _ hWndOwner = GetParent(m_hWnd)
Call SendMessage(hWndOwner, WM_NOTIFY, lCurTab, unmhdr)
End Sub
cheers, jw
Greg Wilson - 02 May 2007 06:54 GMT Below is how I understood it should be set up. Still nothing happens. If there are no errors in my understanding then perhaps I should try another route.
Manually holding down the Ctrl key and clicking the TAB key changes the pages. However, VBA's SendKeys Ctrl + TAB doesn't work. Perhaps the API equivalent will work? I'm thinking of PostMessage with WM_Char message type.
Code follows:
Const TCM_SETCURSEL = &H130C Const TCM_SELCHANGE = &H1300 - 1 Const WM_NOTIFY = &H4E
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long Const GWL_ID = (-12)
Type NMHDR hWndFrom As Long idFrom As Long code As Long End Type
Sub SetPrecond(hWnd As Long) Dim h1 As Long, h2 As Long Dim tc As Long, ec As Long Dim id As Long, x As Long Dim ret As Long Dim unmhdr As NMHDR
h1 = FindWindowX(hWnd, ByVal 0, ByVal "MDIClient", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "DocAppWindow", ByVal vbNullString) h1 = FindWindowX(h2, ByVal 0, ByVal "WndAppWindow", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "#32770", ByVal vbNullString) x = h2 tc = FindWindowX(h2, ByVal 0, ByVal "sysTabControl32", ByVal vbNullString)
id = GetWindowLong(tc, GWL_ID) SendMessage tc, TCM_SETCURSEL, 3, 0&
unmhdr.code = TCM_SELCHANGE unmhdr.hWndFrom = tc unmhdr.idFrom = id
Call SendMessage(x, WM_NOTIFY, 3, unmhdr) End Sub
Much appreciative of your time.
Greg
mr_unreliable - 02 May 2007 18:21 GMT I have run out of ideas for this thread.
There is another possible route. I am assuming that if you mouse-click on the tab of your target app, that the page's controls will be properly displayed.
You can do this via api's by getting the "rectangle" of the tab you want. You could do this by getting the rectangle of the tab window, and doing some arithmetic (i.e., dividing by the number of tabs -- _or_ more directly getting the rectangle of the individual tab you wish to click on.
To get the tab control's rectangle, use the GetWindowRect api.
To get the rectangle of an individual tab, use this (also from Klaus Probst's vbBox code):
' ' Retreives the effective display RECT for the ' specified tab. ' Function GetItemRect(ByVal Index As Long, ByRef urc As RECT) As Long
GetItemRect = SendMessage(m_hWnd, TCM_GETITEMRECT, Index, urc)
End Function
Note that the rect typedef is passed ByRef. The call (if successful) will fill in the rectangle dimensions.
Finally, with a little geometry calculation you can get the screen coordinates of the center of the (tab) rectangle.
Then move the mouse to the (center) of the individual tab using mousemove:
Const WM_MOUSEMOVE = &H200
And finally, (programatically) click the mouse by sending a mouse down/up.
Const WM_LBUTTONDOWN = &H201 Const WM_LBUTTONUP = &H202
cheers, jw
p.s. It would be best to look up some source code on the various vb source code sites, because there is a discrepancy between "the usual" screen coordinates (pixels) and something called "mouse coordinates". You will have to convert (ugh!) between pixels (which the rectangle will give) and mouse coordinates to move the mouse. Ain't this fun?????
Greg Wilson - 03 May 2007 06:41 GMT jw,
Thanks for your time.
I noticed that the Winspector utility doesn't list hidden windows. Entire window branches are not shown until the tab containing it is selected. So the window structure is more complex than immediately apparent.
I think the problem is with identifying the window whose wndproc contains the code that responds to the tab change. Maybe it's not the tab control's parent ??? I tried using the handle to the page window (#32770) without success. I don't know enough about this stuff to make good judgements, enen if my code syntax is correct.
I thnk I should do more experimenting and studying for now. I'll try your suggestion and also the PostMessage option this weekend. If nothing else, the part that uses WM_SetText to send the text directly to the edit windows should speed things up. I can construct the code that identifies all the edit windows and use a loop to populate them instead of tabbing to them using SendKeys.
Thanks again for all your time.
Greg
Greg Wilson - 05 May 2007 22:45 GMT Hi jw,
Problem finally solved. I needed TCM_SETCURFOCUS instead of TCM_SETCURSEL. As you know, achieving this very simple result took days. So for the benefit of others I post the solution. I don't know if it is specific to my situation or will always work for the sysTabControl32 class. Hope this saves others a lot of wasted time.
The code does two things: 1. Enters text in the second text box in a dialog (class "Edit") 2. Selects the 4th tab of a tab control (class "sysTabControl32") 3. The controls "contained in" the tab control pages actually respond - i.e. the ones "belonging" to the selected tab become visible and those "belonging" to all other tabs become invisible. This was not achieved using TCM_SETCURSEL.
I use quotes in point 3 above because these controls are actually child to the dialog the same as the tab control itself - i.e. sibling to the tab control. This is explained with a quote from Mr_Unreliable from http://tinyurl.com/2gjfhn:
'Start quote The notion of "pages" is just the way it appears, not the way it's programmed. When you click on a tab, then the controls associated with that tab are displayed (i.e., made visible) and all the other controls are hidden. While it "looks like" a separate page is being displayed, it is really just individual controls being turned on-and-off in the tab click event handler. 'End quote
Code:- *Note that the dialog must already be open. Opening it programmatically is my next project.
Declare Function FindWindowX Lib "user32" Alias _ "FindWindowExA" (ByVal HWnd1 As Long, _ ByVal HWnd2 As Long, ByVal lpsz1 As String, _ lpsz2 As String) As Long
Declare Function SendMessage Lib "user32" Alias _ "SendMessageA" (ByVal hWnd As Long, _ ByVal wMsg As Long, ByVal wParam As Long, _ lParam As Any) As Long
Const WM_SETTEXT = &HC Const TCM_SETCURFOCUS = &H1330
Sub SetPrecond(hWnd As Long) Dim h1 As Long, h2 As Long Dim tc As Long, ec As Long
'hWnd is application window (ShellWindow) handle passed to this procedure 'Drill down to dialog window containing tab control and text box h1 = FindWindowX(hWnd, ByVal 0, ByVal "MDIClient", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "DocAppWindow", ByVal vbNullString) h1 = FindWindowX(h2, ByVal 0, ByVal "WndAppWindow", ByVal vbNullString) h2 = FindWindowX(h1, ByVal 0, ByVal "#32770", ByVal vbNullString) 'now get tab control handle (child to h2) tc = FindWindowX(h2, ByVal 0, ByVal "sysTabControl32", ByVal vbNullString) 'now get first text box handle h1 = FindWindowX(h2, ByVal 0, ByVal "Edit", ByVal vbNullString) 'now get second text box handle (target text box) ec = FindWindowX(h2, h1, ByVal "Edit", ByVal vbNullString) 'now set desired text in text box SendMessage ec, WM_SETTEXT, ByVal 0&, ByVal "1234" 'now select 4th tab of tab control '(1st tab has index 0 so 4th tab has index 3) SendMessage ByVal tc, ByVal TCM_SETCURFOCUS, ByVal 3&, ByVal 0&
End Sub
Kind regards, Greg
Patel - 28 May 2007 12:43 GMT Hi Greg Wilson,
I got stuck with similar situation. My issue is, code is working excellent with simple tab control but if i changes the 'appearance' property to 'FlatButtons', My code is not working.
To be more specific, i'm working with C# application, Using API functions, i able to select / change the tab, but if i change the appearance property of tabcontrol then the tab selecting code is not working. (I'm working in WinXP servicepack 2, developing application in C#, Testing it against a C# application).
Please help me, if u already got some solution...
Regards, Patel.
On May 6, 2:45 am, Greg Wilson <GregWil...@discussions.microsoft.com> wrote:
> Hi jw, > [quoted text clipped - 69 lines] > Kind regards, > Greg
|
|
|