A DataView displays rows of data. It is similar to a DataGrid form, a control available in the LiveCode IDE.
LiveCode Version: 8.x or higher
Platforms: Tested on Windows, macOS, and iOS.
A DataView is responsible for taking row data that your code provides and rendering it in a highly customizable way using row templates. Row data is an array of key=>value pairs. Out of the box you can assign a numerically indexed array of arrays with key=>value pairs to a DataView (see example below). But you can customize the data source any way you would like.
The DataView Demo application includes an example of using this helper:
Creating a DataView
The DataView helper includes two commands which can create DataView group controls in the LiveCode IDE:
dvIdeCreateDataViewControl pName, pTargetCard, pBehavior
dvIdeCreateDataViewControlUsingDialog pTargetCard, pBehavior, pRowStyleTemplateGroupsA
dvIdeCreateDataViewControl will create a DataView with no additional input. The only required parametr is
pName which will be the name of the group that is created. If
pTargetCard is empty then the DataView group control will be added to the current card of the
topStack. If you would like to assign a behavior to the DataView group control other than the "DataView Behavior" stack then pass a reference to it in
pBehavior. For example, if you want to create a new DataView group control in the current card of the
topStack that uses the DataView Array controller then you would make the following call:
dvIdeCreateDataViewControl "My DataView", empty, the long id of stack "DataView Array Controller Behavior"
dvIdeCreateDataViewControlUsingDialog will display a dialog with additional options.
pBehavior behave the same way as for
The modal dialog that appears has a couple of additional options:
- There is a field for entering a name for the DataView group control.
- There is a checkbox for creating a row template in the ./templates folder of your Levure application.
- There is a filed where you can enter a comma-delimited list of row template styles you want to create. One row template group will be created for each style in the list.
- There is a checkbox for specifying that you want to create a new behavior to assign to the DataView group control. This new behavior is used for your application specific code that you want to attach to the DataView. It will be saved as a script only stack in a "behaviors" folder sitting alongside the stack that the DataView is created in. The script only stack file will also be added to the
stackFilesproperty of the stack.
If you pass in a value for
pBehavior will be assigned as the behavior of the new behavior that is created. If you do not pass in
pBehavior then the "DataView Behavior" stack will be assigned as the behavior of the new behavior.
Note: If you plan on adding your application specific logic directly to the DataView group control script then you do not need to create an additional behavior script. Creating the additional behavior script is only necessary if you are using version control with your application or if prefer editing your LiveCode scripts in a separate text editor.
Here is an example of creating a new DataView that uses the array controller and populating it with some test data. It is assumed that in the dialog you opted to create a row behavior. The row template that is created is coded to display a "label" key in each row.
dvIdeCreateDataViewControlUsingDialog empty, the long id of stack "DataView Array Controller Behavior" put "Line 1" into tA["label"] put "Line 2" into tA["label"] put "Line 3" into tA["label"] put "Line 4" into tA["label"] set the dvData of group "My DataView" to tA
If you make changes to the row template group and want to see the change reflected in the DataView then issue the following calls:
dispatch "ResetView" to group "My DataView" dispatch "RenderView" to group "My DataView"
Assigning data to a DataView
To test that a new DataView is working you can create a simple numerically indexed array and assign it to it's
dvData property. The default row template displays a "title" key in a field so a simple test would look like this:
put "Row 1" into tDataA["title"] put "Row 2" into tDataA["title"] put "Row 3" into tDataA["title"] set the dvData of group "MyDataView" to tDataA
Customizing row templates
Each row in a DataView is rendered using a row template. A row template is simply a
group that contains all of the controls necessary to display the data for a row. The group has a behavior script assigned to it that contains all of the logic for displaying row data in the template and positioning the controls in the template.
Mapping row template groups to row styles
Each row in a DataView has a style assigned to it. The default style is
default and that is the only style that is supported if you are using the data controller behavior script assigned to a DataView by default. If you create a custom data controller script then you can use style names of your choosing.
Each style needs to be mapped to a group serving as a row template. You map a row template group to a row style through the
row style templates property of a DataView. This property is an array whose keys are style names and values are a reference to a group control. Here is a simple example:
put the long id of group "MyRowTemplate" of stack "MyRowTemplateStack" \ into tStyleTemplatesA["default"] set the viewProp["row style templates"] of group "MyDataView" to tStyleTemplatesA
After running the above code all rows in the "MyDataView" DataView would use the "MyRowTemplate" group as the row template.
If your DataView needs more than one row template than you can use multiple styles. For example:
put the long id of group "HeadingTemplate" of stack "MyRowTemplateStack" \ into tStyleTemplatesA["heading"] put the long id of group "ParagraphTemplate" of stack "MyRowTemplateStack" \ into tStyleTemplatesA["paragraph"] set the viewProp["row style templates"] of group "MoreComplexView" to tStyleTemplatesA
After running the above code you could assign either the
paragraph style to each row in the "MoreComplexView" DataView and the appropriate row template group would be used.
Where do I store row template groups?
Row templates should be stored in the
.app/templates folder of a Levure project. Stacks in this folder are treated like a
ui stack in that they are not loaded into memory at startup but will be loaded when the stack name is referenced in code. What is different is that stacks in the
./app/template folder will not be password protected which means their contents can be copied into a DataView in a standalone that is otherwise password protected.
ui folder, each folder in the
templates folder contains one or more livecode stacks as well as the supporting behavior script files. Here is an example of a
./app/templates/my-row-template/ folder that stores the behavior in a script only stack:
Each stack should have the following properties:
- The stack has one card.
- The card has one or more groups serving as a row template.
- Each group has a behavior assigned to it.
- If the behavior is stored in a script only stack then then assign the behavior script only stack file to the
stackFilesproperty of the stack the group is a part of.
FillInData pDataA, pRow
LayoutControl pControlRect, pRow
HideRowControl # if cache is
OpenNextFieldEditor pRow, pKey, pDirection, pActionTriggeredBy
CloseFieldEditor pEditor, pRow, pKey, pEventTriggeredByEvent
ExitFieldEditor pEditor, pRow, pKey, pEventTriggeredByEvent
DragReorderRows pTargetRows, pEffectiveDroppedAfterRow, pActualDroppedAfterRow
Creating a custom data controller
A DataView doesn't have any internal knowledge of the data that it is displaying. Each time it displays a row it asks the outside world to provide the data for that row. When it needs to know how many total rows it should display it also asks the outside world. The code that provides that data can be thought of a data controller. The data controller script orchestrates moving data from a data source into a DataView and saving any changes made to data within the DataView back to the data source.
The DataView helper comes with a data controller script (
dataview_controller.livecodescript) that is assigned as a behavior of a new DataView. This data controller script allows you to assign a numerically indexed array of data to the
uData property of the DataView. It will then handle feeding the data in that array to the DataView.
For more advanced cases you can remove this data controller script as the behavior and use your own data controller code. That code can reside in a different behavior script that you assign to the DataView or it might exist in another script in the message hierarchy – e.g. a
group that the DataView is in or in the
stack script. Regardless of where your data controller code is located, you will need to handle one message and two functions. The message is
DataForRow and the functions are
CacheKeyForRow(). These handlers will be sent and called when you send the
RenderView command to the DataView.
Let's look at each in turn. In the example code for each handler assume that
sData is a numerically indexed array.
DataForRow message is sent whenever the DataView needs data to associate with a row. It takes three parameters:
DataForRow pRow, @rDataA, @rTemplateStyle
pRowis the number of the row that you should provide data for.
rDataAis an array that you populate with the data needed to display the row.
rTemplateStyleis the style to associate with the row. If you don't provide a value then
defaultwill be used.
command DataForRow pRow, @rDataA, @rTemplateStyle put sDataA[pRow] into rDataA put "default" into rTemplateStyle end DataForRow
command DataForRow pRow, @rDataA, @rTemplateStyle sqlquery_moveToRecord sQueryA, pRow put sqlquery_currentRowToArray(sQueryA) into rDataA put "default" into rTemplateStyle end DataForRow
NumberOfRows() function must return the number of rows being displayed in the DataView.
function NumberOfRows return the number of elements of sDataA end NumberOfRows
function NumberOfRows return sqlquery_get(sQueryA, "number of records") end NumberOfRows
CacheKeyForRow() function must return a unique identifier for each row. If you don't define the
CacheKeyForRow() function in the message path then the DataView will use the row number to uniquely identify each row. If your DataView is displaying a flat list of data that cannot be reordered and that never toggles the visibility of rows then there is nothing further that needs to be done.
If, however, the row that data in your data source is associated with can change between calls to
ResetView then you must handle
CacheKeyForRow() and return a unique identifier for the row. For example, the primary key column from a database table will be adequate in most cases. If your DataView is displaying records from multiple tables then the primary key might not be sufficient as the primary keys from two different tables aren't necessarily unique.
function CacheKeyForRow pRow return pRow end CacheKeyForRow
function CacheKeyForRow pRow return sDataA[pRow]["id"] end CacheKeyForRow
function CacheKeyForRow pRow return revDatabaseColumnNamed(sCursorId, "id") end CacheKeyForRow
Specifying whether or not a row is selectable
If your DataView has rows that should not be selectable by the user then return false for the
dvCanSelect property of the row template. The following script can be added to a row template behavior:
getProp dvCanSelect return false end dvCanSelect
How caching works
Opening a field editor in a DataView
EditKeyOfRowto the DataView
- DataView dispatches
EditKeyto the row control.
CreateFieldEditorForFieldand pass in the field to edit in the row control.
Possible values for pEventThatTriggeredClose are
close control is sent when the user scrolls the row that is being edited out of view
and caching is not turned on.
animate selections property
The DataView can animate selections of rows that are not currently in view. You need to set the
viewProp["animate selections"] property to true and have the animationEngine library in use.