Dashboard displays are the paradigm for data cards, not diagrams. Rows and columns of gauges, trends, KPIs, tables. Every element lives in a Cell, and cells are arranged in a responsive grid that reflows on window resize.
...
Prerequisite: load Skill Display Construction — Basics first.
The whole display is defined by:
Columns and Rows arrays describing the grid track sizesRow, Col, optional RowSpan/ColSpan, a Cell.HeaderLink for the card title, and Content (the element that fills the cell)...
| language | json |
|---|
...
<ac:structured-macro ac:name="code"> <ac:parameter ac:name="language">json</ac:parameter> ac:plain-text-body{ "Name":
...
"OperationsOverview",
...
"PanelType":
...
"Dashboard",
...
"DashboardDisplay":
...
{
...
"Columns":
...
["*",
...
"*",
...
"*"],
...
"Rows":
...
["Auto",
...
"*",
...
"*"],
...
"Cells":
...
[
...
{
...
"Row":
...
0,
...
"Col":
...
0,
...
"ColSpan":
...
3,
...
"Cell.HeaderLink":
...
"Plant
...
Overview",
...
"Content":
...
{
...
/*
...
header
...
card
...
*/
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
0,
...
"Cell.HeaderLink":
...
"Production
...
Rate",
...
"Content":
...
{
...
/*
...
gauge
...
*/
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
1,
...
"Cell.HeaderLink":
...
"Active
...
Alarms",
...
"Content":
...
{
...
/*
...
alarm
...
viewer
...
*/
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
2,
...
"Cell.HeaderLink":
...
"Shift
...
Output",
...
"Content":
...
{
...
/*
...
KPI
...
*/
...
} }
...
] } }</ac:plain-text-body> </ac:structured-macro>
The Columns and Rows arrays define track sizes. Each entry can be:
"*" — equal share of remaining space (like CSS 1fr)"2*" — twice the share (like CSS 2fr)"Auto" — size to content"240" — fixed pixel width"*,min=200" — fractional with a minimum in pixelsStandard patterns:
| Layout | Columns | Rows |
|---|---|---|
| 3 equal columns, 2 rows | ["*","*","*"] | ["*","*"] |
| Header + 2 equal rows | ["*","*","*"] | ["Auto","*","*"] |
| Sidebar + main | ["240","*"] | ["*"] |
| Header + sidebar + main | ["240","*"] | ["Auto","*"] |
| 4 KPIs across top, trend below | ["*","*","*","*"] | ["Auto","*"] |
| Operator wall (4×3) | ["*","*","*","*"] | ["*","*","*"] |
In Canvas you place a gauge at Left: 472, Top: 320. In Dashboard you place a gauge at Row: 1, Col: 2. The engine handles pixel positioning, cell padding, and reflow.
This is a completely different mental model. If you find yourself calculating Left/Top/Width/Height for Dashboard elements, stop — you're building a Canvas display by accident.
Not every element type works inside a Dashboard cell. Rule of thumb:
...
Works in Dashboard
...
? Interaction, Charts, Gauges, Viewer, Editors, TextBlock, Label
...
Does NOT work in Dashboard
...
Setting a row or column to "*" gives that track the space; it does not force the cell's content to fill it. A fixed-size gauge in a "*" row will sit at its native size inside a large cell, not stretch. For content that should fill the cell, use stretch-friendly controls (TrendChart, BarChart, AlarmViewer, DataGrid). Gauges are fixed size; place them in cells sized to match.
Not every element type works inside a Dashboard cell. Call list_elements('Dashboard') for the authoritative compatibility list in the current release. Rule of thumb:
...
Dynamics work in Dashboard for visual controls (FillColorDynamic on a TextBlock, VisibilityDynamic on a chart) but the animation dynamics (Rotate, Scale, MoveDrag, Skew, Bargraph) are Canvas-only — they're silently ignored in Dashboard cells.
Six data cards in a 3-column × 2-row grid:
...
...
json
...
{
...
"Name":
...
"PlantKPIs",
...
"PanelType":
...
"Dashboard",
...
"DashboardDisplay":
...
{
...
"Columns":
...
["*",
...
"*",
...
"*"],
...
"Rows":
...
["*",
...
"*"],
...
"Cells":
...
[
...
{
...
"Row":
...
0,
...
"Col":
...
0,
...
"Cell.HeaderLink":
...
"Production
...
Rate",
...
"Content":
...
{
...
"Type":
...
"CenterValue",
...
"LinkedValue":
...
"@Tag.Plant/ProductionRate",
...
"CenterTextFormat":
...
"N1",
...
"AccentTextLink":
...
"units/hr"
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
1,
...
"Cell.HeaderLink":
...
"Active
...
Alarms",
...
"Content":
...
{
...
"Type":
...
"CenterValue",
...
"LinkedValue":
...
"@Tag.Plant/AlarmCount",
...
"CenterTextFormat":
...
"N0",
...
"AccentTextLink":
...
"alarms"
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
2,
...
"Cell.HeaderLink":
...
"Operator
...
on
...
Duty",
...
"Content":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"{@Tag.Shift/CurrentOperator}",
...
"FontSize":
...
22,
...
"FontWeight":
...
"Bold"
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
0,
...
"Cell.HeaderLink":
...
"Temp
...
Trend",
...
"Content":
...
{
...
"Type":
...
"TrendChart",
...
"Duration":
...
"5m",
...
"Pens":
...
[{"Type":"TrendPen","LinkedValue":"@Tag.Plant/AvgTemp","Stroke":"#FFEF4444"}]
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
1,
...
"Cell.HeaderLink":
...
"Pressure
...
Trend",
...
"Content":
...
{
...
"Type":
...
"TrendChart",
...
"Duration":
...
"5m",
...
"Pens":
...
[{"Type":"TrendPen","LinkedValue":"@Tag.Plant/AvgPressure","Stroke":"#FF38BDF8"}]
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
2,
...
"Cell.HeaderLink":
...
"Shift
...
Output",
...
"Content":
...
{
...
"Type":
...
"BarChart",
...
"LinkedValue":
...
"@Tag.Shift/OutputByHour"
...
}
...
}
...
]
...
}
...
}
Twelve cells. Full-screen control-room overview with one card per reactor / line / zone:
...
json "Columns":
...
["*","*","*","*"],
...
"Rows":
...
["*","*","*"]
Asset tree on the left, detail panel on the right:
...
json
...
{
...
"DashboardDisplay":
...
{
...
"Columns":
...
["240",
...
"*"],
...
"Rows":
...
["Auto",
...
"*"],
...
"Cells":
...
[ { "Row":
...
0,
...
"Col":
...
0,
...
"ColSpan":
...
2,
...
"Cell.HeaderLink":
...
"Production
...
Area
...
A",
...
"Content":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"{@Tag.Site/Name}
...
—
...
{@Now}"
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
0,
...
"Cell.HeaderLink":
...
"Equipment",
...
"Content":
...
{
...
"Type":
...
"AssetsTree"
...
}
...
},
...
{
...
"Row":
...
1,
...
"Col":
...
1,
...
"Cell.HeaderLink":
...
"{@Client.Context.AssetName}",
...
"Content":
...
{
...
"Type":
...
"ChildDisplay",
...
"DisplayLink":
...
"EquipmentDetail"
...
}
...
}
...
]
...
}
...
}
The AssetsTree has all its bindings pre-wired to @Client.Context.* by default — drop it in and navigation "just works."
...
json
...
{
...
"Row":
...
0,
...
"Col":
...
0,
...
"ColSpan":
...
2,
...
...
...
}
...
//
...
cell
...
spans
...
columns
...
0
...
and
...
1
...
{
...
"Row":
...
1,
...
"Col":
...
0,
...
"RowSpan":
...
2,
...
...
...
}
...
//
...
cell
...
spans
...
rows
...
1
...
and
...
2
Use ColSpan on a header card to stretch it across all grid columns. Use RowSpan when you want a tall element (AlarmViewer, AssetsTree) next to shorter cards.
| Cell purpose | Best control | Why |
|---|---|---|
| Display title / section header | TextBlock with composite LinkedValue | "{@Tag.Site/Name} — {@Now}" in one element |
| Single-value KPI (numeric) | CenterValue | Pre-styled big-number-plus-unit tile |
| Single-value KPI (string) | TextBlock with FontSize 24+ | Simpler than CenterValue for strings |
| Time-series trend | TrendChart | The workhorse |
| Tag value dial | RadialGauge | Gauge with threshold bands |
| Tag value bar (linear) | LinearGauge | For bar-style KPIs with setpoint pointer |
| Cumulative count / meter | DigitalGauge or DigitalMeter | Specialty numeric readouts |
| Categorical comparison | BarChart | e.g., output by shift |
| Current alarms | AlarmViewer | Pre-wired to Client.AlarmPage context |
| Asset hierarchy navigation | AssetsTree | Pre-wired to Client.Context |
| Dropdown selector | ComboBox (DataTable-backed for FK) | Zero-script FK lookup |
| Data table / list | DataGrid | The list in list→detail |
| Document viewer (SOP, work order) | PdfViewer | URL-bound inline PDF |
| Fleet/plant map | MapsOSM | Lat/long-positioned markers |
| Embedded another display | ChildDisplay | Reusable detail panels |
| Multiple panels user switches | TabControl | Manual click-to-switch |
| Slideshow of status boards | Carousel | Auto-cycle with AutoCycleLink |
| Collapsible panel | Expander | Click to expand advanced settings |
| Card-per-item layout | FlowPanel | N items from a data source, one template |
Cell.HeaderLink patternEvery cell should have a Cell.HeaderLink. The value is either:
...
The header renders as a small title strip at the top of the card, in the theme's card-header style. Leaving it empty gives you a headerless card — useful for the header / status row banding the top of the display, but unusual elsewhere.
Most dashboard cells end up being trend charts. The canonical setup:
...
json
...
{
...
"Type":
...
"TrendChart",
...
"Duration":
...
"5m",
...
"YMinValue":
...
0,
...
"YMaxValue":
...
100,
...
"YLabels":
...
5,
...
"XGridLines":
...
6,
...
"YGridLines":
...
5,
...
"LegendPlacement":
...
"BottomPanel",
...
"VerticalCursor":
...
true,
...
"BackgroundTheme":
...
"ControlBackground",
...
"ForegroundTheme":
...
"TextForeground",
...
"BorderBrushTheme":
...
"DefaultBorder",
...
"Pens":
...
[
...
{
...
"Type":
...
"TrendPen",
...
"LinkedValue":
...
"@Tag.Reactor/Temperature_C",
...
"PenLabel":
...
"Temperature",
...
"Stroke":
...
"#FFEF4444",
...
"StrokeThickness":
...
2
...
},
...
{
...
"Type":
...
"TrendPen",
...
"LinkedValue":
...
"@Tag.Reactor/Setpoint",
...
"PenLabel":
...
"Setpoint",
...
"Stroke":
...
"#FF34D399",
...
"StrokeThickness":
...
1
...
}
...
]
...
}
"Pens": [ {...}, {...} ] ← prefer this for readability"Pens": { "Type": "TrendPenList", "Children": [ {...} ] } ← older form, also accepted| Property | Required | Notes |
|---|---|---|
LinkedValue | ? | Tag binding: @Tag.Reactor/Temperature_C |
PenLabel | — | Legend text |
Stroke | — | Line color hex. Not theme-aware — hex only. |
StrokeThickness | — | Default 1; use 2 for emphasis, 3 for setpoint/limit |
YMin / YMax | — | Per-pen Y scale (overrides chart scale) |
YAxisPosition | — | "Left" (default) or "Right" for dual-axis plots |
Auto | — | true for auto-scaling that pen |
Visible | — | true/false |
| Duration | When |
|---|---|
"1m" | Demos — populates visibly in ~60s |
"5m" | Default operator view — recent activity |
"15m" | Short-term process monitoring |
"1h" / "4h" | Shift-length trends |
"24h" / "7d" | Production reports, historical review |
"BottomPanel" (default), "RightPanel", "TopPanel", "None". For narrow cells use "RightPanel". For wide cells "BottomPanel".
Call list_elements('XYChartCharts') currently returns the TrendChart schema byte-for-byte. For a true for the authoritative chart catalog in the current release. TrendChart is the right default for time-series data. For X-vs-Y scatter plot, this isn't it. Use TrendChart for time-series; for correlation plots wait on TDEV-1276 or compose one from Canvas primitivesor correlation displays, verify the current element set before committing — the available chart types and their schemas evolve between releases.
Use a UserType-typed Client tag as the "selected row" carrier. The DataGrid pushes selected-row values into its fields, detail controls read from its fields via @Tag.Selected<X>.Field. Zero code-behind.
@Tag.SelectedReactor with UserType: "ReactorRow".SelectedValuesLink to "@Tag.SelectedReactor". The DataGrid automatically pushes values from the currently-selected row into each matching field of the UDT.@Tag.SelectedReactor.Name, @Tag.SelectedReactor.Temperature, etc. They update automatically as the operator clicks rows....
| language | json |
|---|
...
json { "Type":
...
"DataGrid",
...
"ItemsSource":
...
"@Dataset.Query.ActiveReactors",
...
"SelectedValuesLink":
...
"@Tag.SelectedReactor",
...
"Columns":
...
{
...
"Type":
...
"GridColumnList",
...
"Children":
...
[
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Reactor",
...
"FieldName":
...
"Name",
...
"Width":
...
120
...
},
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Temp
...
(°C)",
...
"FieldName":
...
"Temperature",
...
"Width":
...
100
...
},
...
{ "Type":
...
"GridColumn",
...
"Title":
...
"Pressure",
...
"FieldName":
...
"Pressure",
...
"Width":
...
100
...
},
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Status",
...
"FieldName":
...
"Status",
...
"Width":
...
100
...
}
...
]
...
},
...
"BackgroundTheme":
...
"ControlBackground",
...
"ForegroundTheme":
...
"TextForeground",
...
"BorderBrushTheme":
...
"DefaultBorder"
...
}
...
json
...
{
...
"Row":
...
1,
...
"Col":
...
1,
...
"Cell.HeaderLink":
...
"Temperature",
...
"Content":
...
{
...
"Type":
...
"CenterValue",
...
"LinkedValue":
...
"@Tag.SelectedReactor.Temperature",
...
"CenterTextFormat":
...
"N1",
...
"AccentTextLink":
...
"°C"
...
}
...
},
...
<p>{ "Row": 1, "Col": 2, "Cell.HeaderLink": "Pressure", "Content": { "Type": "CenterValue", "LinkedValue": "@Tag.SelectedReactor.Pressure", "CenterTextFormat": "N1", "AccentTextLink": "bar" } },</p> <p>{ "Row": 2, "Col": 1, "ColSpan": 2, "Cell.HeaderLink": "Status", "Content": { "Type": "TextBlock", "LinkedValue": "Status: {@Tag.SelectedReactor.Status}
...
When ", "FontSize": 16 } }]]></ac:plain-text-body> </ac:structured-macro></p> <p>When the operator clicks a row in the DataGrid, all three detail cells update simultaneously. No script, no event handler.
...
</p> <h3>Requirements</h3> <ul> <li>The UDT's member names must match the DataGrid column
...
<code>FieldName</code>s (case-sensitive).
...
</li> <li>The Client tag must be typed by that UDT.
...
</li> <li>The dataset query must return rows whose column names match the UDT members.
...
</li> </ul> <h2>Section 6 — ComboBox zero-script FK-
...
| language | json |
|---|
...
lookup</h2> <ac:structured-macro ac:name="code"> <ac:parameter ac:name="language">json</ac:parameter> <ac:plain-text-body><![CDATA[{ "Type":
...
"ComboBox",
...
"ItemsSourceType":
...
"DataTable",
...
"ItemsSourceLink":
...
"@Dataset.Query.OperatorsList",
...
"DisplayMember":
...
"FullName",
...
"SelectedValuePath":
...
"OperatorID",
...
"SelectedValueLink":
...
"@Tag.Shift/CurrentOperatorId",
...
"Foreground":
...
"theme:TextForeground",
...
"Background":
...
"theme:ControlBackground",
...
"BorderBrush":
...
"theme:DefaultBorder"
...
}
When the operator picks "Maria Costa" from the dropdown, the OperatorID (say 42) lands in @Tag.Shift/CurrentOperatorId automatically. Other displays binding to that tag update immediately.
Use this for: operator selection, product changeover, reactor mode select, batch selection — anywhere the user picks one row from a database-backed list.
"DataTable" — the pattern above, dataset query as source"Array" — bound to an array tag"StringTag" — comma-separated string (legacy, avoid)"Text" — hardcoded comma-separated inline optionsThe AlarmViewer default template ships pre-wired to @Client.AlarmPage.* context tags:
...
json
...
{
...
"Type":
...
"AlarmViewer",
...
"ShowRowSelectorPane":
...
false,
...
"BackgroundTheme":
...
"ControlBackground",
...
"ForegroundTheme":
...
"TextForeground"
...
}
That's the entire cell content. All alarms, all columns, all features — wired.
...
...
json
...
{
...
"Type":
...
"AlarmViewer",
...
"Filter":
...
"Area
...
=
...
'ReactorZone'",
...
"ShowRowSelectorPane":
...
false
...
}
Filter syntax: "Priority >= 2", "Area = 'Tank1'", or a tag binding "@Tag.AlarmFilter".
...
json
...
"Columns":
...
{
...
"Type":
...
"GridColumnList",
...
"Children":
...
[
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Active",
...
"FieldName":
...
"ActiveTime_Ticks",
...
"Width":
...
140
...
},
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Tag",
...
"FieldName":
...
"TagName",
...
"Width": 200 }, { "Type":
...
"GridColumn",
...
"Title":
...
"Msg",
...
"FieldName":
...
"Message",
...
"Width":
...
400
...
},
...
{
...
"Type":
...
"GridColumn",
...
"Title":
...
"Pri",
...
"FieldName":
...
"Priority",
...
"Width":
...
50
...
}
...
]
...
}
Available field names: AckStatus, ActiveTime_Ticks, TagName, Group, Value, ID, ItemName, State, AckRequired, Condition, SolutionName, Area, Priority, NormTime_Ticks, AckTime_Ticks, UserName, Message, Duration, Category, DateCreated_Ticks, AuxValue, AlarmLimit, PreviousValue, AuxValue2, AuxValue3. Tick fields format as DateTime at render time.
The canonical asset-driven master-detail pattern. One "detail template" display shows whatever asset the operator picks in the tree.
...
json
...
{
...
"Type":
...
"AssetsTree"
...
}
The default template already binds:
LinkedValue → @Client.Context.AssetName (output — which asset is selected)AssetPathLink → @Client.Context.AssetPath (output — full path)...
json
...
{
...
"Type":
...
"ChildDisplay",
...
"DisplayLink":
...
"EquipmentDetailTemplate"
...
}
Inside EquipmentDetailTemplate, every binding uses Asset(@Client.Context.AssetPath + ".Property") or direct @Tag paths constructed from the context. When the operator clicks a different tree node, the ChildDisplay re-renders with the new asset's data.
...
json
...
{
...
"Type":
...
"ChildDisplay",
...
"DisplayLink":
...
"@Tag.DetailDisplayName"
...
}
A Script calculates which detail display to show based on the selected asset's type (Pump → PumpDetail, Reactor → ReactorDetail) and writes to @Tag.DetailDisplayName. The ChildDisplay swaps automatically.
Both accept the same structure: HeaderElements (navigation chrome) and TabItems (panels). Difference:
AutoCycleLink: 5 for 5-second auto-cycling. Lobby displays, rotating KPI boards....
| language | json |
|---|
...
<ac:structured-macro ac:name="code"> <ac:parameter ac:name="language">json</ac:parameter> ac:plain-text-body{ "Type":
...
"Carousel",
...
"AutoCycleLink":
...
5,
...
"TabItems":
...
[
...
{
...
"Type":
...
"TabItem",
...
"IsSelected":
...
true,
...
"Header":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"Production"
...
},
...
"Children":
...
[
...
{
...
"Type":
...
"TrendChart",
...
...
...
}
...
]
...
},
...
{
...
"Type":
...
"TabItem",
...
"Header":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"Quality"
...
},
...
"Children":
...
[
...
{
...
"Type":
...
"BarChart",
...
...
...
}
...
]
...
},
...
{
...
"Type":
...
"TabItem",
...
"Header":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"Alarms"
...
},
...
"Children":
...
[
...
{
...
"Type":
...
"AlarmViewer"
...
}
...
]
...
}
...
]
...
}</ac:plain-text-body> </ac:structured-macro>
Rules:
IsSelected: trueChildren is an array — can contain multiple elements per tabAutoCycleLink can be a number (seconds) OR a tag binding ("@Tag.ShowroomCycleSeconds") for runtime-configurable cycling...
...
json
...
{
...
"Type":
...
"CenterValue",
...
"LinkedValue":
...
"@Tag.Plant/ProductionRate",
...
"CenterTextFormat":
...
"N1",
...
"AccentTextLink":
...
"units/hr",
...
"CenterFontSize":
...
48,
...
"AccentFontSize":
...
14,
...
"BackgroundTheme":
...
"ControlBackground"
...
}
CenterTextFormat: "N0" (integer), "N1" (1 decimal), "N2" (2 decimals), "P1" (percent 1 decimal). AccentTextLink is the unit or caption under/beside the main number.
...
...
json
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"Throughput:
...
{@Tag.Plant/ProductionRate}
...
units/hr
...
({@Tag.Plant/TargetPct}%
...
of
...
target)",
...
"FontSize":
...
16,
...
"FontWeight":
...
"SemiBold",
...
"ForegroundTheme":
...
"TextForeground"
...
}
...
json
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"{@Tag.Plant/AvgTemp}
...
°C",
...
"FontSize":
...
32,
...
"Dynamics":
...
[
...
{
...
"Type":
...
"TextColorDynamic",
...
"LinkedValue":
...
"@Tag.Plant/AvgTemp",
...
"ChangeColorItems":
...
[
...
{
...
"Type":
...
"ChangeColorItem",
...
"ChangeLimit":
...
0,
...
"LimitColor":
...
"#FF38BDF8"
...
},
...
{
...
"Type":
...
"ChangeColorItem",
...
"ChangeLimit":
...
75,
...
"LimitColor":
...
"#FF34D399"
...
},
...
{ "Type": "ChangeColorItem",
...
"ChangeLimit":
...
90,
...
"LimitColor":
...
"#FFF59E0B"
...
},
...
{
...
"Type":
...
"ChangeColorItem",
...
"ChangeLimit":
...
100,
...
"LimitColor":
...
"#FFEF4444"
...
}
...
]
...
}
...
]
...
}
Blue cold → green normal → amber warning → red alarm.
Same rule as Canvas: page-to-page navigation belongs in the Header display, not on content pages. Content pages get only in-page interactions.
...
json
...
{
...
"Name":
...
"Header",
...
"PanelType":
...
"Dashboard",
...
"DashboardDisplay":
...
{
...
"Columns":
...
["Auto","Auto","Auto","Auto","*","Auto"],
...
"Rows":
...
["*"],
...
"Cells":
...
[ { "Row": 0, "Col":
...
0,
...
"Content":
...
{
...
"Type":
...
"Button",
...
"LabelLink":
...
"Overview",
...
"Dynamics":
...
[{"Type":"ActionDynamic","MouseLeftButtonDown":{"Type":"DynamicActionInfo","ActionType":"OpenDisplay","ObjectLink":"OperationsOverview"}}]
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
1,
...
"Content":
...
{
...
"Type":
...
"Button",
...
"LabelLink":
...
"Alarms",
...
...
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
2,
...
"Content":
...
{
...
"Type":
...
"Button",
...
"LabelLink":
...
"Trends",
...
...
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
3,
...
"Content":
...
{
...
"Type":
...
"Button",
...
"LabelLink":
...
"Reports",
...
...
...
}
...
},
...
{
...
"Row":
...
0,
...
"Col":
...
5,
...
"Content":
...
{
...
"Type":
...
"TextBlock",
...
"LinkedValue":
...
"Logged
...
in:
...
{@Client.Username}"
...
}
...
}
...
]
...
}
...
}
Minimum button size: 100×32. Recommended: 130×40 for touch-friendly control rooms.
For DataGrids, a double-click to drill into detail:
...
json
...
"Dynamics":
...
[
...
{
...
"Type":
...
"ActionDynamic",
...
"MouseDoubleClick":
...
{
...
"Type":
...
"DynamicActionInfo",
...
"ActionType":
...
"OpenDisplay",
...
"ObjectLink":
...
"ReactorDetail"
...
}
...
}
...
]
The target display reads @Tag.SelectedReactor.Name (etc.) — so you get "double-click row → open detail view of that row" with zero code.
Row: "*" to expand, but the cell content is a fixed-size gauge, the gauge doesn't stretch — the cell is as tall as the row, but the gauge stays its native size centered in the cell. For content that should fill the cell, use controls with stretch-friendly behavior: TrendChart, BarChart, AlarmViewer, DataGrid. Gauges are a fixed size.RotateDynamic, ScaleDynamic, MoveDragDynamic, SkewDynamic, BargraphDynamic. If you need a rotating element or a custom level-bar, move that cell's content to a Canvas display embedded via ChildDisplay, or pick a control with the behavior built in (LinearGauge for level, symbols for motor-running-spin).Cell.HeaderLink renders in a theme-defined style — you can't set FontSize or Foreground on the header itself from the cell. If you need a custom header, set Cell.HeaderLink to empty string and put a TextBlock as the first element inside the cell Content.@Tag.SelectedValueLink with a pre-populated default.RotateDynamic, ScaleDynamic, MoveDragDynamic, SkewDynamic, BargraphDynamic. If you need a rotating element or a custom level-bar, move that cell's content to a Canvas display embedded via ChildDisplay, or pick a control with the behavior built in (LinearGauge for level, symbols for motor-running-spin).Cell.HeaderLink renders in a theme-defined style — you can't set FontSize or Foreground on the header itself from the cell. If you need a custom header, set Cell.HeaderLink to empty string and put a TextBlock as the first element inside the cell Content.@Tag.SelectedValueLink with a pre-populated default.PanelType: "Dashboard" is setDashboardDisplay object includes Columns, Rows, CellsRow and Col; Cell.HeaderLink is either a non-empty string or absentDisplayMember AND SelectedValuePath AND SelectedValueLinkLinkedValue + Strokeget_state PanelType: "Dashboard" is setDashboardDisplay object includes Columns, Rows, CellsRow, Col, and either Cell.HeaderLink (with content) or omitted for header-less cellsDisplayMember AND SelectedValuePath AND SelectedValueLinkLinkedValue + Strokeget_state after write shows errorList empty| Code Block |
|---|
TextBlock - headers, composite KPIs, status text
CenterValue - big-number KPI tiles
TrendChart - all time-series (the workhorse)
BarChart - categorical comparison
AlarmViewer - alarm list with default template
AssetsTree - plant navigation sidebar
ChildDisplay - embedded detail panel
DataGrid - list in list?detail
ComboBox - FK selector dropdown
RadialGauge - circular gauge cells |
...
| language | json |
|---|
...
text TextBlock // headers, composite KPIs, status text CenterValue // big-number KPI tiles TrendChart // all time-series (the workhorse) BarChart // categorical comparison AlarmViewer // alarm list with default template AssetsTree // plant navigation sidebar ChildDisplay // embedded detail panel DataGrid // list in list-to-detail ComboBox // FK selector dropdown RadialGauge // circular gauge cells
...