The following procedure, PointArray.p, is a simpler version of the same application, which handles fewer .NET events, in order to focus on array handling, in this case, using an array of System.Drawing.Point objects used to draw a regular octagon in a graphic region, as shown in the following figure.
Figure 19. Octagon displayed by PointArray.p
From a calculation based on the dimensions of the current client area in a .NET form, the Point objects of the array specify vertices between the sides of the octagon (determined by an AdjustOctagon( ) user-defined function). This procedure then calculates and re-displays the octagon from the point array each time you click the Draw button. For information on locating and running this sample, see Example procedures.
The procedure begins by defining a System.Array object as a point array, a single Point object used to set elements of this array, and the basic user interface elements to display the octagon. In particular, the bold code shows the definition and instantiation of the point array object with the eight points required to draw an octagon. This code instantiates a new Point object and instantiates the array object by calling System.Array:CreateInstance( ) with the element type parameter value obtained using the Point object instance just created. Note that System.Drawing.Point is a value type (structure). So, .NET instantiates the array with default (0-valued) Point objects stored in each element.
USING System.Windows.Forms.* FROM ASSEMBLY.
USING Infragistics.Win.Misc.* FROM ASSEMBLY.
DEFINE VARIABLE rPointArray AS CLASS "System.Drawing.Point[]" NO-UNDO.
DEFINE VARIABLE rArray AS CLASS System.Array NO-UNDO.
DEFINE VARIABLE rPoint AS CLASS System.Drawing.Point NO-UNDO.
DEFINE VARIABLE rDrawBtn AS CLASS UltraButton NO-UNDO.
DEFINE VARIABLE rForm AS CLASS Progress.Windows.Form NO-UNDO.
DEFINE VARIABLE sqrt2 AS DECIMAL NO-UNDO.
DEFINE VARIABLE idx AS INTEGER NO-UNDO.
FUNCTION AdjustOctagon RETURNS "System.Drawing.Point[]" FORWARD.
/* Create .NET Point array and a Point object to set its element values */
ASSIGN
rPoint = NEW System.Drawing.Point(0, 0) rArray = System.Array:CreateInstance(rPoint:GetType( ), 8) rPointArray = CAST(rArray, "System.Drawing.Point[]").
Note: This example casts the System.Array reference to a System.Drawing.Point array type ("System.Drawing.Point[]"), because it later passes the array to a .NET method that requires this particular array type as a parameter. Otherwise, the procedure could continue to use the array directly as a System.Array object.
Note: The Infragistics.Win.Misc.UltraButton class is one of the OpenEdge Ultra Controls for .NET provided with OpenEdge, and Progress.Windows.Form is an OpenEdge extension of System.Windows.Forms.Form designed specifically for running in the ABL environment. For more information on these objects, see Creating and Using Forms and Controls.
Also note that if this procedure did not create an instance of the array element type (System.Drawing.Point) to set its array element values, it could obtain the array element type information needed to instantiate the array object using the static GetType( ) method of the Progress.Util.TypeHelper class. The code to support this method could look like the following code in bold:
...
DEFINE VARIABLE rType AS CLASS System.Type NO-UNDO.
...
PointArray.p continues with the following code to initialize the user interface elements and graphical constants used to build and display octagons. To create a .NET form, the procedure uses the OpenEdge .NET class, Progress.Windows.Form, which, as previously noted, is derived from the System.Windows.Forms.Form class.
sqrt2 = SQRT(2.0). /* "Constant" needed to draw octagons */
rDrawBtn:Click:Subscribe("Draw"). /* Event handler for drawing */
WAIT-FOR rForm:ShowDialog( ).
rForm:Dispose( ).
To actually draw the octagon, the example subscribes an internal procedure, Draw, as a handler for the Click event on the form button. Thus, when you click the button referenced by rDrawBtn, the procedure updates the octagon point coordinates and draws it in the form. For more information on handling .NET events, see Handling .NETevents.
The Draw event handler procedure that follows responds to the button Click event by drawing the octagon from the point array returned by the AdjustOctagon user-defined function. It is the DrawPolygon( ) method that requires that the array object be passed as a "System.Drawing.Point[]" object rather than as the System.Array super class object initially created.
PROCEDURE Draw:
/* Draw octagon from point array */
DEFINE INPUT PARAMETER sender AS CLASS System.Object NO-UNDO.
DEFINE INPUT PARAMETER e AS CLASS System.EventArgs NO-UNDO.
DEFINE VARIABLE rGraphics AS CLASS System.Drawing.Graphics NO-UNDO.
DEFINE VARIABLE rPen AS CLASS System.Drawing.Pen NO-UNDO.
/* Pass adjusted point array to .NET method to draw octagon */
rGraphics:DrawPolygon(rPen, AdjustOctagon( )).
END PROCEDURE.
Note: The PointArray.p procedure also demonstrates the technique for drawing graphics using the graphics object (rGraphics) that is associated with a .NET form. You can only draw form graphics in a form event handler, because the associated graphics object is available for drawing only during the handling of a form event.
As noted previously, the AdjustOctagon user-defined function determines all the points of the array required to draw an octagon within the current client area of the form. This, then, is where ABL uses the single System.Drawing.Point object (rPoint) to set each element of the array using a 0-based index (idx).
FUNCTION AdjustOctagon RETURNS "System.Drawing.Point[]":
/* Adjust and return octagon point array according to form size */
DEFINE VARIABLE iHorizA AS INTEGER NO-UNDO.
DEFINE VARIABLE iHorizSide AS INTEGER NO-UNDO.
DEFINE VARIABLE iVertA AS INTEGER NO-UNDO.
DEFINE VARIABLE iVertSide AS INTEGER NO-UNDO.
DEFINE VARIABLE iHoffset AS INTEGER NO-UNDO.
DEFINE VARIABLE iVoffset AS INTEGER NO-UNDO.
/* Calculate octagon size and position based on form client area */
RUN CalcOctagonSide(INPUT rForm:ClientSize:Width, OUTPUT iHorizA,
OUTPUT iHorizSide, OUTPUT iHoffset).
RUN CalcOctagonSide(INPUT rForm:ClientSize:Height, OUTPUT iVertA,
OUTPUT iVertSide, OUTPUT iVoffset).
/* Generate octagon points */
DO idx = 0 TO 7:
CASE idx:
WHEN 0 THEN ASSIGN
rPoint:X = iHoffset + iHorizA
rPoint:Y = IVoffset.
WHEN 1 THEN ASSIGN
rPoint:X = iHoffset + iHorizA + iHorizSide
rPoint:Y = iVoffset.
WHEN 2 THEN ASSIGN
rPoint:X = iHoffset + ( 2 * iHorizA ) + iHorizSide
rPoint:Y = iVoffset + iVertA.
WHEN 3 THEN ASSIGN
rPoint:X = iHoffset + ( 2 * iHorizA ) + iHorizSide
rPoint:Y = iVoffset + iVertA + iVertSide.
WHEN 4 THEN ASSIGN
rPoint:X = iHoffset + iHorizA + iHorizSide
rPoint:Y = iVoffset + ( 2 * iVertA ) + iVertSide.
WHEN 5 THEN ASSIGN
rPoint:X = iHoffset + iHorizA
rPoint:Y = iVoffset + ( 2 * iVertA ) + iVertSide.
WHEN 6 THEN ASSIGN
rPoint:X = iHoffset
rPoint:Y = iVoffset + iVertA + iVertSide.
WHEN 7 THEN ASSIGN
rPoint:X = iHoffset
rPoint:Y = iVoffset + iVertA.
END CASE.
/* Replace point in point array */
rPointArray:SetValue(rPoint, idx). END.
RETURN rPointArray.
END FUNCTION.
ABL can use this single System.Drawing.Point object to set each array element, because, as a value type, .NET maintains its own copies of all the Point objects stored in the array. ABL can then reset the members of all the array element objects from a single modified object that ABL passes to the SetValue( ) method. After all the Point objects have been set, the function returns the updated point array as a value for use in the Draw event handler (previously described).
The following CalcOctagonSide internal procedure uses a formula based on the square root of 2 to calculate the horizontal or vertical components used to derive coordinates for each point of the octagon.
PROCEDURE CalcOctagonSide:
DEFINE INPUT PARAMETER pDimension AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER pA AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER pSide AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER pOffset AS INTEGER NO-UNDO.
/* Calculate a side of a regular octagon with offsets */
ASSIGN
pA = 1 / ((2 + sqrt2) / (pDimension * 0.8))
pSide = pA * sqrt2
pOffset = pDimension * 0.1.
END PROCEDURE.