Where it makes perfect sense to have absolute coordinate positioning of controls on Windows, the Web operates a little differently. Due to the nature of HTML and CSS, x & y coordinate positioning is something to be used sparingly. Instead, DataFlex offers 3 distinct ways of positioning your controls inside a web interface: Panels, Flow Layout and Grid Layout.
Panels are often used as a top-level way of organizing and designing your interface. From there, inside containers (including Panels) you will use either Flow or Grid Layout.
cWebPanel objects can be used to divide the layout of your view or dialog into regions: top, bottom, left, right and center. Set the peRegion property to determine which region a web panel will occupy.
The following image illustrates a web view that is divided into Top, Bottom, Left, Right and center panels. Each panel then contains a single cWebLabel control.

The source code required to create this view is presented below:
Object oPanelView is a cWebView
Set psCaption to "Multi Panel View"
Set piHeight to 300
Set piWidth to 300
Object oTopPanel is a cWebPanel
Set peRegion to prTop
Set psBackgroundColor to "Blue"
Object oTopLabel is a cWebLabel
Set psCaption to "Top"
Set psBackgroundColor to "#AAAAFF"
Set peAlign to alignCenter
End_Object
End_Object
Object oCenterPanel is a cWebPanel
Set psBackgroundColor to "Orange"
Object oCenterLabel is a cWebLabel
Set psCaption to "Center"
Set psBackgroundColor to "#FFDDAA"
Set peAlign to alignCenter
Set pbFillHeight to True
End_Object
End_Object
Object oLeftPanel is a cWebPanel
Set peRegion to prLeft
Set psBackgroundColor to "#4343FB"
Set piWidth to 60
Object oLeftLabel is a cWebLabel
Set psCaption to "Left"
Set psBackgroundColor to "#AAAAFF"
Set peAlign to alignCenter
Set pbFillHeight to True
End_Object
End_Object
Object oRightPanel is a cWebPanel
Set peRegion to prRight
Set psBackgroundColor to "#4343FB"
Set piWidth to 60
Object oRightLabel is a cWebLabel
Set psCaption to "Right"
Set psBackgroundColor to "#AAAAFF"
Set peAlign to alignCenter
Set pbFillHeight to True
End_Object
End_Object
Object oBottomPanel is a cWebPanel
Set peRegion to prBottom
Set psBackgroundColor to "Blue"
Object oBottomLabel is a cWebLabel
Set psCaption to "Bottom"
Set psBackgroundColor to "#AAAAFF"
Set peAlign to alignCenter
End_Object
End_Object
End_Object
Set the piHeight property to set the height (in pixels) of top and bottom panels.
Set the piWidth property to set the width (in pixels) of left and right panels.
Set the pbResizable property to True for left, right, top or bottom panels to enable a “splitter” bar that allows the height of top and bottom panels or the width of left and right panels to be resized at run time.
Panel objects can contain one or more control objects (such as cWebButton or cWebForm). The controls are contained within the panels and each panel organizes its controls into a separate flow layout.
There are some simple rules that must be followed when using panels:
• cWebPanel objects cannot be siblings of web control objects such as cWebButton or cWebForm. Place web controls inside your cWebPanel objects.
• Each sibling cWebPanel must belong to a different region. For example a cWebView cannot have two top panels or two bottom panels as direct child objects.
• Each set of sibling cWebPanel objects must contain one (and only one) center panel (i.e. peRegion = prCenter). This is to ensure that the panels can correctly divide and occupy all of the space provided by their parent container. Top, bottom, left and right panels are optional.
A more complex panel layout can be achieved by dividing panels into sub-panels. This is achieved by declaring a set of cWebPanel objects inside an existing panel.
The following image illustrates the previous web view where the right panel has been subdivided into right-top, right-center, and right-bottom.

The source code required to modify the right panel from the previous example is presented below:
Object oRightPanel is a cWebPanel
Set peRegion to prRight
Set piWidth to 60
Object oRightTopPanel is a cWebPanel
Set peRegion to prTop
Set psBackgroundColor to "Red"
Object oRTLabel is a cWebLabel
Set psBackgroundColor to "#FFAAAA"
Set psCaption to "RT"
Set peAlign to alignCenter
End_Object
End_Object
Object oRightCenterPanel is a cWebPanel
Set psBackgroundColor to "Red"
Object oRCLabel is a cWebLabel
Set psBackgroundColor to "#FFAAAA"
Set psCaption to "RC"
Set peAlign to alignCenter
Set pbFillHeight to True
End_Object
End_Object
Object oRightBottomPanel is a cWebPanel
Set peRegion to prBottom
Set psBackgroundColor to "Red"
Object oRBLabel is a cWebLabel
Set psBackgroundColor to "#FFAAAA"
Set psCaption to "RB"
Set peAlign to alignCenter
End_Object
End_Object
End_Object
The rules which apply when dividing a view into panels also apply when dividing a panel into sub-panels.
There is no technical limit to how many layers of sub-panel you can create, however more than two levels of sub-panel division is probably of little practical use.
Within Flow Layout, controls flow naturally from the top left to the bottom right of a container. The order of your controls within a container are as they appear in your code.
To regulate the size and flow-positioning of controls, a container can be divided into any number of equal-width columns. Each child control will be positioned in its designated column and the control’s width will occupy one or more column widths.
Columns provide a fast and simple way to vertically align controls and to ensure that control widths line up evenly to fixed positions.
The following image illustrates a view where the horizontal position and widths of cWebForm controls fit into a view’s column layout.

Set the piColumnCount property of the cWebView, cWebPanel or any other container object to determine how many columns it is divided into.
The fewer columns you have, the easier and faster it is to reorganize the layout of controls. The more columns you have, the more choice you will have over position and size of each control. According to best practice guidelines, a column count of 12 results in the best balance of configurability and simplicity.
Set piColumnIndex to determine which column the control occupies. Column numbering starts at 0. By default each controls' piColumnIndex is set to zero which means it will occupy the left-most layout column of its parent View (or panel).
Set piColumnSpan to determine the width of a control (and its label) as expressed by the number of layout columns that it spans. By default, most controls' piColumnSpan is set to zero which means that it spans all available columns.
The source code required to build the column layout shown in the previous image appears below:
Object oColumnLayout is a cWebView
Set piWidth to 600
Set psCaption to "Column Layout View"
Set piColumnCount to 3
Object oWebForm1 is a cWebForm
Set piColumnSpan to 1
Set psLabel to "Web Form 1:"
Set piLabelOffset to 90
End_Object
Object oWebForm2 is a cWebForm
Set piColumnSpan to 1
Set piColumnIndex to 1
Set psLabel to "Web Form 2:"
Set piLabelOffset to 90
End_Object
Object oWebForm3 is a cWebForm
Set piColumnSpan to 1
Set piColumnIndex to 2
Set psLabel to "Web Form 3:"
Set piLabelOffset to 90
End_Object
Object oWebForm4 is a cWebForm
Set piColumnSpan to 1
Set psLabel to "Web Form 4:"
Set piLabelOffset to 90
End_Object
Object oWebForm5 is a cWebForm
Set piColumnSpan to 2
Set piColumnIndex to 1
Set psLabel to "Web Form 5:"
Set piLabelOffset to 90
End_Object
Object oWebForm6 is a cWebForm
Set piColumnSpan to 3
Set psLabel to "Web Form 6:"
Set piLabelOffset to 90
End_Object
End_Object
In the above example the controls are arranged into three vertical columns. The cWebView object sets a piColumnCount of 3. The controls are either piColumnIndex of 0, 1 or 2 and piColumnSpan is set to 1, 2 or 3.
An alternative could be to set the piColumnCount to 9 instead of 3, then multiply each piColumnCount and piColumnIndex value in the above code by 3. This would produce an identical result but with the added flexibility of allowing a finer adjustment if you wanted to modify the layout later.
Realize that for controls which include a label (e.g cWebForm) the width of the control includes the width occupied by its label. The piLabelOffset property allows you to adjust the amount of space that is occupied by a control’s label (in pixels). Set piLabelOffset < 120 to reduce the label width, or > 120 to increase the label width.
The Studio’s Web View wizard provides a column layout tool that will help you to set the initial layout of your selected objects. The preview button allows you to review the layout visually. This is a good tool to learn the relationship between the piColumnCount, piColumnIndex, piColumnSpan and piLabelOffset properties.
There are some simple rules that govern how piColumnIndex and piColumnSpan are interpreted in a set of multiple controls…
• The piColumnIndex of a control cannot exceed the number of columns in the parent view or panel. Remember that piColumnIndex is 0-based so if the view’s piColumnCount = 9 then the control’s piColumnIndex must be between 0 and 8.
• If you declare two consecutive control objects (e.g. two cWebForm objects) both with the same piColumnIndex value, then they will both occupy the same column. Flow-layout rules then determine that one control will be positioned above the other. This is determined by the order the objects are declared in your code.
• If you want two consecutive control objects to occupy the same row in the layout then set the piColumnIndex of the second object to a value >= the first object’s piColumnIndex + piColumnSpan. For example review oWebForm1, oWebForm2 and oWebForm3 in the above example.
When you display a web view (or dialog) in the Studio’s WebApp Designer and select one of the web view’s web object in the Code Explorer, you can see the column index and column span occupied by that object.
The following image illustrates part of the Order Entry view in the previewer where the “Zip” cWebForm is selected and highlights its column index and column span.

Introduced in DataFlex 2023, Grid Layout allows you to leverage the power of CSS Grid to gain more control over the vertical positioning of your controls. As the name suggests, Controls position themselves on a Grid that you configure at container level. Controls can be assigned specific rows and columns to occupy, granting a very fine level of control over a control’s exact position.
To get started with Grid Layout, the first step is to set up your Grid that your controls will place themselves on later. When switching to Grid Layout (by setting peLayoutType to ltGrid on the container), you get access to several additional properties besides those that govern the columns. Using these properties allows you to set up your grid by configuring your columns, but also your rows.
For the rows:
piRowCount
Sets the minimal amount of rows to display in this grid’s container. If controls span more than the set amount of rows, additional rows will automatically be generated.
psRowHeights
format: “ROWINDEX/VALUE ROWINDEX/VALUE ROWINDEX/VALUE …”
example: “1/75 3/1fr 4/20%”
Allows custom row heights to be specified, overriding the default
row height.
Format listed above. Several values can be used:
Exact pixel values, simply enter the desired number
Percentages (e.g.: “1/20%”). Makes the row a percentage of the total available height.
Fractions (e.g: “1/1fr”, “4/3fr”). Makes the row a fraction of the total available remaining height. For example: In a grid with exactly 4 rows that are each 1 fraction (1fr), each row will claim 1/4th of the total available height. If a 5th row is added that is exactly 20 pixels high, then the other 4 rows will claim 1/4th of the total height that remains after subtracting the 20 pixels
Advanced clauses like minmax, max-content, etc. refer to the CSS Grid documentation for more information (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout). Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
piDefaultRowHeight
default: 20 (pixels)
Sets the default minimum row height for each row that is not
assigned via psRowHeights. Default height rows will always claim this
minimal pixel row height, and grow (if possible) according to their
content. For example: With piDefaultRowHeight set to 50, an empty
row will be 50 pixels high, putting a 150px high WebList in a default
height row will cause the row to stretch to match this 150 pixels.
Putting a 40px high WebButton in the row will cause there to be 10
pixels of unused space, which will show as whitespace.
Several values can be used:
Exact pixel values, simply enter the desired number
Percentages (e.g.: “1/20%”). Makes the row a percentage of the total available width.
Fractions (e.g: “1/1fr”, “4/3fr”). Makes the row a fraction of the total available remaining height. For example: In a grid with exactly 4 row that are each 1 fraction (1fr), each row will claim 1/4th of the total available height. If a 5th row is added that is exactly 20 pixels tall, then the other 4 rows will claim 1/4th of the total width that remains after subtracting the 20 pixels.
Advanced clauses like minmax, max-content, etc. refer to the CSS Grid documentation for more information (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout). Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
piColumnCount
Works exactly like it does in Flow Layout.
psColumnWidths
format: “COLUMNINDEX/VALUE COLUMNINDEX /VALUE COLUMNINDEX /VALUE …”
example: “1/75 3/1fr 4/20%”
Allows custom column width to be specified, overriding the default
column width. Format listed above.
Several values can be used
Exact pixel values, simply enter the desired number
Percentages (e.g.: “1/20%”). Makes the column a percentage of the total available width.
Fractions (e.g: “1/1fr”, “4/3fr”). Makes the column a fraction of the total available remaining width. For example: In a grid with exactly 4 columns that are each 1 fraction (1fr), each column will claim 1/4th of the total available width. If a 5th column is added that is exactly 20 pixels wide, then the other 4 columns will claim 1/4th of the total width that remains after subtracting the 20 pixels
Advanced clauses like minmax, max-content, etc. refer to the CSS Grid documentation for more information (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout). Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
psDefaultColumnWidth
default: 1fr
Sets the default width value for each column that is not assigned
via psColumnWidths. The default is “1fr”, which mimics the behavior
of the Flow Layout; causing each column to be the same width.
Several values can be used:
Exact pixel values (e.g. “30px”)
Percentages (e.g.: “1/20%”). Makes the column a percentage of the total available width.
Fractions (e.g: “1/1fr”, “4/3fr”). Makes the column a fraction of the total available remaining width. For example: In a grid with exactly 4 columns that are each 1 fraction (1fr), each column will claim 1/4th of the total available width. If a 5th column is added that is exactly 20 pixels wide, then the other 4 columns will claim 1/4th of the total width that remains after subtracting the 20 pixels
Advanced clauses like minmax, max-content, etc. refer to the CSS Grid documentation for more information (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout). Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
After setting up your grid, it is time to position your controls. This is done by assigning controls to populate specific cells. This assignment is determined by the value of several properties.
piRowIndex
default: -1
Positions the control at the set row index on the grid. If not specifically set, CSS Grid will attempt to fill any “gaps” in your grid. How this is done exactly also depends on whether piColumnIndex is explicitly set or not. Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
piRowSpan
Causes a control to span 1 or more rows. Note that this effectively claims the rows to use as space. Whether the control actually visibly fills the space depends on its height or whether pbFillHeight is set to True.
piColumnIndex
default: -1
Positions the control at the set column index on the grid. If not specifically set, CSS grid will attempt to fill any “gaps” in your grid. How this is done exactly also depends on whether piRowIndex is explicitly set or not. Note that CSS Grid is 1-based, but the DataFlex Grid Layout implementation is 0-based.
piColumnSpan
Just like in Flow Layout, this causes a control to span 1 or more columns. By default, controls will span the full width of their assigned columns. Note that while setting this to 0 in Flow Layout would cause it to span the remaining space of the row, in Grid this causes it to simply occupy 1 column.
pbFillHeight
In Grid Layout, if set to True the control will attempt to size its height to its allocated rows, effectively causing it to fill any rows it is assigned to. Combine pbFillHeight with rows set to 1 or more fractions (“1fr”) to get a similar effect to that of pbFillHeight in Flow Layout.
If set to False, the control will attempt to retain its “natural height”, usually determined by CSS, piHeight, piMinHeight or a combination of these.
Grid and Flow both have their place in Web UIs, and one is not specifically better than the other. Depending on the use case, you should use either Grid or Flow.
Use Flow when you…:
Use Grid when you…:
A cool thing is that you can mix-and-match Flow and Grid. You could for example have a View that is set to use Flow Layout, then inside make several groups that each use Grid Layout. The possibilities are endless!
For both the Flow and the Grid Layout as well as for panels, a visual designer is available to help you with building your interfaces. The WebApp Designer can be shown or hidden via the Studio's View menu or by pressing F7.
Inside the designer, controls can be selected by clicking on them. A blue outline will highlight the currently selected control. From there, the functionality of the designer depends on whether the container is set to use Flow or Grid Layout.
The following image should give a good understanding of what certain buttons inside the designer do when set to Flow Layout
When moving controls and hovering over the designer, a black marker will indicate where the control will be positioned. Controls can be moved inside the designer, but also dragged from the class palette onto the designer itself.
For Grid Layout, the designer functions a little differently. First off, when selecting a container object with peLayoutType set to ltGrid, an icon will appear in the top right corner of the selection box. This toggles the Grid Overlay, where you can set up and configure the container’s grid.
The following image should give a good understanding of what is possible inside the designer when the grid overlay is active.
When moving a control and hovering it over a Grid Layout container, the overlay will automatically show and the cells the control will claim will be highlighted.
You may also notice that controls inside a Grid Layout container have a slightly different selection box when highlighted. Moving a control is simply done via the drag handle and when dropping on the highlighted cells the piColumnIndex and piRowIndex will be updated accordingly. Resizing is done via the icon on the bottom right of the selection box by dragging it, where again the Grid Overlay will show and highlight the cells the control will occupy after resizing at which point the piColumnSpan and piRowSpan will be updated to match the new width and/or height.
Previous Topic: Web Application Flow
Next Topic: Managing Web Control Visibility