Assigning Dynamic Tag Names to WinCC I/O Field Properties

David Krause19 min read
SiemensTutorial / How-toWinCC
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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.

Scope: The techniques below apply to WinCC in TIA Portal V15.1 through V18 SP1 (the WinCC Engineering toolchain part of STEP 7 / TIA Portal). The runtime targets covered are WinCC RT Advanced, WinCC RT Professional, and SIMATIC Comfort/MTP panels. Where WinCC flexible 2008 SP3 behavior diverges, it is called out explicitly.

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:

  1. Tag prefix (TagPrefix) – the namespace declared on the containing picture window.
  2. Tag name – the local name configured in the HMI tag table.
  3. 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:

  1. 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.
  2. 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:

  • GetTagChar is the synchronous read; for non-blocking reads from the PLC use GetTagCharState or the asynchronous variant.
  • Always pass a buffer size as the third argument. The third parameter to GetTagChar is the maximum number of characters to copy (including the terminator), and the function will return -1 if the buffer is too small.
  • szFullName must 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, or GetTagBitWait depending 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);
}
Performance: A C-script triggered on Change value fires on every keystroke. For large string-tag lists or a high-frequency update, prefer the Input finished event, which fires only when the operator presses Enter or tabs out. The Change value event is appropriate for validation logic where the script must veto the input before it is committed.

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 sees test01@NOTP::Temp and 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::test01value1 string 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:

  1. 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.
  2. Use a C-script that calls SetLink on 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.
  3. 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

  1. Add an internal text tag IntPartName (length 32) in the HMI tag table.
  2. 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.
  3. 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.
  4. Open the I/O field's Events tab. Add a C-script on the Input finished event.
  5. Implement the script from §5.2. The script reads IntPartName, appends the fixed suffix, and uses GetTagChar / SetTagChar to exchange the value with the dynamically addressed tag.
  6. Compile the project. Resolve any "tag not found" warnings — they almost always indicate a missing HMI tag or a wrong prefix.
  7. Download to the panel (WinCC RT Advanced) or the runtime PC (WinCC RT Professional).
  8. Verify in the runtime: type a value into the I/O field, change the value of IntPartName in 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

  1. Insert the I/O field and bind its OutputValue to any valid placeholder tag (so the project compiles).
  2. Insert a button that triggers the re-binding script. The script runs on Click.
  3. Write the C-script from §6. The script must be a C-script on the button object, not a global script, so that lpszPictureName and lpszObjectName resolve to the button's picture.
  4. 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.
  5. 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: GetTagChar with a name like test01array[3] works in WinCC RT Advanced; in WinCC flexible 2008 the syntax was test01array,3 with 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 IntPartName is empty, the script will attempt to read a tag named value1. 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) < 128 before 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: SetLink exists 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

  1. Default to faceplates with typed interfaces for any panel that has more than two screen instances.
  2. Reserve C-script dynamic tag binding for true runtime variability — operator-selectable tag list, recipe-driven naming, etc.
  3. Always guard against an empty prefix in the C-script.
  4. Use Input finished for write-back, not Change value, unless validation requires the latter.
  5. Do not concatenate @NOTP:: with a runtime variable; pass it as a literal at position 0 of the string supplied to SetLink.
  6. 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.
  7. Wrap every GetTagChar call with a length check and a buffer-size check.
  8. 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.

Back to blog