Display Design Best Practices

Best practices and known patterns for creating displays, symbols, and dashboards via MCP.

Table Types

Use DisplaysList for creating and editing displays. DisplaysDraw is the Designer visual editor tab — not a writable table. Similarly, use DisplaysSymbols for symbols. Always call get_table_schema('DisplaysList') for the display schema.

Display vs Symbol Sizing

Displays use Width and Height properties directly on the display object. Symbols use a Size property with format "Width,Height" (e.g., "400,300"). Do not mix them — Size on a display is ignored, Width/Height on a symbol are ignored.

Canvas Positioning

Every element on a Canvas display needs explicit position:

Elements without position stack at (0,0). For a 1920×1080 display, typical layout starts elements at Left=20, Top=20 with spacing.

Dashboard Cell Format

Dashboard displays use a flat Cells array. Each cell needs:

{
  "Type": "DashboardDisplay",
  "Columns": 3,
  "Rows": 2,
  "Cells": [
    { "Column": 0, "Row": 0, "Elements": [ ... ] },
    { "Column": 1, "Row": 0, "ColumnSpan": 2, "Elements": [ ... ] }
  ]
}

Do not create CellGroup, Grid, RowDefinition, or ColumnDefinition objects — those are internal WPF representations. The importer builds them from the flat Cells format.

Controls That Cannot Be Written

Some controls are read-only and will be rejected by write_objects. Check the schema — if writeSupported is false, do not include that control. The import will return an error.

Current read-only controls include: Image (use IndustrialIcon or shapes instead), RadioButton, WPF Control, Blazor Control, ToggleButton, MapsESRI, MapsGMap, MapsOSM, FlowPanel.

When a write is rejected, the error message suggests using get_table_schema with the specific control name for alternatives.

IndustrialIcon

Use the IconName property with a human-readable name like "Home", "Valve", "Pump", "Tank", "Motor". Do not set FontFamily, FontSize, or Text — those are internal properties managed by the import pipeline.

Call get_table_schema('IndustrialIcon') to see the full list of available icon names.

LinkedValue — The Universal Data Binding

LinkedValue is the primary data-binding property for almost every control. It accepts tag paths in the format "Tag.FolderPath/TagName".

For controls that display text (Button, ToggleSwitch, NumericTextBox), set Text for static labels. LinkedValue binds the control's value to a tag.

Exception: BarChart data items use FieldNameValue instead of LinkedValue.

Dynamics

Dynamics add runtime behavior to any element. Each dynamic is a separate JSON object within the element's Dynamicsarray.

{
  "Type": "Ellipse",
  "Width": 40,
  "Height": 40,
  "Dynamics": [
    {
      "Type": "FillColorDynamic",
      "LinkedValue": "Tag.Process/Status",
      "ChangeColorItems": [
        { "Value": "0", "Color": "Gray" },
        { "Value": "1", "Color": "Green" }
      ]
    }
  ]
}

Key rules:

Collections (Pens, Columns, Data Items)

Controls with child collections (TrendChart pens, DataGrid columns, PieChart items) use wrapper arrays. The wrapper needs a Type that names the collection class:

{
  "Type": "TrendChart",
  "Pens": {
    "Type": "TrendPenList",
    "Items": [
      { "PenName": "Temperature", "TagName": "Tag.Process/Temp" }
    ]
  }
}

Collection types: TrendPenList, PieChartDataList, GridColumnList, ColumnDataList, TimelineColumnList.

Do not set Id on collection items — it is auto-managed by the engine.

Themes

To apply a theme color, use the "theme:ColorName" prefix on any color property:

{ "Fill": "theme:Accent1", "Foreground": "theme:Text" }

To clear a theme (revert to explicit color), set the color to any explicit value like "#FF0000" — the engine removes the theme binding automatically.

Shapes

All shapes require StrokeThickness (default is usually 1 but should be explicit). Common shapes: Ellipse, Rectangle, Line, Polyline, Path, Border.

Use Fill for interior color, Stroke for border/outline color.

Groups and Containers

Layouts and Navigation

The MainPage display (ID=0 in DisplaysList) is the default display every solution starts with. Configure it as the main process overview. The Startup layout (ID=0 in DisplaysLayouts) controls which displays appear on startup.

Use get_table_schema('DisplaysLayouts') for layout structure. Layouts define regions; displays fill those regions.

Verification After Creating

After calling write_objects, the success response is your primary confirmation. Do not enter a screenshot verification loop. At most, take one screenshot to confirm visual rendering, then deliver your report and let the user review.

If something looks wrong in a screenshot, mention it in the report — do not silently retry or investigate further.