|
|||||||||||||||||||
Recent queries on the Works forum led us to realise that there isn’t one single document that describes the various ways (well two) that you can interact with OLE controls within OI10. So we thought that we’d put it all together in one place using as an example a third party control called CSxGraph which you can download here . This is actually an ActiveX control, rather than an OLE control but that’s primarily a Microsoft branding exercise from nearly 30 years ago now, 1996! So, if you see something described as an ActiveX control, just assume it’s an OLE control. Put simply, OLE components can be added to forms and manipulated via the standard Presentation Server functions, GET and SET_PROPERTY, EXEC_METHOD etc, or they can be created programmatically and be manipulated using intrinsic R/Basic routines such as OLEGETPROPERTY. If you want the user to see the control and possibly interact with it, you’ll need to add it to a form. If that isn’t a requirement then you’d instantiate the control programmatically. OLE on FormsTo add an OLE control to a form, simply select the OLE Control menu item, and position the control where you want it on the form. As this is a new OLE control, the system needs to know which OLE control to use, so it presents a dialog from which you can choose the control to insert If you know the CLSID or the ProgId of the control you can enter it here, or you can use the options button to select from a dialog, cleverly only showing the OLE objects that can be added as controls. In this case we know that we’re working with csXGraph to we can select that from the dialog OK this, then OK the initial dialog, the system inserts the OLE control as requested There is an options button next to the CLSID which enumerates all the properties, methods and events that the control exposes, along with additional general information about the control type
So, it’s possible to work with the control without checking the documentation for a lot of things. Whilst this dialog provides a handy summary of what’s available, it’s more like a table of contents, pointing to additional items in the property panel. Further down the property panel we find OLE Properties. Interacting with the UIAll of the OLE Properties that are exposed by the control can be found in the OLE Properties section of the property panel. We can set these properties manually in the property panel at design time or we can alter these properties at run time programmatically. The property list is comprehensive as you can see But, if we know that we want this to be a Pie Chart we could select the graph type and choose that option Similarly, we could set the title to a known value And if we run the form, having set these properties, Nothing happens… This is to be expected, we’ve told the control what title we want and that we want it to draw a pie chart, but we haven’t given it any data to display (properties), and more importantly, we haven’t told it that we actually want it to display anything (methods). So let’s add an edittable to capture our pie slice values and add a button to actually grab that data and display it. To achieve this, we need to firstly get the data which we wish to display, then grab the title and amend it to include the updated date and time, clear the existing data (if any) from the OLE Control and then for each line of data to display, update the data in the pie chart. Finally, we need to tell the control to plot and display the results. In the interests of transparency, the device this is being written on has a High DPI screen and this control is not High DPI aware so some manual playing with properties in the panel was required to make it visually – I won’t say appealing – appropriate. Getting the dataThis is a normal OI GET_PROPERTY call
Changing the titleThis still uses normal OI GET and SET properties, but to ensure there is no clash between a PS property and an OLE property, we explicitly tell the PS that we’re wanting an OLE property by prepending the property name with “OLE.”
Clearing the dataFor this we use EXEC_METHOD function, but as with the GET and SET we qualify our method name Updating the dataAgain we’re using EXEC_METHOD and providing random colours for the slices Plot and displayAnd finally again an EXEC_METHOD Interacting with EventsThat’s the UI element dealt with but of course we might want to respond to an event on the control. For example, clicking the mouse down button on the graph, or maybe doubleclicking on an area of the graph. For this we have to turn to the property panel again, but this time the OLE Integration property
As you will see, by default there are no events qualified, so when the events ARE triggered, they will not raise an OLEEVENT on the control. If we were interested in trapping the mouse down event and the doubleclick event, we would simply launch the QualifiedOLEEvents dialog and check those events. Now when the control is double clicked, or the mouse is clicked down on the control, an OLEEVENT will be raised and the parameters documented will be passed to your handling routine, be it a script or a commuter. So if we put a debug in the OLEEVENT script for a control and mouse down over the control we will see this
With the EventName being onMouseDown, and with the parameters being (according to the control documentation) Param1 Mouse Button Moving on to other elements of the event setup - the three Sync Types are Synchronous, Asynchronous and Callback. By default, the sync type is set to Asynchronous. An asynchronous event is queued and is thus guaranteed to be executed once everything before it in the queue has been dealt with. This can lead to a slight lag if the application is busy. Setting the event to Synchronous will attempt to execute it immediately, but if the engine is tied up doing anything else it will just fail. Setting the event to Callback creates a sort of hybrid situation where the system will attempt to execute the event immediately, but if it fails it will establish if the engine is waiting on a response from the Presentation Server rather than the engine being tied up running a program. If it is waiting, it will branch off and run the event there and then. So synchronous can be used when failure is immaterial, asynchronous can be used when failure is not an option and callback can be used when failure is optional but speed is of the essence. For a discussion of the other check boxes see OLE event [Revelation Wiki], but essentially –
OLE in ProgramsProgrammatic OLE control allows us to automate our applications, driving external programs to produce results that would be hard to achieve in pure OI. Sending Emails, performing mail merges, generating images etc. Using OLE in programs is normally just a case of “instantiating” the OLE control – creating and initialising it – setting relevant properties and telling it to run the required methods. It is less likely that we would need to respond to OLE events in this context. To illustrate programmatic usage, we will again return to our csXGraph control, but this time we will programmatically generate the graph and save the image. To manipulate OLE controls programmatically we make use of the following self-evidently named routines – OLECREATEINSTANCE - to instantiate the OLE control Note that these routines are intrinsic to the compiler and so do not need to be declared. Note further that these routines must be called individually for each property or method. It is not possible to stack the parameters in an array using @Rms and make a single call as is possible with the relevant PS routines. OLESTATUSThis function takes no parameters and returns the status of the last OLE operation. If the value if FALSE$ then the operation succeeded otherwise a negative integer representing the error code is returned. This error code is not particularly helpful, so it helps to use the system function RTI_ErrorText to retrieve an English language description. This function takes three parameters. The kind of error being retrieved, the error code in question, and whether Carriage Return/LineFeed should be swapped out for spaces. Status = oleStatus() It should be used after every OLE operation. OLECREATEINSTANCEThis function takes a single parameter – being the name of the class of the OLE control that we wish to instantiate. So for example oleObj = oleCreateInstance( “csXGraphTrial.Draw” ) If there was no error then we now have an OLE control instantiated in memory to play with. The oleObj variable will contain the handle for the OLE control. OLEPUTPROPERTYThis routine takes a polymorphic set of parameters, depending upon whether the OLE object is indexed. If it isn’t, then it takes the standard three SET_PROPERTY parameters, namely the OLE object handle, the property and the value to set. If the property is indexed (and it’s important to realise that this might not just be X and Y indexes – the object could be n dimensional), then the index values follow the property name, so olePutProperty( oleObj, property[, index1, index2,…], value) OLEGETPROPERTYThis routine takes a polymorphic set of parameters, depending upon whether the OLE object is indexed. If it isn’t, then it takes the standard two GET_PROPERTY parameters, namely the OLE object handle, and the property to get. If the property is indexed (and it’s important to realise that this might not just be X and Y indexes – the object could be n dimensional), then the index values follow the property name, so Return = oleGetProperty( oleObj, property[, index1, index2,…]) OLECALLMETHODThis routine takes two required parameters – the OLE object handle and the name of the method to call. If the method requires parameters then these are passed as optional parameters to the OLECALLMETHOD call. OleCallMethod( oleObj, methodName[, param1, param2…]) Bringing it all togetherFor our example, it wasn’t apparent from the screen shots, but several default properties had been changed to make the graph more attractive. We need to replicate this in our code. The properties that were changed were primarily to do with the size of the image, the title and obviously the graph type. It doesn’t really matter what order we set the properties in as long as all required properties are set before we ask the control to execute any methods. So alphabetical is as good as any. CenterX The “*” is used to draw attention to the fact that although this property is named identically to the form designer property, there will be no confusion as we are setting and getting OLE properties directly without going through the PS. So now we need to modify the program to set the required properties. Note that there’s a debug in the checkOLEStatus. Check the code above to see what line you think it’ll break at when the program is run. Well, the line with the debug in checkOLEStatus obviously, but who was the culprit? So we run the code, there is an error, and obviously it’s -2147352570 error….
Which using the aforementioned RTI_ErrorText tells us is an “Unknown name”… note that the final version of this program uses OLE in place of WIN as the error type – the two are interchangeable but using OLE makes more sense in an OLE context. And sure enough “PiaDia” is an error – it should be PieDia. We can rectify this then start to add the data. To add the data, we first tell the control to use randomised colours – so we can see that the graph is a different screen shot than the Form Designer one. We then use oleCallMEthod to add our four data values. Then we tell the OLE control to draw the graph And finally, we tell it to save the graph to a BMP file
The end result? Hopefully armed with the foregoing you'll now be adequately equipped to tackle your own OLE projects, but if you want to play the code for this article follows. The Demo ProgramSubroutine zz_OLE_Demo( Void ) oleObj = Olecreateinstance( "cSxGraphTrial.Draw") | If ok Then olePutProperty( oleObj, "CenterX", 350 ) | // now add the data If ok Then olePutProperty( oleObj, "UseRNDColor", TRUE$ ) | If ok Then retVal = oleCallMethod( oleObj, 'AddData', "Cherries", 48, 0) | // and tell it to draw the graph If ok Then retVal = oleCallMethod( oleObj, 'DrawGraph' ) | // and save to file If ok Then retVal = oleCallMethod( oleObj, 'SaveToFile', "ZZ_OLE_DEMO.BMP" ) | Return checkOLEStatus: OK = TRUE$
|
|||||||||||||||||||
| |||||||||||||||||||