Opinion about ThemedControls bug

Topics: Attention VFPX Admins, Bug Information, General
Developer
Jul 30, 2008 at 6:00 PM
I've made a lot of tests with ThemedControls and found a bug with ThemeNumber 0 (automatic, following Windows XP theme).
In a lot of circunstances, even binded to the Windows Theme Changed event, my associated method isn't fired.
I've tried to change a lot of things and still not found a workaround.
So, here comes my question:
Someone is using the theme number 0 (only works in Windows XP)?
Could I remove this feature?
Developer
Jul 30, 2008 at 7:06 PM
I think I found the problem cause...
Only one hwnd can be binded to the same windows message.
I'm using Ctl32 controls that already is binded to _vfp.hwnd and windows WM_THEMECHANGED message.
Could I bind my method to the Ctl32 method instead of windows message?
Developer
Jul 30, 2008 at 10:55 PM
Emerson, this can be solved by using a proxy property in _Screen or _VFP

Let me prepare something so you can review it, if we decide on a standard way to deal with this, we should be OK.

Carlos
Developer
Jul 31, 2008 at 12:13 PM
Hi Carlos!
Firstly, thank you for the help!
I'll wait your response.
Coordinator
Aug 6, 2008 at 1:45 AM
Emerson and Carlos,

Did you reach a consensus on this?
Developer
Aug 6, 2008 at 3:31 PM
Hi Craig.
Carlos said that he will prepare something to review and I'm waiting another contact.
Developer
Aug 15, 2008 at 3:02 PM
Edited Aug 15, 2008 at 4:54 PM
OK, here I am with this. Good to have you here too Craig. I have been trying to come up with "the perfect" way to handle this, and gave up on that approach, maybe it´s better to just show what I have right now, and people can jump in and improve it, I am shure smarter people than me will be able to do much better.

The problem:

"When binding to Windows message (Win Msg) events, only one hWnd to Windows message pairing can exist. You can pass an hWnd value of 0 if you want to bind all windows to the same Windows message event. A Windows message event binding can be released with the UNBINDEVENTS( ) Function and the CLEAR Commands. Also, if the event handler object specified with the oEventHandler parameter no longer exists, the binding is released when its Windows message occurs."

A solution I implemented in the ctl32 controls:
Since there is no limit to the number of BINDEVENTS you can do to native events or properties, I create a proxy to share window messages bindevents, each object does a bindevent to the window message, and also a bindevent to a proxy property.
The last instantiated object then handles the bindevent to the window message, setting the proxy property, and the previous instantiated objects just chain up to the proxy property.

This is the code:

I give up, there is no sensible way to add code here.
Developer
Aug 15, 2008 at 4:21 PM
Carlos,
You can find the available tags here:
http://www.codeplex.com/Wiki/View.aspx?ProjectName=CodePlex&title=CodePlex%20Wiki%20Markup%20Guide
Developer
Aug 15, 2008 at 4:30 PM
Hi Carlos!
About the problem with Ctl32 and ThemedControls I think we could create a WindowsEventHandler object to bind to the Windows Events and bind our controls to this object.
In the Init of our controls, we could check if this object is created and, if not, create it.
This object could have 2 hook methods to bind and unbind to the Windows Events if the specified event isn't binded.
So instead of directly bind to the Windows Events, we could bind to the methods of this object.
What do you think?
Developer
Aug 26, 2008 at 7:03 AM
Edited Aug 26, 2008 at 7:11 AM
Emerson, how about something like this:

Define Class bindeventproxy As Custom

    _wparam = 0
    _lparam = 0
    _hwnd = 0
    _result = .Null.
    _msg = 0
    _windowproc = 0
    Name = "bindeventproxy"

    Procedure _msghandler
        Lparameters HWnd As Integer, Msg As Integer, wParam As Integer, Lparam As Integer

        Local lnResult

        This._hwnd = m.HWnd
        This._wparam = m.wParam
        This._lparam = m.lParam
        This._msg = m.Msg

        If Isnull(This._result) Then
            m.lnResult = CallWindowProc(This._windowproc, m.HWnd, m.Msg, m.wParam, m.lParam)
        Else
            m.lnResult = This._result
            This._result = .Null.
        Endif

        Return m.lnResult
    Endproc

    Procedure Init
        #Define WM_THEMECHANGED        0x031A
        #Define GWL_WNDPROC            (-4)

        Declare Integer GetWindowLong In win32api ;
            Integer HWnd, ;
            Integer nIndex

        Declare Integer CallWindowProc In win32api ;
            Integer lpPrevWndFunc, ;
            Integer HWnd, ;
            Integer Msg,;
            Integer wParam,;
            Integer Lparam

        This._windowproc = GetWindowLong(_vfp.HWnd, GWL_WNDPROC)

        If Type("This.Parent") # "O" Then
            Bindevent(_vfp.HWnd, WM_THEMECHANGED, This, "_MsgHandler", 4)
        Else
            If Type("This.Parent.HWnd") = "N" Then
                Bindevent(This.Parent.HWnd, WM_THEMECHANGED, This, "_MsgHandler", 4)
            Endif
        Endif
    Endproc

Enddefine

The idea would be to add a property to _VFP, since we cannot add an object to it, and create an instance of this class in that property, something like:

AddProperty(_VFP,"bindeventproxy", createobject("bindeventproxy")

Then in our classes we do a bindevent to _VFP.Msg and if we want to return a value to override the default window procedure, you just store the value to the _Result property.

This is generic, so it can be used for other messages, and could also be used for the _Screen object, or for a form.

What do you think? Any suggestions for changes?

Carlos
Developer
Aug 26, 2008 at 11:45 AM
Hi Carlos!
Super! I think we can try this.
You'll also need to change Ctl32, right?
Is this too hard to change in Ctl32?

We could name this object BindWindowsEventsProxy and place it in a library named VFPX.vcx.
This way, another VFPX projects and controls that needs to bind to Windows event, can use this control and of course, if we need to add more binds to windows events, this can be easily done just changing this class.
Do you agree?

I'll create this object and send a private e-mail to you, ok?

Emerson
Developer
Aug 29, 2008 at 8:58 PM
Edited Aug 29, 2008 at 9:00 PM
Regarding this, we have to consider that we have to come up with a class that can be used not only to "share" the WM_THEMECHANGED message received by _VFP.hwnd, but also any other generic window message, by any other VFP object that has a HWND property, like _Screen and any VFP form.

Since there is no "ADDOBJECT" method for the _VFP object, my proposal is that in that case we can add a property to it and assign a proxy class instance to it. In the case of _SCREEN and any other form, we could just do an ADDOBJECT.

In the case of forms, sometimes we need to do a bindevent not to the form HWND, but to the "inner window" HWND of the form, but that could be easily handled by the form proxy object to I guess.

To have something usable, we have to come up with a naming standard, so anyone can check out if the property/object already exists, and if not, add it, and the actual BINDEVENT can be done outside the class itself, by any interested client.

Another thing is that we have to decide if we make this a visual class or a class contained in a PRG. As to the name or location of the class itself, that does not really matter, the important thing is that the methods and property names are standarized and well known.

It would be great if smarter people could jump in and have a saying in this.

Carlos
Developer
Sep 3, 2008 at 12:25 PM
Carlos,
BindEvent problem solved!
Everything is working like a charm with the changes made in the lastest versions of our controls.
Congratulations for your great job to fix this.
Thanks a lot and kudos for you.

Emerson