I have a dialogue which displays a graph using RMChart. I need to be able to detect LButtonUp and LButtonDown on the chart area.
All help appreciated.
-David
I suspect that you should subclass the RMChart control and respond to the appropriate message in your subclassed message handler. There are lots and lots of examples of subclassing on the PB Forum.
Not sure how to do this. There are no callbacks, the control just paints the graph onto a designated dialogue window to specific coordinates. Any thoughts or examples or work arounds would be appreciated.
I have never used RMChart, but if it only paints the chart onto a dialog then I assume that you can use the built in FireFly mouse message handlers for the Form.
I mean, you know at what coordinates you are drawing the chart on the Form. In the FireFly mouse message handlers you normally get the client xPos and yPos mouse position.... so all you need to do is to determine if the mouse is currently over the chart.
Function FORM1_WM_LBUTTONDOWN ( _
hWndForm As Dword, _ ' handle of Form
MouseFlags As Long, _ ' virtual keys that are pressed
xPos As Long, _ ' x-coordinate of cursor
yPos As Long _ ' y-coordinate of cursor
) As Long
Local rc As Rect
'Example, the rectangle that contains the RMChart
SetRect rc, 5, 5, 50, 100
If PtInRect( rc, xPos, yPos ) Then
MsgBox "Mouse clicked on chart"
End If
End Function
Thanks Paul. Will try in the morning. My only concern is when trying something like this earlier, the lbuttonup and down worked fine on the form similar to what you suggest, however, only outside of the graph area, but not within the graph area. I will try adding the rect code you specified.
-David
I just downloaded RMChart and ran the PowerBASIC demo. The graphs are not painted on to the dialog. They are created as child controls as I had originally thought. The class name is "RMC". You can easily see this for yourself using a program like Spy++.
So, I still say that the best route is to subclass the child control (i.e. the Chart control) and then respond to the mouse messages in your subclass message handler.
Do you need help with the subclassing code?
... a very quick search in POFFS shows a generic skeleton for subclassing posted by Lance Edmonds:
Quote
Subclassing is a method of specifying your own Window or Dialog Procedure (WNDPROC/DLGPROC) that is called instead of the original procedure.
This permits your code to intercept messages and events for particular windows, dialogs and/or controls. Your code then has the choice of
passing each message on, changing the behaviour of the control to certain messages, or simply "discarding" certain messages. Additionally,
your code can process the message both before and after the message is passed on to the original procedure if required.
' Create our global variable
GLOBAL gOrigSubClassProc AS DWORD
...
' Set a new class handler (Subclass procedure. Do this immediately after you create your control).
gOrigSubClassProc = SetWindowLong(hWnd, %GWL_WNDPROC, CODEPTR(NewWndProc))
...
FUNCTION NewWndProc(BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
SELECT CASE wMsg
CASE %WM_some_message_to_intercept
' process the message and exit the function (the original WNDPROC does not get the message)
EXIT FUNCTION
CASE %WM_somethingElse
' Pass the message on to the orignal WNDPROC()
EXIT SELECT
CASE %WM_DESTROY
' Restore the original handler, and then pass the message on for the last time
SetWindowLong hWnd, %GWL_WNDPROC, gOrigSubClassProc
EXIT SELECT
END SELECT
FUNCTION = CallWindowProc(gOrigSubClassProc, hWnd, wMsg, wParam, lParam)
END FUNCTION
I am a little surprised to see that the RMChart control does not post notification messages to the parent dialog (ie. WM_COMMAND or WM_NOTIFY messages) that would indicate when the mouse is over the control or has clicked on the control. Maybe you could suggest this to the control's author.
Okay, I see that you have already asked a similar question in the author's forum.... Looks like Rainer will be adding it in the next release. Cool.
Paul-
thanks for the help. will let you know how I make out.
-David
Paul-
Having a little trouble with the subclassing. Not sure exactly how and where to set subclassing up to trap the RM Chart. Any suggestions or direction appreciated.
-David
Okay, I used the sample PB code that comes with RMChart.
Step 1: Define the global variable to hold the original window procedure of the chart control. I simply put it as the first line in the Form's code editor.
GLOBAL gOrigSubClassProc AS DWORD
Step 2: Subclass the chart after it is created. In my test I created the chart after clicking a Command Button.
'------------------------------------------------------------------------------------------------------------------------
Function FORM1_COMMAND1_BN_CLICKED ( _
ControlIndex As Long, _ ' index in Control Array
hWndForm As Dword, _ ' handle of Form
hWndControl As Dword, _ ' handle of Control
idButtonControl As Long _ ' identifier of button
) As Long
Call Show_BarsImage( hWndForm)
' Set a new class handler (Subclass procedure. Do this immediately after you create your control).
gOrigSubClassProc = SetWindowLong(GetDlgItem(hWndForm, %ID_RMC1), %GWL_WNDPROC, CodePtr(NewWndProc))
End Function
Step 3: Respond to the mouse message.
FUNCTION NewWndProc(BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
SELECT CASE wMsg
Case %WM_LBUTTONDOWN
' process the message and exit the function (the original WNDPROC does not get the message)
MsgBox "We have clicked on the Chart"
Exit Function
'CASE %WM_somethingElse
' ' Pass the message on to the orignal WNDPROC()
' EXIT SELECT
CASE %WM_DESTROY
' Restore the original handler, and then pass the message on for the last time
SetWindowLong hWnd, %GWL_WNDPROC, gOrigSubClassProc
EXIT SELECT
END SELECT
FUNCTION = CallWindowProc(gOrigSubClassProc, hWnd, wMsg, wParam, lParam)
End Function
Paul-
This works great with an unfortunately large exception which is when the form is sized I have it recalling the function to display the graph with the correct new size. When this happens the subclassing ceases to work. I tried calling the subclass routine again also without success and also tried destroying (not sure I did this correctly) the subclass before calling the subclass routine, also without success. I am sure there is a simple solution for this which is eluding me.
-David
hmmm.... so in your WM_SIZE message handler you are destroying the control and re-creating it ???
not destroying the control, trying to destroy the subclass and recreate it. I know the problem is muddled in here somewhere.
1 .Create Form
2. Call Chart Function (with current form size which creates ctrl on form)
3. Create SubClass
works fine
4. When form is sized Call Chart Function (with current form size which creates ctrl on form)??
I have tried before recreating the subclass after #4 as well as killing subclass prior to call chart in #4, calling the chart and then creating the sublcass again.
I can see I tell I am not doing something right, but not sure exactly what.
control kill prior to calling the chart with the new size and the recreating the subclass works. Thanks.
It may work but that certainly doesn't seem like the best solution. I guess that internally RMChart is resetting its window procedure when it redraws the new chart. I'll try some tests later to see what exactly happens. You should be able to resize the control without having to kill it and re-apply the subclass.
Have you seen Rainier's post in his form about the beta that he has ready. Apparantly, it handles the mouse messages for you now.
Scratch that last post... looks like you have already posted in the RMChart forum about the beta. I hope that it will solve the problem more elagantly than having to go through this subclass route.
Agreed about Rainer's updates. However, just making sure I have backup in case he run's into some delays.
David,
I just tried an extremely simple experiment by resizing the chart in the Form's WM_SIZE message handler. The chart resized and it did not affect the subclassing at all. I did not have to destroy or re-create the control at all. Are you doing something strange that I am not aware of? :)
Function FORM1_WM_SIZE ( _
hWndForm As Dword, _ ' handle of Form
fwSizeType As Long, _ ' type of resizing request
nWidth As Long, _ ' new width of client area
nHeight As Long _ ' new height of client area
) As Long
'resize chart to 75% of the Form's width and height
SetWindowPos GetDlgItem(hWndForm, %ID_RMC1), 0, 0, 0, nWidth * .75, nHeight * .75, %SWP_NOZORDER
End Function
Will try your example in a few. As far as strange goes, odds are extremely likely. I tend to prefer to think of myself as unique, which when all is said and done is just a fancy word for strange. Thanks again.
-David
Paul-
The challenge is going to be to be able to pass data to the form. I am using the button up to detect moving a global variable onto the graph which adds or changes price data. This is detected in the subclass perfectly, however, how can I redraw or modify the chart while I am in the subclass?
-David
Off the top of my head:
Create a user defined message (place at the top of the Form).
%USR_RECREATE_CHART = %WM_USER + 1000
In your subclass mouse code, simply post your user message to the Form with the appropriate variable value (or store the value in your global variable like you are doing now). You could pass your value via the wParam or lParam parameter of the PostMessage....
PostMessage HWND_FORM1, %USR_RECREATE_CHART, 0, 0
To get the user defined message, use the CUSTOM message handler for the FORM:
Function FORM1_CUSTOM ( _
hWndForm As Dword, _ ' handle of Form
wMsg As Long, _ ' type of message
wParam As Dword, _ ' first message parameter
lParam As Long _ ' second message parameter
) As Long
If wMsg = %USR_RECREATE_CHART Then
'destroy the existing chart
RMC_DeleteChart %ID_RMC1 'or, maybe simply: DestroyWindow GetDlgItem(hWndForm, %ID_RMC1)
Call Show_BarsImage( HWND_FORM1, aData() )
' Set a new class handler (Subclass procedure. Do this immediately after you create your control).
gOrigSubClassProc = SetWindowLong(GetDlgItem(hWndForm, %ID_RMC1), %GWL_WNDPROC, CodePtr(NewWndProc))
End If
End Function
I haven't tested this, but it should work. :)
Will try this morning. You are the Man!!!!!!!!
Paul-
Works the first time like it should, in appearance. However, does not work after the first time and the app becomes unstable. Closing the chart window closes the entire application.
-David
Okay, I tested it on a live project.
Replace:
RMC_DeleteChart %ID_RMC1
With:
DestroyWindow GetDlgItem( hWndForm, %ID_RMC1)
No GPF's now.
It looks like the RMC_DeleteChart does not actually destroy the control (no WM_DESTROY is sent).