6.3 Sequence and Value Aggregation

To populate a View object, a correct sequence is required when setting the View attributes. They are set by the IDM in a predefined, class-specific sequence (as in the attribute list of the object) to take the dependencies between the attributes into account. For a poptext, as an example, the sequence

…, .itemcount, …, .text[], …, .activitem, …, .userdata[], …

applies to observe the dependency of .text[], .activeitem and .userdata[] upon the .itemcount attribute. However, this sequence can only be observed for the representation ( that is, a synchronization triggered at the View component, e.g. via :represent()). The IDM has no influence on the sequence when the synchronization is triggered by the Model component, e.g. when using the DM_DataChanged() function.

If a Data Model is linked to all four attributes, first the number of elements is set, then the texts, then the active element is altered and finally the .userdata[] field is set.

In principle, the IDM does not prohibit using user-defined attributes as View attributes, but cannot ensure a consistent sequence for them. User-defined attributes and predefined attributes that do not belong to the object class are always handled after the predefined attributes for which the object class determines the sequence.

There are different kinds of value aggregation, i.e. the relation of data values between Model and View:

Figure 16-25: Relations between Model and View

There are two attributes to enable these kinds of relations:

anyvalue .dataindex[<View or Model attribute>] <index>

attribute .datamap[<View attribute>] <View attribute>

The .dataindex[] attribute is used to select a value from a value list and to transform indexed values. It should be noted that the attributes may occur as scalar, array, associative array or matrix (two-dimensional array), but access always only allows a vector (i.e. a one-dimensional array).

When assigning collections (value count <n>) to a vector attribute (one-dimensional or two-dimensional), the following transformations are carried out depending on the index:

Table 16-5: Index transformations when assigning collections

Index

Assignment

[0,<col>]

[1,<col>] … [<n>,<col>]

[<row>,0]

[<row>,1] … [<row>,<n>]

[<row>,<col>]

[<row>,<col>]

0

1 … <n>

<row>

<row>

void

void

others

void

The .datamap[] attribute allows merging data values (from one or more Data Models), which shall be mapped to exactly one View attribute. The main field of application will be two-dimensional attributes as found on the tablefield object. For this purpose, a virtual View attribute is used, which is then mapped to an actual View attribute by means of the .datamap[] attribute. Any predefined or user-defined attribute may serve as a virtual attribute, but it may be preferable to use an existing attribute on the View object to influence the sequence for a complete representation of all model values.

Example

The following example illustrates these different kinds of relations. For instance, the View object PtMonth is linked to the entire content of the attribute .MonthName[] from the Model object RecDate. In contrast, the View object StCurMonth displays only the 3rd item from the RecDate.FullMonthName[] array.

For the View object TfCurWeek, the data linkage is quite more complex. The weekday names from RecDate.DayName[7] are distributed to the header row (elements [1,1] … [1,7]) by the linkage .dataindex[.content] [1,0];. To link the week numbers into the table, the .field attribute is used by means of the definitions .dataget[.field] .WeekNr; and .dataindex[.field] [0,8];. Since there is a header row set through .rowheader 1;, the week numbers are written to the elements [2,8] … [6.8]. For marking the current day, the virtual attribute .text is used, for what a mapping .datamap[.text] .content; has been established to direct the marker text into the field .content[4,3.

dialog D
record RecDate
{
  integer CurMonth := 3;
  integer CurDay := 23;
  integer CurWeekDay := 3;

  string DayName[7];
  .DayName[1] := "Mon";
  ...
  string MonthName[12];
  .MonthName[1] := "Jan";
  ...
  string FullMonthName[12];
  .FullMonthName[1] := "January";
  ...
  integer WeekNr[5];
  .WeekNr[1] := 9;
  ...
}

window WiData 
{
  .title "Datamodel: index-coupling";
  .width 600;  .height 400;
  .datamodel RecDate;

  poptext PtMonth
  {
    .xauto 0;
    .dataget .MonthName;  /* fills poptext.text[] with "Jan","Feb","Mar",... */
    .dataget[.userdata] .FullMonthName;  /* fill full names into .userdata[] */
    .dataget[.activeitem] .CurMonth;
  }

  statictext StCurMonth
  {
    .ytop 30;
    .xauto 0;
    .alignment 0;
    .dataget .FullMonthName;
    /* fills statictext.text with "Mar" by Model index */
    .dataindex[.FullMonthName] 3;
  }

  tablefield TfCurWeek
  {
    .xauto 0;  .yauto 0;
    .ytop 60;
    .rowheight[0] 30;  .colwidth[0] 60;
    .colcount 8;  .rowcount 6;
    .content[1,8] "Week";
    .rowheader 1;
    .dataget[.content] .DayName;
    .dataindex[.content] [1,0];  /* fill "Mon"... to first row via View index */
    .dataget[.field] .WeekNr;  /* map to .field attribute to fill week no.    */
    .dataindex[.field] [0,8];  /* vertical into last column below the header  */
    .datamodel[.text] Marker;  /* mark a specific day using the               */
    .dataget[.text] .value;    /* .datamap attribute, because it goes into    */
    .datamap[.text] .content;  /* the already used View attribute .content!   */
    .dataindex[.text] [4,3];
  }

  on close { exit(); }
}

The screenshot for it looks like this:

Figure 16-26: A tablefield populated from different Data Models

It is crucial to understand this fact:

Data exchange between Model and View works via the existing mechanisms of the IDM, i.e. setvalue(), getvalue(), setvector() und getvector(). This implies that the exchanged data values are either scalar or vectorial.

If the change of a Model attribute is signaled (e.g. in a custom data function by calling the DM_DataChanged() function), this usually yields a Model index as an indication for the changed detail (typically of type void, integer or index). Since this change signaling is processed by the IDM as a datachanged event, multiple changes are combined into the overall change (void index) respectively redundant events are omitted.

In conjunction with the ability to influence the value aggregation through the .dataindex[] attribute, this has several consequences and particularities to be considered:

  1. Complete population of a two-dimensional attribute (e.g. .content[] at the tablefield) is possible without problems if the alignment as well as the number of columns and rows match the data length. The table size is adjusted through the setvector() functionality.

    However, single value modifications can only be updated at the correct cell in the tablefield if the Data Model issues a change notification with the respective index.

    Transfer of a two-dimensional array with its row and column form to an arbitrary position in a tablefield is however not possible, only the signaling of value changes at any position.

  2. With the use of .dataindex[] at the tablefield, a scalar or vector data value can be moved to an explicitly defined cell, row or column. Automatic cell extension happens according to the rules of setvalue() or setvector() and therefore also depends on the index transformation. Please note that user-defined attributes (e.g. arrays) do not support automatic extension.
  3. The same applies to vector and matrix attributes as they exist on listbox, treeview and poptext.
  4. The index for a Model attribute is used to avoid unnecessary propagation of value changes (e.g. a change of Data.Attr[5] is of no relevance for View objects linked to Data.Attr[2] only).
  5. When using void (default value for .dataindex[]) for the index transformation, propagation of the Model index – signaled by a datachanged event – is performed depending on the attribute type (scalar, vectorial, two-dimensional) up to the presentation by :represent(). Among others this is used in the example randomcolors.dlg to link color values from an associative array to the .bgc[] attribute of a tablefield object. However, this also means that linkages with different value dimensions (e.g. an array is mapped to a scalar) result in a value update depending on the index of the last change - which is usually not surprising. A possible solution is to use the correct index for the View and Model attributes by means of the .dataindex[] attribute.
  6. Setting default values (index [0], [0,0], [0,*] or [*,0]) of the involved Model and View attributes is not possible.

The following examples (located in the examples/datamodel/ subdirectory of the IDM installation directory) are worth a look at the source code as well as the trace file during execution:

relations.dlg

Demonstrates various value aggregations of scalar or vector data values in a listbox object with and without adaptation of the .content[] field as well as the effect of single changes.

Figure 16-27: Value aggregations for list objects, example relations.dlg

matrixrel.dlg

Demonstrates several simple and complex value aggregations of different data values into a tablefield object.

Figure 16-28: Value aggregations for tablefields, example matrixrel.dlg

numbers.dlg

Demonstrates the complete population of a tablefield object with a header row, including automatic row count adjustment.

Figure 16-29: Populating an entire tablefield, example numbers.dlg

puzzle.dlg

Demonstrates populating a tablefield from a record as a Data Model or with a data function (local or remote).

Figure 16-30: Populating a tablefield from record and data function, example puzzle.dlg