Writing the real event handler for CMain
The Fortran event handler we wrote in the last section (CMainTestEventsHandler) doesn’t do much, it just prints a message on the console window of the main program. What we really want is an event handler that computes the logarithms and factorials tables and writes these tables into the MSFlexGrid of the form in the CMain object. Also, we want to be able to create an Excel spreadsheet and plot the results of these calculations. So let’s get to work.
Start by creating a new event handler subroutine in module f90VBGUI (Sample Code 15). This new event handler is a little more complicated than the test subroutine we wrote earlier (Sample Code 13). Notice the definition of several constants. The string constants DescTextLogs and DescTextFact contain the text that will be shown in the input window displayed by class CInput. We need a different description text which will depend on whether the user is entering the upper value for the table of logarithms or the table of factorials. Conversely, constants TopLog and TopFact have the maximum values allowed in the input field of the CInput class.
You will also notice that now events OnLogTableClick and OnFactorialTableClick trigger actions rather than simply printing the name of the event on the console window. In both cases we will first call subroutine GetInputData that as we saw earlier, display the form wrapped by the CInput class and allow the user to enter the upper limit (argument UpperValue) for the table to be computed. If the user did not cancel the input operation, then subroutines ComputeLogarithms or ComputeFactorials are called. Note that both subroutines receive references to the CMain object as arguments. We’ll see the content of these procedures later.
Event OnExcelChart is processed by subroutine CreateExcelChart. If Excel is not installed in your system, this subroutine returns a negative value in argument iRet, in which case an error message is printed using subroutine DisplayError. We saw how this subroutine works at the beginning of this tutorial.
Subroutines ComputeLogarithms and ComputeFactorials are very similar (Sample Code 16). The first thing these subroutines do is to set the headers of the MSFlexGrid exposed by CMain as property fgTable:
!Set the labels in the grid
VarStr = VariantCreate(VT_BSTR,'Value')
call PropertyPut(CMain,'fgTable.TextMatrix',VarStr, &
VariantCreate(VT_I4,0),VariantCreate(VT_I4,0))
call VariantClear(VarStr)
VarStr = VariantCreate(VT_BSTR,'Log(Value)')
call PropertyPut(CMain,'fgTable.TextMatrix',VarStr, &
VariantCreate(VT_I4,0),VariantCreate(VT_I4,1))
call VariantClear(VarStr)
For this, the subroutines use property TextMatrix of the MSFlexGrid control. In addition to the text for the cell, property TextMatrix needs two arguments; the row and the column identifying the cell to which the text will be written.
Next, the subroutines set the number of rows of the control:
!Reset the number of rows to the number of values computed
call PropertyPut(CMain,'fgTable.Rows',VariantCreate(VT_I4,UpperValue+1))
Finally we use a loop to fill the rows of the control with the computed values (logarithms or factorials, depending on the subroutine):
do i=1, UpperValue
x=log(real(i))
!Put the value and the computed logs in the grid
call PropertyPut(CMain,'fgTable.TextMatrix',VariantCreate(VT_I4,i), &
VariantCreate(VT_I4,i),VariantCreate(VT_I4,0))
call PropertyPut(CMain,'fgTable.TextMatrix',VariantCreate(VT_R4,x), &
VariantCreate(VT_I4,i),VariantCreate(VT_I4,1))
enddo
Though you may never use a MSFlexGrid control in one of your projects, what is important here is that the technique we have used to expose the control to the main Fortran application is general enough that it can be used with any ActiveX controls you have. You should note, however, that only ActiveX controls may be exposed in this way. Internal Visual Basic controls are not Automation-enabled and may be manipulated only from inside Visual Basic forms.
Subroutine CreateExcelChart is a little more complicated, because it performs quite a few formatting and setting operations in Excel (Sample Code 17)
The subroutine starts by checking if there is an instance of Excel already running in the system. For this we use f90VB’s GetActiveOleObject:
Excel = GetActiveOleObject('Excel.Application',iRet)
if (iRet.ne.S_OK) then
!no instances of excel are running, create one
Excel = CreateOleObject('Excel.Application', iRet)
if (iRet.ne.S_OK) then
iRet=-2
goto 1000
endif
endif
If there isn’t a running instance of Excel, then we try to create a new one using CreateOleObject. If this also fails, then Excel is not (properly) installed in your system and the subroutine exits with an error code (iRet = -2).
Next, we add a new workbook :
!Add a new workbook by calling Workbooks.Add method
WorkBook=ExecMethod(Excel,'Workbooks.Add')
And set the first two columns of the first row with the appropriate headers:
!Get heading from MSFlexGrid in CMain
XLabel = PropertyGet(CMain,'fgTable.TextMatrix', &
VariantCreate(VT_I4,0),VariantCreate(VT_I4,0))
!Write heading to cell A1 in spreadsheet
call PropertyPut(WorkBook,'ActiveSheet.Cells', XLabel, &
VariantCreate(VT_I4,1),VariantCreate(VT_I4,1))
!Get heading from MSFlexGrid in CMain
YLabel = PropertyGet(CMain,'fgTable.TextMatrix', &
VariantCreate(VT_I4,0),VariantCreate(VT_I4,1))
!Write heading to cell B1 in spreadsheet
call PropertyPut(WorkBook,'ActiveSheet.Cells', YLabel, &
VariantCreate(VT_I4,1),VariantCreate(VT_I4,2))
Note that we get the headers from the fgTable object exposed by CMain. This way, the headers are different for the logarithms and factorials tables. Another important thing to notice is how we can nest properties in calls to PropertyPut, PropertyGet and ExecMethod. For example, the full path to the Cells property is Workbook.ActiveSheet.Cells(row,col). We don’t need to create an instance of the ActiveSheet property to gain access to the Cells property. Instead, we can just pass the path to reach the property starting from the Workbook object we have already instantiated. This method works as long as you don’t need arguments to identify any of the intermediate objects of the path. The last object (Cells in this example) can have any number of arguments, which are passed as parameters to the invoked function.
After setting the headers, subroutine CreateExcelChart uses a loop to copy the values of the computed table into cells of the active Excel spreadsheet:
!Add all the computed values
do i=1, VariantToInteger(PropertyGet(CMain,'fgTable.Rows'))-1
!Get Value from MSFlexGrid
VarTmp = PropertyGet(CMain,'fgTable.TextMatrix', &
VariantCreate(VT_I4,i),VariantCreate(VT_I4,0))
!Convert Value into a real
call VariantChangeType(VarTmp,VarTmp,VT_R8)
call PropertyPut(WorkBook,'ActiveSheet.Cells', VarTmp, &
VariantCreate(VT_I4,i+1),VariantCreate(VT_I4,1))
!Get log(Value_ or Factorial(Value) from MSFlexGrid
VarTmp = PropertyGet(CMain,'fgTable.TextMatrix', &
VariantCreate(VT_I4,i),VariantCreate(VT_I4,1))
!Convert Value into a real
call VariantChangeType(VarTmp,VarTmp,VT_R8)
call PropertyPut(WorkBook,'ActiveSheet.Cells', VarTmp, &
VariantCreate(VT_I4,i+1),VariantCreate(VT_I4,2))
enddo
Because the MSFlexGrid control stores all its values as character strings, we need to convert them into numeric quantities for them to appear in the chart. We do this by calling subroutine VariantChangeType just before the value is put into the corresponding Excel cell.
The rest of the code in CreateExcelChart does some carpentry work to create the chart. We won’t get into the details here, but you can consult Excel’s User Manual for more information about what each invoked property and method does.
Once the chart has been created and formatted, we make sure the Excel application is visible:
!Make sure excel is visible
call PropertyPut(Excel,'Visible',VariantCreate(VT_BOOL,.true.))
Finally, we do some clean-up, releasing references to objects and clearing values stored in variant variables:
!Clean up
call VariantClear(XLabel)
call VariantClear(YLabel)
call Release(Range)
call Release(Chart)
call Release(Workbook)
call Release(Excel)
We can create our Main program to use the new event handler. This program is very similar to the previous TestCMain, with some simple changes; we replaced all references to function CMainTestEventsHandler by references to CMainEventsHandler, as shown in Sample Code 18a (for Absoft Pro Fortran), Sample Code 18b (for Compaq Visual Fortran) or Sample Code 18c (For Lahey Fortran 95). The links below explain how to compile and link program Main with different compilers:
Instructions to compile Main with Absoft Pro Fortran
Instructions to compile Main with Compaq Visual Fortran
Instructions to compile Main with Lahey Fortran 95
Figure 19 and Figure 20 show screen shots of the running application.
Getting rid of that ugly Console Window