Posted: 2015-11-07 in Grid

graphical checkbox in a grid column
Currently not supported. You can however put combo boxes in grids using Static or dynamic choicelists.

Grid Column Attributes are accessed by double-clicking on the Grid Column Header. These Attributes then apply to the Column in the ‘Grid View’ and also in the ‘Form View’ when you are using a Split Grid.

In the Lianja App Builder, you are probably used to the idea of double-clicking on a control or container’s header or caption to access its Attributes and the same is true for a Grid Column:
You might not know that you can also right-click on a Grid Column Header to display an additional menu of options:
This gives you an alternative access to the Column’s Attributes, the options to add a blank Column before or after the current Column and allows you to remove the current Column from the Grid.
Remember you can add the Column back in if you change your mind, by dragging the field from the ‘Column Names’ in the Sidebar into the Grid and you can rearrange the Column order in the Grid by dragging a column by its header.

Web Client Grid sections have a standard Save/Cancel/Add/Edit/Delete actionbar in the header unless the Grid itself is Readonly.

You can call the grid.sort() method

oGrid = Lianja.get("page1.section1").grid
// sort 3rd column ascending
// columns start from 0
// direction: 1=ascending, -1 = descending

The Sort delegate is not currently called when clicking on the column headers.
I’ll check on support in JavaScript and the Web Client.

Split Grids are normal Grid Sections with the ability to display a single record Form View within the Section. In the lianjademo App, the Orders and Order Details grids in the ‘Customers Orders’ dashboard are both Split Grids.
You set Split Grid behaviour in the Grid Section Attributes, checking ‘Double click to edit’ and/or ‘Split grid’.
With ‘Double click to edit’ set, double-clicking on a Grid row displays a single record Form in the grid. If you have memo or object (e.g. image) fields, check the ‘Show memo/object panel’ attribute.


Tip: hover your mouse over the join between the grid and the Form View to display a handle allowing your to resize the width of the Form View or hide it completely. There’s also a resize handle for the height of the memo panel, just hover your mouse at the top of the panel header to reveal it.
Checking ‘Split grid’ gives you three possible ‘Views’ for the grid and displays buttons in the grid header allowing you to switch between them.


Split View:


Form View:


Grid View:


ip: the displayed columns/fields are the same in the Form View as in the Grid View, i.e. a ‘removed’ column will not display in the Form View. They are also displayed in the same order – remember you can reorder the columns in a grid by dragging a column header over another.

For a standard Grid Section, this appears to working fine: 


The Sort() method in the Grid takes 2 parameters nColumn, nDirection but where do they come from & how does it work? 
the number refers to the order of columns in a table, as displayed in the data manager. I haven’t tried (yet) to use macros there, with a routine that would use afields() to get the order from the name. Give it a try!
{fnum4fld(“firstname”},{fnum4fld(“lastname”}, etc.
Note that the sort() nColumn argument (Column number) starts from 0 for the first column. There is also some information about the sort() indexing here. 
Also remember when you are manipulating a Grid Section with Lianja.getElementById() and synonyms, you need to work on the Section’s embedded Grid object, e.g.

oGrid = Lianja.get("page1.section1").grid
oGrid.sort(0,1)  // sort ascending on the first column


proc Gamelist_grdGameList_dblclick()


proc Gamelist_grdGameList_dblclick()
       oGrid = Lianja.Get("Gamelist.grdGameList").grid
       nRow = oGrid.activerow

Is it possible to access a grid column heading through code?
For example, how do I determine the column heading for column 3?
The Column object (returned from grid.columns(n) has a property called Header1 that returns a reference to the Header object for that column. e.g.

oCol1 = grid1.columns(1)
oHeader = oCol1.header1
oHeader.caption = "This is Column 1"

When you create a new grid object, make sure it is a ‘mygrid’ not a standard ‘grid’ or it will not have the colattribs() method defined.

ui_goalhomecont.addobject("ui_goalhomegrid", "grid")

needs to be

ui_goalhomecont.addobject("ui_goalhomegrid", "mygrid")

How do I properly set this column to be read-only and have the grid functional?

oCol1 = ui_homegrid.columns(1)
oCol1.readonly = .T.

event that fires for a child grid as he moves through the parents. Parentdatachanged() fires before the new child is selected, so can’t be used to pick up the child value (first row in child grid).
The click() firing has been confirmed as expected behaviour – it shouldn’t fire if you are clicking within the same row.



Reference the grid contained by the Grid Section:

oGrid = Lianja.Get("test.section2").grid
? oGrid.rowcount
? oGrid.item(1,2)

To get information from the Grid, or select a cell:

oGrid = Lianja.get("mypage.mysection").grid
// what is the value at row 3, column 4
? oGrid.item(3,4)
// what is the current row?
? oGrid.activerow
// what is the current column?
? oGrid.activecolumn
// select row 2, column 5

Have a section “section1”, as grid, there’s a test table in it.
Another section “section2” as a canvas with a button on it.
Code in button: lianja.get(“section1”).grid.add 
The grid is bound to the test table so all you need to do is append/insert a record in the table issue a refresh on the grid then position onto the new row using …grid.goto( recno() ).

May I know if I can add a command button in a column of a grid so that user can click on it to popup a form for data entry
The grid section is comprised of columns.
Double click on the column header to slide in the column attributes.
A column has a wide range of attributes including the ability to display as a “Button”, a “Hyperlink” or a “Custom Control”.

Screen Shot 2015-01-25 at 10.10.21 AM

Then in the Grid section attributes add your delegate for “Link Clicked”.

Screen Shot 2015-01-25 at 10.11.20 AM

any grid section filters that have {…} macros in them, these are expanded at the time of refreshing the grid. This basically means that the filter itself is dynamically evaluated at the time the grid is refreshed. This works in all views; desktop, web and mobile.

A grid is bound to data so IOW there is a corresponding cursor for each grid.
As you navigate records in the grid the active record in the underlying cursor is changed.
You can update the active record and then refresh that specific record in the grid using oGrid.refreshRecord( recno() )
If the underlying cursor has been dynamically created from a virtual table query you need to specify a primary key and issue the replace statement which will generate the SQL dynamically and execute it.

If you make the grid “readonly” it will be highlighted.

Lianja.get("page1.section2").grid.activeRow = 0

The grid is bound to your data which I understand to be a SQL query so you can update the data and refresh the grid (saving the activerow and positioning back on it).
The ROWID for the base table is a hidden column that you can use to perform the update.
Look in the console and LIST STATUS to see what cursor is bound to your grid them SELECT cursorname and issue a ? rowid. That is the record in the base table that needs updated.

You need to use the doScroll() method like this if you want to move down the rows of the grid.

Lianja.get("clock_records.list").grid.doScroll( arg )

Note that Lianja.get() does not require “page:”, you are looking up the object.

I tried the SQL with filter as follows but the grid always show all records

select * from ventlrec

Filter :

where clernoee=”{peoplekiosk.emp_no}”

The filter is a condition. Remove the where. 
A better way to achieve this would be to apply the whole sql select … Where …
What data type is clernoee? If it’s not a char column remove the quotes.

I have a desktop app with a page with one grid section
I have set the “Filter” attribute to as follows for top grid section : 


What is “peoplekiosk”? If it is a namespace perhaps it is not declared until later.
Setting a filter on a large number of records is also not a good idea.
Better to have an index key on it and restrict by the index key.
You do not say if this is a table, SQL statement or Virtual Table.
Setting filters to variables requires the grid to be manually refreshed programmatically when you change the variable.

If the grid is not editable use SQL statement.
If it is editable use a Virtual Table.
Look at the Virtual Tables example and you will see how {…} macros are used in the “where” condition of the SQL statement.

Added the ability for a grid section to have “Multi Select Rows”. This displays a checkbox column for each row. As the user clicks a checkbox, the “selectionChanged” delegate is called with a comma separated list of values. These values are determined by the “Multi Select Row Expression” e.g. You can multi select rows in a grid and then call your own business procedure to handle app specific business logic

Screen Shot 2014-12-11 at 3.03.53 PM

And these are the attributes you need to set for the grid section to enable this functionality.

Screen Shot 2014-12-11 at 3.04.51 PM

The “selectionChanged” delegate itself looks like this.

// Event delegate for 'selectionchanged' event
proc page1_section2_selectionchanged(arg)
    // 'arg' is a comma separated list of items selected
    // It can be used directly to load the selected items into a ListBox
    Lianja.writeOutput("selectionChanged() arg="+arg)

Is there an attribute I can set to create a column calculation such as sum or average, etc?
Grid column subtotals were added in Lianja v1.1.3. You can perform both horizontal and vertical totaling using NoCode.

Screen Shot 2014-12-11 at 2.58.25 PM

Screen Shot 2014-12-11 at 2.59.19 PM

You can hide and show a grid section dynamically using Lianja.getElementByID(“page1.section1”).hide() or show().

In the v1.3 release I have implemented “MultiSelect rows” for grids.
There are two new attributes for grid and attachment sections.
“MultiSelect rows”
“MultiSelect row expression”
Together with a new delegate called “SelectionChanged”.
If you check “MultiSelect rows” in the grid section attributes then the first column of the grid contains a checkbox for each row. Clicking the checkbox calls the “SelectionChanged” delegate passing it a comma separated list of items that are evaluated for each row selected based on the “MultiSelect row expression”.
This provides the ability to select multiple rows in a grid then perform an operation on them (section menu of other action) such as send an email or delete the records, then refresh the grid.

In v1.2.4 I have added the ability to dynamically sort the data displayed in a grid by clicking on the grid column headers. This is implemented in both desktop and web clients. There is a new grid section attribute “Sortable” which you can check to enable this on a grid.
Here is an example in the Web Client. Just click the column header once to sort ascending and again to sort descending. This is all handled automatically for you and the grid is refreshed while still maintaining the parent->child relationship, filters and search condition if active.

Screen Shot 2014-10-07 at 1.50.08 PM

To make a grid “Sortable” just check the “Sortable” attribute in the section attributes,

Screen Shot 2014-10-07 at 1.54.07 PM

I am struggling to find info on this, my grid has Price and Quantity columns and I want to add a Total column:
1. Must I have a db column that is marked as calculated? if so, does this column need to be created in code because I don’t see anything in the ide column editor to indicate that the column is calculated.
2. If I don’t have a calculated db column, can I just have a column grid column? if so, where do I enter the formula?
I guess I may need something like PRODUCT.PRICE*PRODUCT.QUANTITY in ‘Get Data Mapping’ but that has no effect.
Just add a column and put the expression as the data source.
Set other columns that affect this new “calculated” column to “Recalculate” and its all done for you.
The “Grid formatting” example App demonstrates this.

We can change the RecordSource of a grid no problem but removing the columns & starting again seems more difficult.
we can use ogrid.removecolumn(ColumnNo) in a loop to remove the columns but when the RecordSource is changed to a new value, the original columns headers & number of columns return.
How can we get the grid to re-read the column headers in the way that happens when the grid is first created?
Then try clear() on the grid.

In our app (at the customers request), we have 14 grid gadgets in 7 form sections (2 per section), populated by 14 READWRITE SQL cursorsA
The prg does the SQL & all works fine. The cursors are populated. Logic for the grids, called at each pass through the logic, is :

oGrid = Lianja.Get("Page.section").GadgetGrid
oGrid.RecordSourceType = 4
oGrid.RecordSource = "zzz"

The SQL always produces data for each cursor.
All of the above is so simple and works beautifully SOMETIMES. Sometimes the grids work great, sometime some of them are blank, sometimes they are not. Sometimes they the blank ones are populated & sometimes the populated ones go blank. This is ridiculous. 
These grid gadgets really only want tables and when they are blank they error by saying no such table. We have tried every permutation of RecordSourceType and RowSourceType.
yes I do understand the way VFP devs like using cursors like that but that does not work web/mobile client/server so I’ll look and see what you are doing first.
There should be no issue with Lianja closing cursors unless you are selecting into the same cursor name in which case it behaves just like VFP.
it seems that what you want to do is choose multiple items in a list by checking the checkbox in the first column.
what happens as you do that? Does that cause you to perform a query on another grid? Then iterate this process on the child grids…?
i think this is where the design needs looked at.
what you need is a top level parent form e.g. Job# 
this then relates to the child table that has records added to it for the job. This then will work multiuser and in the web / mobile.
The trick is to add all the related child records when you create a new job# or proposal# and these can be done in an insert trigger. They are related by job# so you have one child table with all the multiple choices as records.
This is then properly relational and can be properly queried by “joining” the tables together and displaying the related data in the UI.
this will allow you to design the app as form -> grid -> grids and the Lianja engine will do all the heavy lifting.
and all that will work in web/mobile.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s