Try OpenEdge Now
skip to main content
GUI for .NET Programming
Using .NET data types in ABL : Accessing and using .NET arrays : Example: Accessing a .NET array
 

Example: Accessing a .NET array

This manual first introduces the use of .NET array objects in ABL with the EventHandlers.p example described in Accessing and Managing .NET Classes from ABL to demonstrate .NET event handling (see Event handling example).
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.
...

/* Create .NET Point array */
ASSIGN
  rType       = Progress.Util.TypeHelper:GetType("System.Drawing.Point")
  rArray      = System.Array:CreateInstance(INPUT rType, INPUT 8)
  rPointArray = CAST(rArray, "System.Drawing.Point[]").
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.
/* Prepare UI to draw octagons */
ASSIGN
  rDrawBtn              = NEW UltraButton( )
  rDrawBtn:AutoSize     = TRUE
  rDrawBtn:Text         = "Draw"
  rDrawBtn:DialogResult = DialogResult:None
  rDrawBtn:Top          = 4
  rDrawBtn:Left         = 4.

ASSIGN
  rForm        = NEW Progress.Windows.Form( )
  rForm:Height = 300
  rForm:Width  = 300
  rForm:Text   = "Octagons".

rForm:Controls:Add(rDrawBtn).

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 .NET events.
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.

  ASSIGN
    rGraphics  = rForm:CreateGraphics( )
    rPen       = NEW System.Drawing.Pen(System.Drawing.Color:Black)
    rPen:Width = 2.

  /* 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.