Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsVB SyntaxEnterprise DevelopmentDatabase AccessControlsCOMWin APICrystal ReportDeploymentGeneralGeneral 2
Related Topics
VB.NET / ASP.NETMS SQL ServerMS AccessOther Database ProductsMore Topics ...

VB Forum / Win API / May 2007



Tip: Looking for answers? Try searching our database.

Controls don't change when tab selected (WM_NOTIFY ???)

Thread view: 
Enable EMail Alerts  Start New Thread
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
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.