1. Problem Overview
Engineers building reusable WinCC Comfort, WinCC Advanced, or WinCC Professional face panels frequently need to bind the OutputValue property of an I/O field to a tag whose name is constructed at runtime. The classic case is a base-prefix read from an internal text tag (for example IntPartName = "test01") that must be concatenated with a fixed suffix ("value1", "start", "_sp", and so on) to produce a fully qualified tag name like test01value1.
The naïve approach, typing "IntPartName" & "value1" into the dynamic property dialog, does not return a tag reference—it returns a string literal. WinCC treats the expression as a string, not as an indirect tag path. This article walks through the supported ways to perform indirect tag binding in TIA Portal WinCC, why some of them silently fail inside embedded picture windows, and which combinations survive the runtime compiler, the picture-window tag prefix, and the I/O field property checker.
2. How the WinCC Tag System Resolves a Property Reference
Every dynamic property in WinCC ultimately resolves to a fully qualified tag path. That path is built from three components:
- Tag prefix (TagPrefix) – the namespace declared on the containing picture window.
- Tag name – the local name configured in the HMI tag table.
- Array / element suffix – applied only for array tags or UDT element access.
The fully qualified name is therefore <TagPrefix>.<TagName>. When the prefix is empty, the qualified name is just the tag name. The runtime stores both parts in the picture's tag-prefix stack and merges them when evaluating an expression such as {tag_name} on a dynamic property.
Three prefix tokens are recognized by the runtime parser:
| Token | Meaning | Behavior |
|---|---|---|
(none) |
Use the picture-window prefix | The prefix is prepended automatically; name is resolved within the prefix namespace. |
@NOTP:: |
No Tag Prefix | The prefix is stripped and the tag is resolved in the global namespace. |
@DP:: |
Default Prefix | Resolves in the configured default prefix (HMI device > Settings > Default tag prefix). |
The @NOTP:: token is the most useful escape hatch for engineers trying to keep a tag lookup outside the active picture-window prefix—see Siemens Online Support entry ID 109757967 on tag-prefix syntax for the canonical reference.
3. The Picture Window TagPrefix Problem
Picture windows (faceplate-type objects) carry a TagPrefix string property. Every dynamic tag reference inside the picture window is rewritten at compile time to TagPrefix.TagName. The runtime then looks up TagPrefix.TagName in the tag table.
The result: an I/O field configured to display Temp inside a picture window that has TagPrefix = "OwnPrefix" actually shows OwnPrefix.Temp. If you set the picture window's prefix to test01, the I/O field will resolve test01.Temp. The mechanism is documented in the TIA Portal Help under "Picture window" → "Tag prefix".
Two failure modes appear most often:
-
Static
@NOTP::prefix works, dynamic@NOTP::<runtime_value>does not. A static expression typed into the property dialog compiles correctly; an expression that is supposed to be recomputed at runtime (via SetLink, SetPropertyByTag, or an indirect connection) silently fails to find the tag because the runtime parser only honors the literal@NOTP::token at the start of a path. If a variable is concatenated after the token, the parser no longer recognizes it as a prefix escape. -
Embedded picture windows inherit the parent's prefix. A picture window inside another picture window does not start with an empty prefix stack; it inherits the outer window's effective prefix. Even an
@NOTP::reference that worked at the first level can be lost when re-embedded.
4. Why the Direct Concatenation Fails
The dynamic property dialog for OutputValue accepts either:
- A direct tag (a green icon in the dialog), or
- An expression that returns a value of the property's data type.
Entering "IntPartName" & "value1" returns a string—never a tag reference. The runtime cannot dereference a string into a tag read. The compiler will compile the expression, but the I/O field will display the literal text IntPartNamevalue1 in display mode and refuse to write back in input mode because there is no underlying tag.
To bind to a tag whose name is only known at runtime you must use one of three mechanisms:
| Mechanism | Where to use it | Resolves dynamically? |
|---|---|---|
Direct tag with @NOTP:: literal |
Static dynamic property | No (compile-time) |
C-script with GetTagChar / SetTagChar
|
Event (e.g. Input field changed) | Yes (runtime) |
| SetLink / SetPropertyByTag + indirect connection | Script with access to I/O field object | Yes (runtime) |
| Faceplate with typed interface | Picture-window property | Indirect via container |
5. Method A — C-Script Reading the Tag by Computed Name
The most reliable approach is a C-script that builds the tag name string and then uses the WinCC tag API to read or write it. The script runs on a screen event (e.g. Input field > Change value or a cyclic trigger).
5.1 Example: read computed tag into a temporary internal tag
// C-script (ANSI-C) on event "Input field 'IOField_1' → Change value"
{
char szPrefix[64];
char szFullName[128];
char szValue[64];
// Read the runtime prefix stored in an internal text tag
GetTagChar("IntPartName", szPrefix, sizeof(szPrefix));
// Build "<prefix>value1"
sprintf(szFullName, "%s%s", szPrefix, "value1");
// Read the value of the computed tag
GetTagChar(szFullName, szValue, sizeof(szValue));
// Display it in the I/O field (write to the I/O field's process tag)
SetTagChar("Display_Result", szValue);
}
Notes on this script:
-
GetTagCharis the synchronous read; for non-blocking reads from the PLC useGetTagCharStateor the asynchronous variant. - Always pass a buffer size as the third argument. The third parameter to
GetTagCharis the maximum number of characters to copy (including the terminator), and the function will return-1if the buffer is too small. -
szFullNamemust be the bare tag name without a prefix. If your tag lives in a picture window with a tag prefix, either include the literal prefix ("OwnPrefix.test01value1") or strip the prefix from the runtime by configuring the internal text tag to contain the full name. - For numeric tags use
GetTagFloat,GetTagDouble,GetTagWord,GetTagDWord,GetTagRaw,GetTagSByte, orGetTagBitWaitdepending on the underlying PLC data type.
5.2 Example: write back to the computed tag
// C-script on event "Input field 'IOField_1' → Input finished"
{
char szPrefix[64];
char szFullName[128];
char szInput[64];
GetTagChar("IntPartName", szPrefix, sizeof(szPrefix));
sprintf(szFullName, "%s%s", szPrefix, "value1");
// Read the value the user typed
GetTagChar("IOField_1", szInput, sizeof(szInput));
// Write it to the dynamically named tag
SetTagChar(szFullName, szInput);
}
6. Method B — C-Script Using SetLink on a Property
If the I/O field is configured with a direct tag (e.g. Display_Result) but you want to swap the link at runtime, the runtime offers SetLink on the I/O field object. The script must run on the I/O field's object, not a generic screen event, because it needs access to the I/O field's OutputValue property handle.
// C-script on event "Button 'btn_Switch' → Click"
{
char szPrefix[64];
char szFullName[128];
GetTagChar("IntPartName", szPrefix, sizeof(szPrefix));
sprintf(szFullName, "%s%s", szPrefix, "value1");
// Build a dynamic link on the I/O field's OutputValue property.
// lpszPictureName and lpszObjectName identify the I/O field on the current screen.
SetLink(lpszPictureName, lpszObjectName, "OutputValue", szFullName);
}
On WinCC Professional the equivalent API is exposed through the runtime object's Properties collection; on WinCC Advanced the function is SetLink in the C-script API as shown above. The exact function signature is documented in the TIA Portal Help under "ANSI-C function descriptions > Tag > SetLink".
7. Method C — Dynamic Property via SetPropertyByTag
SetPropertyByTag is the form-friendly variant: it takes the picture name, object name, property name, and a tag path string. It performs the same job as SetLink but is callable from a global script and accepts a fully qualified name including the optional @NOTP:: token.
// C-script on a scheduled task or screen event
{
char szTagName[128];
sprintf(szTagName, "@NOTP::%svalue1", "test01");
SetPropertyByTag(lpszPictureName, "IOField_1", "OutputValue", szTagName);
}
The @NOTP:: token here is a literal. The runtime recognises the literal at the start of the string and bypasses the active picture-window tag prefix. Once any character appears before the token, however, the parser will not recognise it.
8. Method D — Expression Without a Script (Limitations)
WinCC expressions (the orange "fx" icon) can return strings, but the dynamic property dialog for I/O field OutputValue expects either a tag link or an expression that returns the value of the property's data type—not a tag name. Expressions cannot dereference a string into a tag. The dialog will not present a "Use as indirect tag name" option.
There is one exception: an expression that returns the value of another tag directly, e.g.
{Tag1}
or
{Tag1} + 1
These are evaluated at runtime against the current value of Tag1. They are not, however, indirect: the tag name Tag1 must be a literal in the expression. You cannot write {Concat("Tag", Index)} in the WinCC expression editor; the editor does not support a tag() function of the kind that exists in Ignition's expression language (compare with the Ignition community discussion on using a variable with a custom property in an expression—the conceptual gap with WinCC is exactly that WinCC expressions are not tag-returning).
9. The @NOTP:: Escape Token In Depth
The @NOTP:: token is documented in the WinCC Comfort/Advanced manual under "Tags and references > Tag prefix". It must appear at the very beginning of the tag path, with no whitespace, and must be followed by two colons. The runtime parser scans the first 7 characters of every dynamic tag path; if they match @NOTP::, the picture-window tag prefix is bypassed for that lookup.
Common failure patterns:
-
{ @NOTP::Temp }— leading whitespace. The parser will not strip whitespace before the token, so this resolves to the literal string "@NOTP::Temp" or fails entirely depending on the property type. -
{IntPartName & "@NOTP::Temp"}— token appears after a runtime value. The parser seestest01@NOTP::Tempand tries to find a tag with that exact name. The token is not recognised as a token because it is no longer at position 0. -
{"@NOTP::" & IntPartName & "value1"}— runtime concatenation that should produce a valid@NOTP::test01value1string at runtime. The string is correct, but the dynamic property evaluator still does not dereference a string into a tag. The result is the literal text of the concatenation.
Therefore @NOTP:: is a compile-time token, not a runtime string. It cannot be built from pieces at runtime and used as a path prefix. If you need to escape the picture-window prefix in a runtime-resolved tag, you must use a C-script API call (SetLink, SetPropertyByTag) that accepts a fully qualified path with the token at position 0.
10. Embedded Picture Windows — The Real Failure Mode
The original symptom in the source material was a setup where an outer picture window carries a tag prefix and an inner picture window sits inside it. Even when the engineer writes @NOTP::OwnPrefix.Temp as the default OutputValue, the value displays correctly at startup. But once the engineer tries to swap the link programmatically (via SetLink or an indirect connection), the inner picture window still picks up the outer picture window's prefix.
The reason: the inner picture window evaluates any tag path against its own effective prefix, which is computed as the concatenation of the outer prefix, the inner window's own prefix, and any tag prefix configured on the picture itself. Setting @NOTP:: at compile time on the inner window's I/O field works because the runtime sees the literal token before the prefix-stripping logic runs. Setting it at runtime through SetLink with a string that begins with @NOTP:: also works—but only if the parser can scan position 0 of the supplied string.
Mitigations in order of preference:
- Push the dynamic binding down to a faceplate with a typed interface. The faceplate's interface tags are statically bound; the parent constructs the faceplate instance and assigns the data via the interface, which avoids runtime string-to-tag resolution.
- Use a C-script that calls
SetLinkon the inner picture window's I/O field, passing the picture name and object name explicitly. Do not pass a string that was constructed with leading whitespace or a variable in front of the@NOTP::token. - Avoid nested picture windows where the outer one already has a tag prefix. Use a single-level picture window per logical faceplate, and select the prefix at instantiation time on the parent screen.
11. Faceplate / Typed Interface Alternative
The recommended modern pattern in TIA Portal V16 and later is to convert the picture window to a faceplate with a typed PLC data interface. A faceplate exposes its internal tags as interface tags (e.g. Temp, Setpoint, Mode). The instance in the parent screen binds the interface tags to specific tag names. The faceplate's internal expressions then refer to the interface tags by their local name, and the binding is done at the parent — never with a string that needs to be parsed at runtime.
The advantage is that the binding is statically verifiable: the compiler knows which tag each interface tag will resolve to. The downside is that the same faceplate cannot be used in contexts where the binding is truly dynamic, e.g. selecting the binding from a drop-down at runtime. For that case, the C-script approach in §5 is the supported path.
12. Performance Considerations
| Approach | Trigger frequency | Runtime cost | Recommended use |
|---|---|---|---|
| Cyclic C-script polling a computed tag | Every cycle (default 1 s) | Low per call, but adds permanent load | Slow-changing display values, supervisory pages |
| Event-driven C-script on Change value | Per keystroke | Moderate; can stall the GUI on large tag trees | Validation only — keep work minimal |
| SetLink / SetPropertyByTag in scheduled task | Once per task (default 1 s) | Higher; triggers property re-evaluation | Re-binding after a navigation event |
| Faceplate typed interface | Compile-time binding | Negligible | Default — most re-usable panels |
Direct tag with @NOTP:: literal |
Compile-time binding | Negligible | Tags that never change namespace |
Rule of thumb: if the binding is known at compile time, never use a script. If the binding is known when the screen is opened, use SetLink once on the screen's Loaded event, not cyclically. If the binding can change while the screen is visible, use an event-driven script with the smallest possible trigger surface.
13. Step-by-Step Procedure — Event-Driven Dynamic Tag Binding
- Add an internal text tag
IntPartName(length 32) in the HMI tag table. - Add the actual PLC tags that the prefix should resolve, e.g.
test01value1,test01value2,test01start— all defined in the HMI tag table and pointing to the appropriate PLC addresses. - Insert an I/O field on the screen. Leave its OutputValue bound to a placeholder internal tag such as
Display_Result(string, length 32). This avoids the "no tag assigned" warning during design. - Open the I/O field's Events tab. Add a C-script on the Input finished event.
- Implement the script from §5.2. The script reads
IntPartName, appends the fixed suffix, and usesGetTagChar/SetTagCharto exchange the value with the dynamically addressed tag. - Compile the project. Resolve any "tag not found" warnings — they almost always indicate a missing HMI tag or a wrong prefix.
- Download to the panel (WinCC RT Advanced) or the runtime PC (WinCC RT Professional).
- Verify in the runtime: type a value into the I/O field, change the value of
IntPartNamein the tag simulator, and confirm that the read/write goes to the correct physical tag.
14. Step-by-Step Procedure — Runtime Re-Binding with SetLink
- Insert the I/O field and bind its OutputValue to any valid placeholder tag (so the project compiles).
- Insert a button that triggers the re-binding script. The script runs on Click.
- Write the C-script from §6. The script must be a C-script on the button object, not a global script, so that
lpszPictureNameandlpszObjectNameresolve to the button's picture. - Inside the script, build the new tag name string and pass it to
SetLink. If you want to bypass the active tag prefix, prefix the name with the literal@NOTP::at position 0. - Compile and download. Click the button at runtime and confirm the I/O field re-displays the new tag's value.
15. Troubleshooting Matrix
| Symptom | Likely cause | Diagnostic | Fix |
|---|---|---|---|
| I/O field shows literal text "test01value1" | Expression returned a string instead of dereferencing a tag | Right-click property > "Used as tag name" | Convert to C-script and use GetTagChar |
| Script reports "Tag not found" at runtime | Tag name does not exist in the HMI tag table or has a typo | Check HMI tag table; search for the exact name | Create the tag, or correct the concatenation |
| Tag found in tag table, but value stays at zero | PLC connection not active, or tag's acquisition cycle disabled | Check "Connection" in tag properties; toggle "Update continuously" | Enable cyclic acquisition; check PLC reachability |
| Compile warning "Tag prefix not found" | Picture window prefix referenced before the picture is loaded | Inspect the order of picture loading | Use a literal tag prefix or @NOTP:: |
| SetLink succeeds but I/O field still shows old value | Picture window has a competing tag prefix; the new link is silently rewritten | Print lpszPictureName at runtime; check active prefix | Move the I/O field out of the picture window or strip the prefix in the script |
| Performance drops after enabling C-script on Change value | Script fires per keystroke; tag polling is heavy | Measure task cycle with ProDiag / Trace | Move to Input finished, or to a scheduled task |
| @NOTP:: works on first screen, fails on second | Second screen is in a picture window with its own prefix | Compare active tag-prefix stack between screens | Use a faceplate interface or SetLink with @NOTP:: literal |
16. Edge Cases and Field-Proven Caveats
-
Array tag access:
GetTagCharwith a name liketest01array[3]works in WinCC RT Advanced; in WinCC flexible 2008 the syntax wastest01array,3with a comma. Check the runtime target's documentation. -
UDT access: Indirect access to a UDT element (e.g.
test01udt.field1) requires the full path including the dot. The picture-window tag prefix is prepended automatically; the dot syntax inside the path is preserved. -
Empty prefix handling: If the internal text tag
IntPartNameis empty, the script will attempt to read a tag namedvalue1. Add a guard:if (strlen(szPrefix) == 0) return;. - Case sensitivity: WinCC HMI tag names are case-insensitive on lookup but case-sensitive on display. Always capitalise consistently to avoid diagnostic confusion.
-
Length limits: The fully qualified tag name (prefix + dot + local name) is limited to 128 characters. Validate with
strlen(szFullName) < 128before calling the API. - Multilingual deployments: If you plan to translate tag names, the translation table cannot rename HMI tags — it only renames display texts. Plan tag names once, in English, and never translate them.
-
WinCC flexible SP3 differences:
SetLinkexists in WinCC flexible with a slightly different signature. The picture-name argument is required and must be passed; some V13-era dialogs required the field to be a "symbolic I/O field" for SetLink to work.
17. Comparison — Script vs Faceplate
| Aspect | C-script with computed tag name | Faceplate with typed interface |
|---|---|---|
| Binding time | Runtime | Compile / instantiation |
| Type checking | None — runtime error if types mismatch | Strong — interface enforces type |
| Performance | Adds event handling overhead | Negligible |
| Reusability across screens | Yes (script is portable) | Yes (faceplate is portable) |
| Suitability for dynamic re-binding | High — fully dynamic | Low — fixed at instance creation |
| Versioning / library control | Requires script library discipline | Native WinCC library support |
18. Best Practices Summary
- Default to faceplates with typed interfaces for any panel that has more than two screen instances.
- Reserve C-script dynamic tag binding for true runtime variability — operator-selectable tag list, recipe-driven naming, etc.
- Always guard against an empty prefix in the C-script.
- Use
Input finishedfor write-back, notChange value, unless validation requires the latter. - Do not concatenate
@NOTP::with a runtime variable; pass it as a literal at position 0 of the string supplied toSetLink. - Keep computed tag names in a single helper C-function (or in a global script function) so all dynamic-binding code is consistent and easy to audit.
- Wrap every
GetTagCharcall with a length check and a buffer-size check. - Document the full qualified tag-name format (prefix, separator, suffix) in the project documentation; future engineers will need it.
Can I concatenate a runtime string with a tag suffix directly in the I/O field's OutputValue property dialog?
No. The dynamic property dialog for OutputValue accepts a tag reference or an expression that returns the value of the property's data type, not a string-to-tag dereference. A concatenation like "IntPartName" & "value1" returns the literal text IntPartNamevalue1, not a tag read. Use a C-script with GetTagChar or SetLink to resolve the computed name at runtime.
What does the @NOTP:: prefix do, and can I build it at runtime?
@NOTP:: is a literal token that the WinCC runtime parser recognises at position 0 of a tag path. It strips the active picture-window tag prefix for that single lookup. The token is recognised at compile time and at the moment a SetLink call is processed, but it cannot be created by concatenating a variable in front of it — the parser scans position 0 only and will not find a token that appears after a runtime value.
Why does my dynamic binding work on the main screen but fail in a nested picture window?
A nested picture window inherits the outer picture window's effective tag prefix. Any tag path you set on the inner I/O field is rewritten against the inner window's prefix stack. The literal @NOTP:: escape works only when the parser can see it at position 0 of the path string. If the binding is done through a string concatenation that places other text before the token, the parser will not recognise it. Use a faceplate with a typed interface, or call SetLink with the picture name and object name explicitly, to make the binding survive nesting.
Should I trigger the C-script on Change value or Input finished?
Use Input finished for read/write of a dynamically named tag — it fires once when the operator commits the value (Enter, tab, focus loss). Use Change value only when the script must validate or veto the input on every keystroke. A Change value script that calls GetTagChar on every keypress can stall the GUI on a panel with many tags or a slow connection.
Is there an expression-language equivalent of Ignition's tag() function in WinCC?
No. The WinCC expression editor evaluates expressions against the current values of literal tag references inside the expression. It does not provide a function that takes a string and returns the value of the tag whose name is that string. For that capability you must move to a C-script and use the WinCC tag API (GetTagChar, GetTagFloat, SetTagDWord, and so on) with a string you constructed at runtime. See the TIA Portal Help "ANSI-C function descriptions > Tag" for the full list.