...
title
...
:
...
"MQTT
...
Discovery Services — Connect, Browse, and Build Asset Navigation" tags: [mqtt,
...
discovery-services, tagprovider,
...
sparkplugb, asset-navigation,
...
dynamic-tags
...
, uns, iiot, datagrid, trendchart] description:
...
"Connect
...
FrameworX to
...
an
...
MQTT
...
broker
...
via
...
Discovery Services,
...
explore
...
its
...
namespace with the built-in MQTT browser, link data into the AssetTree, and build a dynamic asset-navigation UI with DataGrid, TrendChart, and AlarmViewer — all driven by Asset() bindings." version: "1.0" author: "Tatsoft"
...
This skill walks through a complete MQTT Discovery Services integration: connecting to a broker, exploring its topic tree, linking data into the UNS AssetTree, and
...
building a dynamic asset-navigation UI
...
| Warning | ||
|---|---|---|
| ||
This skill has open items that require resolution before publishing. See the Open Items section at the bottom of this page. |
...
| width | 50% |
|---|
This skill connects FrameworX to an external MQTT broker using a TagProvider — the dynamic, zero-local-tags approach. Instead of creating individual UnsTags and mapping them with Device Points, the TagProvider auto-discovers the broker's topic tree and extends the UNS namespace directly. The skill then builds a complete asset-navigation UI: a Layout with an AssetTree on the left, a header with commands, and a content area that reacts to the user's tree selection using Asset() bindings and Client.Context properties.
| Code Block | ||||
|---|---|---|---|---|
| ||||
MQTT Broker ??? TagProvider ??? UNS Namespace (dynamic)
?
AssetTree Folder
?
?????????????????????????????????
Header AssetTree MainPage
(Canvas) (Navigation) (DataGrid + Trend)
?
Client.Context.AssetPath
drives all content dynamically
|
where selecting a tree node changes all displayed data.
Prerequisite skill: Read skill-discovery-services first if you don't understand the difference between Device Module, DataLink, and Dynamic Tags.
Use when:
Do NOT use when:
open_solution or create_solution completed)Option A: Built-in broker for testing/demo
Navigate to Data Explorer → MQTT Tools and start the built-in services:
designer_action('navigate', 'DataExplorer.MQTTTools')
From MQTT Tools, start the built-in broker and simulator using the tab actions:
designer_action('builtin_broker', 'start')
designer_action('builtin_simulator', 'start')
This creates a local MQTT broker on localhost:1883 and a SparkplugB simulator publishing sample data (solar panels, cities, sensors).
Option B: External production broker
Ask the user for:
First, get the protocol schema to understand the PrimaryStation format:
get_table_schema('UnsTagProviders')
list_protocols('mqtt')
PrimaryStation format for MQTT — 14 semicolon-separated fields:
{Host};{Port};{ClientID};{Username};{Password};{CertFile};{KeyFile};{TLS};{CleanSession};{WillTopic};{QoS};{KeepAlive};{RetainPublish};{UseWebSocket};
Common configurations:
| Scenario | PrimaryStation |
|---|---|
| Local built-in broker | localhost;1883;FrameworX-001;;;;;None;False;;AtLeastOnce;10;False;False; |
| Remote broker | mqtt.factory.local;1883;FrameworX-001;;;;;None;False;;AtLeastOnce;10;False;False; |
| With authentication | broker.cloud.io;8883;MyClient;admin;password123;;;TLS 1.2;False;;AtLeastOnce;30;False;False; |
| WebSocket connection | broker.example.com;8083;MyClient;;;;;None;False;;AtLeastOnce;10;False;True; |
Write the connection:
{
"table_type": "UnsTagProviders",
"data": [{
"Name": "MQTT",
"Protocol": "MQTT",
"PrimaryStation": "localhost;1883;FrameworX-001;;;;;None;False;;AtLeastOnce;10;False;False;",
"Separators": "BranchSeparator=/;AttributeSeparator=/;",
"Description": "Production MQTT Broker"
}]
}
For SparkplugB brokers, use MQTTspB protocol instead:
{
"Name": "SparkplugBroker",
"Protocol": "MQTTspB",
"PrimaryStation": "localhost;1883;FrameworX-SpB;;;;;None;False;;AtLeastOnce;10;False;False;",
"Separators": "BranchSeparator=/;AttributeSeparator=/;",
"Description": "SparkplugB broker with richer metadata"
}
Naming decision: The Name becomes the root of the dynamic namespace — all discovered topics appear under Tag.<Name>.*. Choose a meaningful name: ProductionMQTT, FactoryBroker, EdgeGateway, etc.
Separators: BranchSeparator=/;AttributeSeparator=/; maps MQTT topic hierarchy to UNS path hierarchy. This is almost always correct for MQTT.
Create an AssetTree folder that links to the Discovery Services connection. This makes the data navigable in the AssetsTree UI control.
get_table_schema('UnsAssetTree')
{
"table_type": "UnsAssetTree",
"data": [{
"Name": "Production/MQTTData",
"TagProviderLink": "MQTT",
"InitialBranch": "spBv1.0/GroupID",
"Description": "MQTT production data"
}]
}
Key fields:
From this folder down, the tree is dynamic — no further configuration needed.
The standard asset-navigation pattern uses a Layout with three regions: Header (top), Menu (left side with AssetTree), and Content (main area).
get_table_schema('DisplaysLayouts')
list_elements('Layout')
Read the existing startup layout first to understand current structure:
get_objects('DisplaysLayouts', detail='full', names=['Startup'])
Write or modify the layout:
{
"table_type": "DisplaysLayouts",
"data": [{
"Name": "Startup",
"Description": "Asset navigation — tree on left, dynamic content on right",
"Members": [
{"Region": "Header", "Docking": "Top", "Page": "Display.Header"},
{"Region": "Menu", "Docking": "Left", "Page": "Display.AssetNav"},
{"Region": "Content", "Page": "Display.MainPage"}
]
}]
}
Note: Startup (layout ID 0) is the predefined startup layout — it loads on application launch. Read it first before modifying to preserve any existing configuration.
A simple Canvas display with the application title and optional navigation buttons:
get_table_schema('DisplaysList')
list_elements('TextBlock,IndustrialIcon')
{
"table_type": "DisplaysList",
"data": [{
"Type": "Display",
"Name": "Header",
"PanelType": "Canvas",
"DisplayMode": "Page",
"Width": 1200,
"Height": 60,
"Description": "Application header bar",
"Elements": [
{
"Type": "TextBlock",
"Name": "Title",
"Left": 20,
"Top": 15,
"Text": "MQTT Production Monitor",
"FontSize": 18,
"FontWeight": "Bold"
}
]
}]
}
Use themed colors from list_elements('ThemeColors') instead of hardcoded hex values.
A Canvas display containing the AssetsTree control. When users click nodes, the platform updates Client.Context properties automatically.
list_elements('AssetsTree')
{
"table_type": "DisplaysList",
"data": [{
"Type": "Display",
"Name": "AssetNav",
"PanelType": "Canvas",
"DisplayMode": "Page",
"Width": 300,
"Height": 700,
"Description": "Asset tree navigation panel",
"Elements": [
{
"Type": "AssetsTree",
"Name": "NavTree",
"Left": 0,
"Top": 0,
"Width": 300,
"Height": 700
}
]
}]
}
What happens when the user clicks a node:
| Property | Description | Example value |
|---|---|---|
Client.Context.AssetPath | Full path of selected node | MQTT/spBv1.0/GroupID/Barcelona/Panel1 |
Client.Context.AssetName | Display name of selected node | Panel1 |
Client.Context.AssetNodeName | Leaf node name | Panel1 |
Client.Context.SelectedTag | Full tag path if a tag is selected | tag path string |
These properties drive the content display reactively — all elements bound to them update automatically.
The MainPage uses Asset() bindings and Client.Context to react to the user's tree selection. One display serves the entire asset hierarchy.
list_elements('DataGrid,TrendChart,TextBlock,AlarmViewer')
Dashboard layout approach:
| Column | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
On this page:
|
Use this skill when:
Do NOT use this skill when:
Decision guide:
Scenario | Approach |
|---|---|
Dynamic data, auto-discovery wanted | This skill (TagProvider) |
Specific tags, strict validation (FDA) | Device Module |
Large topic tree, monitoring/IoT | This skill (TagProvider) |
Small fixed set of MQTT topics | Device Module may be simpler |
open_solution or create_solution completed)Category | Items |
|---|---|
Tools |
|
Tables |
|
Designer Actions |
|
Key Namespaces |
|
Before creating any configuration, you need to see what data the broker has. This step is interactive — it involves the user.
If using the built-in broker for testing:
| Code Block | ||
|---|---|---|
| ||
designer_action('start_mqtt_broker')
designer_action('start_mqtt_simulator')
|
This starts a local MQTT broker and a SparkplugB simulator that publishes sample data (solar panels, cities, sensors).
Navigate to MQTT Tools:
| Code Block | ||
|---|---|---|
| ||
designer_action('navigate', 'DataExplorer.MQTTTools')
|
Connect to the broker:
| Code Block | ||
|---|---|---|
| ||
designer_action('connect_mqtt_explorer', '<PrimaryStation string>')
|
The PrimaryStation format for MQTT is a semicolon-delimited string with 14 fields:
| Code Block | ||||
|---|---|---|---|---|
| ||||
{Host};{Port};{ClientID};{Username};{Password};{CertFile};{KeyFile};{TLS};{CleanSession};{WillTopic};{QoS};{KeepAlive};{RetainPublish};{UseWebSocket};
|
Common examples:
Scenario | PrimaryStation |
|---|---|
Local built-in broker |
|
Remote broker |
|
With authentication |
|
Ask the user to explore and select:
The MQTT browser is now connected. Please navigate the tree to explore the available data. When you've identified the nodes you want to integrate into the Unified Namespace, select them and let me know — or I can read your current selection.
Read the user's selection:
| Code Block | ||
|---|---|---|
| ||
get_designer_state()
|
This returns the current MQTT Tools state including which nodes the user has selected (or browsed to). Use this information to determine the initial tree nodes for the TagProvider link.
| Tip |
|---|
AI-driven alternative: If the user says "just connect everything" or "pick reasonable defaults," the AI can read the top-level structure from |
Fetch the schema and protocol details:
| Code Block | ||
|---|---|---|
| ||
get_table_schema('UnsTagProviders')
list_protocols('mqtt')
|
Write the TagProvider object:
| Code Block | ||||
|---|---|---|---|---|
| ||||
{
"table_type": "UnsTagProviders",
"data": [
{
"Name": "MQTT",
"Protocol": "MQTT",
"PrimaryStation": "localhost;1883;FrameworX-001;;;;;None;False;;AtLeastOnce;10;False;False;",
"Separators": "BranchSeparator=/;AttributeSeparator=/;",
"Description": "Production MQTT Broker"
}
]
}
|
Key decisions:
Tag.MQTT.*. Choose a meaningful name (e.g., ProductionMQTT, FactoryBroker).BranchSeparator=/;AttributeSeparator=/; maps MQTT topic hierarchy (plant/line1/temp) to UNS path hierarchy. This is almost always the correct default for MQTT.After creation, the TagProvider immediately starts discovering topics when runtime starts. Topics appear as dynamic tags under Tag.<ProviderName>.<topic.path>.
The AssetTree folder is what makes the TagProvider data visible and navigable in the UI. A Linked Folder connects an AssetTree node to a TagProvider and specifies which initial nodes from the provider's namespace to display.
| Warning | ||
|---|---|---|
| ||
|
| Code Block | ||||
|---|---|---|---|---|
| ||||
{
"table_type": "UnsAssetTree",
"data": [
{
"Name": "Production",
"TagProviderLink": "MQTT",
"InitialPath": "spBv1.0/GroupID",
"Description": "MQTT production data from SparkplugB broker"
}
]
}
|
Key decisions:
From this point down, the AssetTree is dynamic — the platform auto-expands the tree based on whatever the MQTT broker publishes. No further tag creation needed.
The standard pattern for asset-navigation applications is a Layout with three regions:
| Code Block | ||
|---|---|---|
| ||
get_table_schema('DisplaysLayouts')
list_elements('Layout')
|
...
{
"table_type": "...
DisplaysList",
"data": [...
{
...
...
"...
Type": "...
Display",
...
"...
Name": "...
MainPage",
...
"...
PanelType": "...
Dashboard",
...
...
"...
Columns": ...
2,
"Rows": 3,
"Description": "...
Dynamic ...
content ...
— ...
reacts ...
to ...
AssetTree ...
selection",
...
...
...
...
"Elements": [
...
{
...
The Layout Startup (ID 0) is the predefined startup layout — modifying it changes what loads on application launch.
Regions:
A simple Canvas display with the application title:
| Code Block | ||
|---|---|---|
| ||
get_table_schema('DisplaysList')
list_elements('TextBlock,Button')
|
...
| language | json |
|---|---|
| title | write_objects — Header display |
...
"Type": "TextBlock",
"Name": "AssetTitle",
"Row": 0,
"Column": 0,
"ColumnSpan": 2,
"Text": "",
"LinkedValue": "@Client.Context.AssetName",
"FontSize": 20,
"FontWeight": "Bold"
},
{
"...
Type": "...
DataGrid",
"...
Name": "...
AssetData",
"...
Row": ...
1,
"...
Column": ...
0,
"...
ColumnSpan": ...
2,
"...
Height": ...
300
},
{
...
"Type": "...
TrendChart",
...
"Name": "...
AssetTrend",
...
"...
Row": ...
2,
...
"...
Column": ...
0,
...
"...
Height": ...
250,
...
"...
Duration": ...
"00:05:00"
},
...
{
...
"Type": "AlarmViewer",
...
"Name": "AssetAlarms",
"Row": 2,
"Column": 1,
"Height": 250
}
]
}]
...
}
The key pattern — DataGrid bound to context:
A Canvas display containing the AssetTree control that drives navigation:
...
| language | text |
|---|
The DataGrid displays all children (sub-tags and attributes) of whatever the user selected in the AssetTree. Check
list_elements('
...
DataGrid') for the exact property names to bind the DataGrid to Client.Context.AssetPath. The DataGrid automatically shows all tags below the selected path.
Asset() in CodeBehind (display events):
Public Sub DisplayOpening()
' Optionally set a default branch to expand on load
@Client.Context.TreeInitialBranch = "MQTT/spBv1.0/GroupID"
End Sub
designer_action('start_runtime')
After starting:
Client.Context propertiesTake a screenshot to verify:
get_screenshot(target='runtime')
| Aspect | MQTT (standard) | MQTTspB (SparkplugB) |
|---|---|---|
| Protocol name in FrameworX | MQTT | MQTTspB |
| Topic structure | Flat or custom hierarchy | `spBv1.0/GroupID/NBIRTH |
| Metadata | Topic names only | Rich: data types, birth/death certificates, metrics |
| Discovery quality | Basic (topic tree) | Full (typed variables with descriptions) |
| When to use | Generic MQTT brokers, custom topic schemes | Industrial IoT with SparkplugB-compliant edge nodes |
If the broker publishes SparkplugB messages, always prefer MQTTspB — you get typed variables and richer metadata.
Use FrameworX's built-in MQTT broker as a central data hub. External devices publish to it, and the Discovery Services connection reads from it — all on localhost.
{
"Name": "LocalHub",
"Protocol": "MQTT",
"PrimaryStation": "localhost;1883;FrameworX-Hub;;;;;None;False;;AtLeastOnce;10;False;False;",
"Separators": "BranchSeparator=/;AttributeSeparator=/;",
"Description": "Built-in broker as central data hub"
}
Start the built-in broker from Runtime Startup or via designer_action('builtin_broker', 'start').
Create multiple Discovery Services connections, each with a different Name:
{
"table_type": "UnsTagProviders",
"data": [
{"Name": "FactoryA", "Protocol": "MQTT", "PrimaryStation": "mqtt-a.factory.com;1883;..."},
{"Name": "FactoryB", "Protocol": "MQTT", "PrimaryStation": "mqtt-b.factory.com;1883;..."}
]
}
Create separate AssetTree folders for each. The same Layout and MainPage pattern works — Client.Context handles any source.
Use Discovery Services for browsing the full MQTT namespace, but create local tags with DataLink for the few points that need alarms and historian:
{
"table_type": "UnsTags",
"data": [
{
"Name": "Critical/ReactorTemp",
"DataType": "Float",
"DataLink": "/MQTT/plant/reactor/temperature",
"Min": 0,
"Max": 500,
"Description": "Reactor temperature — alarmed and historized"
}
]
}
Then configure alarms and historian on Critical/ReactorTemp as a normal tag. The DataLink binds it to the MQTT path automatically.
If connecting to a historian-capable Discovery Services connection (Canary, InfluxDB, etc.), TrendCharts using Asset() syntax automatically query historical data for the selected time range. Configure the Discovery Services connection with IsHistorian: true.
| Mistake | Why it happens | How to avoid |
|---|---|---|
| Creating local UnsTags for MQTT data | Habit from Device Module workflow | Dynamic tags don't need local tags. Only use DataLink for points needing alarms/historian. |
| Wrong PrimaryStation format | Forgetting semicolons or field order | Always call list_protocols('mqtt') first. The format is 14 semicolon-separated fields. |
| Topics with special chars not accessible | MQTT topics like spBv1.0/Group contain dots | Use quoted Asset() syntax: Asset("/ProviderName/spBv1.0/Group/Node") |
| AssetTree empty after runtime start | Connection not established or linked folder misconfigured | Check Services Monitor for connection status. Verify TagProviderLink matches the connection Name exactly. |
| Using MQTT protocol for SparkplugB broker | SparkplugB has specific message format | Use MQTTspB protocol for SparkplugB brokers — you get typed variables and metadata. |
| DataGrid shows nothing | Not bound to Client.Context properly | Check list_elements('DataGrid') for the correct binding properties. |
| Trying to set alarms on dynamic tags | No local tag exists for dynamic data | Create a local tag with DataLink for that specific path, then configure alarms on it. |
| Task | Tool call |
|---|---|
| Get MQTT protocol format | list_protocols('mqtt') |
| Get SparkplugB format | list_protocols('mqtt sparkplug') |
| Create Discovery Services connection | write_objects('UnsTagProviders', data=[...]) |
| Create AssetTree linked folder | write_objects('UnsAssetTree', data=[...]) |
| Browse AssetsTree element schema | list_elements('AssetsTree') |
| Browse DataGrid schema | list_elements('DataGrid') |
| Navigate to MQTT Tools | designer_action('navigate', 'DataExplorer.MQTTTools') |
| Start built-in broker | designer_action('builtin_broker', 'start') |
| Start built-in simulator | designer_action('builtin_simulator', 'start') |
| Check runtime state | get_state(target='runtime') |
| Navigate to Services Monitor | designer_action('navigate', 'ServicesMonitor') |
| Code Block | ||||
|---|---|---|---|---|
| ||||
{
"table_type": "DisplaysList",
"data": [
{
"Name": "AssetTreeMenu",
"PanelType": "Canvas",
"Width": 300,
"Height": 700,
"Description": "Asset tree navigation panel",
"Objects": [
{
"Type": "AssetsTree",
"Name": "NavTree",
"Left": 0,
"Top": 0,
"Width": 300,
"Height": 700
}
]
}
]
}
|
When the user clicks a node in the AssetsTree, the platform automatically updates:
Property | Example Value |
|---|---|
|
|
|
|
| leaf node name |
| full tag path of the selected tag |
These properties drive the content display reactively.
The MainPage uses Asset() bindings to react to the user's tree selection. All content is driven by Client.Context.AssetPath.
| Code Block | ||
|---|---|---|
| ||
list_elements('DataGrid,TrendChart,TextBlock,AlarmViewer')
|
| Code Block | ||||
|---|---|---|---|---|
| ||||
{
"table_type": "DisplaysList",
"data": [
{
"Name": "MainPage",
"PanelType": "Dashboard",
"Columns": 2,
"Rows": 3,
"Description": "Dynamic content — reacts to AssetTree selection",
"Objects": [
{
"Type": "TextBlock",
"Name": "AssetTitle",
"Row": 0,
"Column": 0,
"ColSpan": 2,
"Text": "",
"LinkedValue": "@Client.Context.AssetName",
"FontSize": 20,
"FontWeight": "Bold"
},
{
"Type": "DataGrid",
"Name": "AssetData",
"Row": 1,
"Column": 0,
"ColSpan": 2,
"LinkedValue": "@Tag",
"LinkedContext": "@Client.Context.AssetPath",
"Width": 800,
"Height": 300
},
{
"Type": "TrendChart",
"Name": "AssetTrend",
"Row": 2,
"Column": 0,
"Width": 500,
"Height": 250,
"Duration": "00:05:00"
},
{
"Type": "AlarmViewer",
"Name": "AssetAlarms",
"Row": 2,
"Column": 1,
"Width": 400,
"Height": 250
}
]
}
]
}
|
Key pattern — the DataGrid LinkedValue:
When you set the DataGrid's LinkedValue to @Tag and provide a LinkedContext pointing to @Client.Context.AssetPath, the DataGrid automatically shows all children (sub-tags and attributes) of whatever the user selected in the AssetTree. This is the heart of the dynamic pattern — one display serves the entire asset hierarchy.
The Asset() syntax for code and expressions:
| Code Block | ||||
|---|---|---|---|---|
| ||||
// In scripts or CodeBehind:
double value = Asset(@Client.Context.AssetPath + "/Temperature");
// In display element LinkedValue fields:
// Static: @Tag.MQTT.plant.line1.temperature
// Dynamic: Asset(Client.Context.AssetPath + "/Temperature")
|
| Code Block | ||
|---|---|---|
| ||
designer_action('start_runtime')
|
After starting:
Client.Context propertiesTake a single screenshot to verify:
| Code Block | ||
|---|---|---|
| ||
get_screenshot('runtime')
|
| Code Block | ||||
|---|---|---|---|---|
| ||||
void Opening()
{
// Optionally set a default branch to expand
@Client.Context.TreeInitialBranch = "MQTT/spBv1.0/GroupID";
}
|
| Code Block | ||||
|---|---|---|---|---|
| ||||
public static double GetTemperature(string assetPath)
{
return TK.ToDouble(Asset(assetPath + "/Temperature"));
}
|
| Code Block | ||||
|---|---|---|---|---|
| ||||
// WPF (.NET Framework 4.8) — automatic type resolution
var value = Asset("/MQTT/plant/line1/temperature");
// HTML5/Portable (NetStandard 2.0) — explicit conversion required
int intValue = TK.ToInt(Asset("/MQTT/plant/line1/count"));
double dblValue = TK.ToDouble(Asset("/MQTT/plant/line1/temperature"));
string strValue = TK.ToString(Asset("/MQTT/plant/line1/status"));
bool boolValue = TK.ToDigital(Asset("/MQTT/plant/line1/running"));
|
get_objects('UnsTagProviders') — verify the TagProvider was created with correct PrimaryStationdesigner_action('navigate', 'Uns.AssetTree') → get_designer_state() — confirm the linked folder appears with the TagProvider referencedesigner_action('start_runtime') → get_runtime_state() — confirm the TagProvider connection is activeget_screenshot('runtime') — verify the AssetTree populates and the Layout renders correctlyMistake | Why It Happens | How to Avoid |
|---|---|---|
Creating local UnsTags for MQTT data | Habit from Device Module workflow | TagProviders don't need local tags — that's the whole point. Only create UnsTags if you need alarms or historian on specific points. |
Wrong PrimaryStation format | Forgetting semicolon delimiters or field order | Always call |
DataGrid shows nothing | LinkedValue not correctly bound to context | Use |
Topics with special chars not accessible | MQTT topics like | Use quoted syntax: |
AssetTree empty after runtime start | TagProvider not connected or linked folder not configured | Verify TagProvider connection in ServicesMonitor, then check that UnsAssetTree folder has correct TagProviderLink |
Trying to set alarms on dynamic tags | Dynamic tags don't exist as local UnsTags | Create a local tag and use a Script expression to copy the Asset() value into it. Or switch those specific points to Device Module. |
Layout regions not rendering | Wrong display names in Layout configuration | Display names in Header, Menu, Content fields must match exactly (case-insensitive) |
Variation A: Mixed Approach (TagProvider + Device Module)
Variation B: SparkplugB-Specific
MQTTspB protocol instead of MQTT in the TagProviderSeparators to match SparkplugB topic structureVariation C: Multiple Brokers
Client.Context handles any sourceVariation D: Add Historian to Dynamic Tags
Asset() syntax to automatically query historical data from historian TagProvidersskill-device-mqtt — Explicit MQTT integration using Device Module (Channel → Node → Points)skill-asset-navigation — Generic asset-navigation UI patterns (not MQTT-specific)skill-sparkplugb-collector — SparkplugB Collector setup with Device Module| Status | ||||
|---|---|---|---|---|
|
# | Item | Status | ||||||
|---|---|---|---|---|---|---|---|---|
1 |
|
| ||||||
2 |
|
| ||||||
3 |
|
| ||||||
4 | DataGrid |
| ||||||
5 | TrendChart Asset() binding: How does a TrendChart dynamically bind its pens to the currently selected asset's children? |
| ||||||
6 | Layout field names: Verify exact field names for |
| ||||||
7 | Template integration: If solution templates with built-in Header+AssetTree+Content exist, reference them as a shortcut. |
|
...