Configuring OnPropertyChanged Scripts in WinCC Unified

David Krause17 min read
HMI / SCADASiemensTutorial / How-to
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

Configuring OnPropertyChanged Scripts in Siemens WinCC Unified

This reference documents the OnPropertyChanged event in TIA Portal WinCC Unified, the standard pattern for triggering JavaScript on a property value change, the recipe-selection workflow built on the ParameterSet control, and the resolution of the Boolean assignment error where the literal TRUE throws "Uncaught ReferenceError" at runtime.

1. Overview of WinCC Unified Event-Driven Scripting

Siemens WinCC Unified uses a JavaScript-based scripting engine that supports event handlers, scheduled functions, and global module exports. Scripts are attached to graphical objects in the screen editor and execute when a defined trigger fires. The scripting model is the Unified replacement for the VBScript C-/V-actions used in classic WinCC (TIA Portal V13/V14/V15/V16) and is documented in the integrated TIA Portal Help under "Visualize processes > Scripts". The engine runs on the Unified Comfort Panel firmware, the Unified PC Runtime, and the WinCC Unified Station — the same script source compiles to all three targets without modification.

Trigger categories in Unified:

  • System events: Loaded, Activated, Deactivated
  • Input events: OnClick, OnMouseDown, OnMouseUp, OnKeyDown, OnKeyUp
  • Property events: OnPropertyChanged (fires when a specific bound or animated property updates)
  • Lifecycle events: OnOpened, OnClosed, OnUpdating
  • Schedule and tag events: scheduled tasks, tag-value-change events (configured globally, not per object)

OnPropertyChanged is unique because it is a granular per-property hook, not a coarse screen-level event. A single object can expose dozens of properties (visibility, position, fill color, process value, etc.); only the properties on which a change script is registered will route their update to a user-defined function. This selectivity is critical for performance on Unified Comfort Panels, where the same screen may host hundreds of dynamized objects in a typical line HMI.

All scripts are written in ECMAScript 2017 (ES8) flavor, executed in strict mode, and exposed to the project namespace through export function. Three globally available objects form the scripting API: Tags() for HMI tag read/write, HMIRuntime for trace, alarm, and screen navigation calls, and Screen for the active screen context. No external JavaScript libraries are bundled with the runtime; any custom module must be loaded as a script file under "Project > Scripts" in the TIA Portal project tree.

2. Understanding the OnPropertyChanged Event

When a property is dynamized (tag binding, animation, dynamization wizard, or another script), each value change pushes a new state into the object. The OnPropertyChanged script fires for the property on which it was registered. The handler signature is generated by the TIA Portal editor and follows the convention:

export function PropertyName_OnPropertyChanged(item, value) {
    // handler body
}

Where:

  • item — the screen object reference. It carries the same properties exposed in the Properties pane and can be passed to Screen.Items().Item("Name") lookups for siblings.
  • value — the new property value as committed by the runtime. Reading it inside the handler is preferred over calling item.<PropertyName> because the parameter reflects the value at trigger time without a second property fetch.

Two important constraints shape how the handler can be written:

  1. No return value: OnPropertyChanged cannot return a value to the property's caller. Any return statement — including an early return; guard — is valid JavaScript, but in this event the return is discarded. Worse, a developer who copies a getter-style template that ends with return value; will see the script run but produce no effect, because the return is thrown away. The most common cause of "the script runs but does nothing" is exactly this.
  2. Strict-mode globals: The handler executes in the project's global script context, so every tag and function declared at project scope is reachable. Local screen-level variables are not visible to the handler unless they are global or passed via the item. This is the opposite of classic WinCC, where every screen had a local VBScript scope.

OnPropertyChanged also has timing semantics that are not always intuitive. The handler fires after the property update is committed to the object. If a subsequent read inside the handler accesses the same object, the new value is visible. This is what makes the handler a reliable place to react to a change, but also what makes it dangerous to write back to the same property — a re-write re-triggers the handler, which (if unguarded) produces a write storm that pegs the runtime CPU.

3. Prerequisites and Project Setup

  • TIA Portal V17 or later with WinCC Unified installed (Comfort Panel, Unified Comfort Panel, or WinCC Unified PC Runtime).
  • An HMI device configured in the project with at least one screen and a parameter set control (recipe view) or any dynamized graphic object.
  • HMI tags of the correct data type — WinCC Unified distinguishes Bool, Int, DInt, WString, Real, etc. Type coercion in JavaScript will fail at write time because Tags().Write() rejects mismatched primitives.
  • TraceView (Start → Siemens Automation → WinCC Unified → TraceView) or the integrated TIA Portal HMI trace pane enabled for diagnostics.
  • For ParameterSet workflows: the "Recipes" option licensed under the HMI device's Runtime settings. Without this license, the ParameterSet control may render in the editor but the CurrentParameterSetID property will not be live at runtime.

Confirm the runtime version matches the development version. A script compiled against the V17 runtime will load on a V17 panel, but the same script loaded on a V16 Comfort Panel will fail because the older runtime does not include the ES8 surface that the V17 compiler assumes. For maintenance, the panel firmware revision is visible at runtime under System → Operating System → Versions; cross-check with the TIA Portal build's "Compile > Software (rebuild all)" output.

4. Step-by-Step: Creating a Property Change Script

Step 1 — Select the Object and Locate the Property

In the screen editor, click the target object (for example, a ParameterSet control). In the Properties pane, expand the property tree to find the property to monitor. For recipe selection workflows, this is CurrentParameterSetID. For slider-driven HMI logic, this is the ProcessValue of the slider, not the slider's position.

Step 2 — Register the Change Script

Right-click the property. In TIA Portal V17 and V18, the context menu shows Add Change Script; clicking it opens the script editor. The editor auto-generates a new function with the name <PropertyName>_OnPropertyChanged. The function name is significant — the runtime binds the handler to the property based on this naming convention. Renaming the function in the script source without also renaming the registration will silently disable the handler.

Step 3 — Write the Handler

A minimal, working handler that mirrors the new parameter set ID to an internal HMI tag:

export function CurrentParameterSetID_OnPropertyChanged(item, value) {
    Tags("SetIdMirror").Write(value);
    HMIRuntime.Trace("Parameter set changed: " + value);
}

Notice the absence of a return statement. The HMIRuntime.Trace line is critical during commissioning — without it, a silent failure is nearly impossible to diagnose because the script editor's compile output is independent of runtime execution.

Step 4 — Compile and Download

Compile the HMI project (Project tree → HMI device → Compile → Software (rebuild all)). The Unified compiler creates one script bundle per screen plus a global bundle for shared modules; failed compilation surfaces as red squiggles in the script editor and a non-zero return code in the Output window. Download the resulting image to the panel or PC runtime. For Unified Comfort Panels, prefer a full download (not a delta) on the first deployment after a script change — delta downloads sometimes miss updated global modules, leaving a stale script bundle in the panel.

5. The Boolean Tag Value Mismatch Issue

The single most common first-time failure on an OnPropertyChanged handler that writes a Bool tag is:

Uncaught ReferenceError: TRUE is not defined
    at CurrentParameterSetID_OnPropertyChanged (Screen_1:line 4)

Root cause: WinCC Unified's JavaScript runtime is a strict-mode subset of ECMAScript. Unlike classic WinCC, which exposed TRUE and FALSE as VBScript-style constants, the Unified engine recognizes only the lowercase JavaScript literals true and false. VBScript developers migrating to Unified hit this on the first script that performs a Boolean write.

A secondary cause surfaces with certain panel firmware revisions: the runtime's tag write path accepts the boolean primitive true / false in V18+ but silently rejects it in some V17 firmware builds, where the integer 1 / 0 is the only reliably-accepted input. Pin to the integer form for portability across firmware revisions.

Fix #1 (preferred in V18 and later): Use the lowercase boolean literal.

Tags("MyBoolTag").Write(true); // type-correct

Fix #2 (for cross-firmware portability): Use the integer 1 for true and 0 for false.

Tags("MyBoolTag").Write(1);      // widely accepted
Tags("MyBoolTag").Write(0);      // false

Workaround pattern (string input from JSON or HMI string tag): Normalize before write.

let v = String(value).toLowerCase() === "true" ? 1 : 0;
Tags("MyBoolTag").Write(v);

Patterns that fail or behave unpredictably, and should be avoided:

  • Tags("MyBoolTag").Write("TRUE"); — string-to-Bool write throws a type-mismatch error on the panel, not in the editor.
  • Tags("MyBoolTag").Write(Number(true)); — works in V18.0+ but fails in V17.0; use the literal integer for portability.
  • Tags("MyBoolTag").Write(!item.SomeFlag); — depends on the boolean operator inside the runtime; safer to compute a number first and write that.
  • Tags("MyBoolTag").Write(TRUE); — original failure mode. Replace with 1.

6. Working with Parameter Sets and Recipe Selection

The ParameterSet control object exposes a "current parameter set" property used by recipe workflows. Wiring an OnPropertyChanged handler to this property is the standard way to detect operator-driven recipe changes from JavaScript, because Unified does not emit a dedicated "recipe selected" system event for the parameter set object until the data record is written to the PLC. The script lets the HMI react to the operator's choice immediately, without waiting for the PLC handshake.

A production-grade handler mirrors the selection to a status tag, audits the change with a timestamp, and increments a counter:

export function CurrentParameterSetID_OnPropertyChanged(item, value) {
    Tags("RecipeStatus.SetID").Write(value);
    Tags("RecipeStatus.LastChange").Write(new Date().toISOString());
    let count = Tags("RecipeStatus.ChangeCount").Read();
    Tags("RecipeStatus.ChangeCount").Write(count + 1);
    HMIRuntime.Trace("Recipe " + value + " selected, count=" + (count + 1));
}

Three cautions specific to recipe change handlers:

  1. Do not write back to the same object inside the handler. Re-writing the same property on the same item re-triggers OnPropertyChanged and creates a write storm. The Trace line above is safe because Trace does not write to a watched property. Writes to unrelated HMI tags are also safe.
  2. Read-then-write patterns are not atomic. The counter increment above will lose an increment if two recipe changes arrive in the same screen cycle — possible during fast operator taps or touchscreen bounce. For audited counters, use a PLC-side counter or an HMI tag of type DInt with a soft-lock flag, or move the increment to the PLC and read back the new value from the same handler.
  3. Parameter set IDs in WinCC Unified are integers, not strings. Even if the recipe name contains letters, CurrentParameterSetID is a numeric handle. Treat the value as a numeric ID, not a recipe name. To map from ID to name, use the recipe data record table or a separate HMI tag array indexed by ID.

For deeper integration, the handler can also fire a screen navigation — for example, switching to a recipe-detail screen on selection:

HMIRuntime.UI.SwitchToScreen("RecipeDetail_" + value, "");

Note that screen switches inside OnPropertyChanged can be aggressive. If the user is mid-typing into another field when a background recipe change fires (e.g., from a PLC-side synchronization), the screen switch will discard the input. Gate the navigation behind an operator-mode check before invoking.

7. Diagnostics: Using TraceView for Script Debugging

When a change script does not run, the first tool is TraceView (Siemens TraceView, installed with TIA Portal) or the integrated TIA Portal HMI trace. Inside the script, the standard diagnostic is:

HMIRuntime.Trace("entry: value=" + value + " item=" + item.Name);
HMIRuntime.Trace("typeof value = " + typeof value);

TraceView's standard filter must be set to "User-defined trace" or "All" to capture HMIRuntime.Trace output; the default filter hides them. For production panels, set the trace level under Runtime Settings → Services → Trace to "Extended" temporarily — the level reverts after a panel reboot, so this is safe to leave as a one-time commissioning step.

For deeper diagnostics, the runtime exposes a browser-style console via the Chrome DevTools protocol when the panel is started with the development mode flag. Connecting to the panel from a remote Chrome on port 9229 reveals script errors, network requests, and live tag values. This is the same tooling that Ignition uses for its designer debugger, and the panel-side setup is identical: enable "Remote debugging" under the panel's Runtime Settings → Services.

Common diagnostic patterns and what they mean:

Symptom Likely Root Cause Fix
No trace line appears on click Script not compiled or not downloaded Rebuild HMI, full download (not delta)
Trace says "TRUE is not defined" Uppercase boolean literal Use true or 1
Trace prints but tag does not update Wrong tag name or no PLC connection Verify tag in HMI tag table; check area pointer and S7 connection
Trace shows stale value Property dynamization not active Re-bind tag in property animation, not in script
Handler runs twice on one click Two change scripts attached to same property Delete duplicate registration in Properties pane
Trace line prints, then runtime freezes Handler writes to its own watched property Add re-entry guard flag, or write to a different object
Compilation succeeds, download fails Tag referenced in script does not exist on panel Cross-check tag name spelling with HMI tag table

8. Event Type Comparison: When to Use Each Trigger

Event Best for Returns a value? Frequency Reentrancy risk
OnClick Button presses, single-shot actions No Per click Low
OnPropertyChanged Watching a specific property (tag/animation) No Per value change High if handler writes same property
OnMouseDown / OnMouseUp Hold-to-repeat, drag handlers No Per frame while held Medium
OnOpened / OnClosed Screen lifecycle, init / cleanup No Once per transition Low
OnActivated / OnDeactivated Cursor focus, screen popups No Per focus change Low
Scheduled task Polling, periodic cleanup No Per schedule tick Configurable
Tag value change (global) Cross-screen, tag-driven No Per tag update Configurable

OnPropertyChanged is the only event that pairs naturally with the dynamization wizard. If the goal is "do X when the operator moves slider A", bind slider A's value to a tag and watch the tag. If the goal is "do X when the operator selects recipe R", watch the parameter set's CurrentParameterSetID — this is the recipe-selection trigger. For cross-screen, decoupled reactions (e.g., update an overview screen when any recipe changes), a global tag value change event is the right tool; OnPropertyChanged only fires while the object owning the property is on the active screen.

9. Best Practices for Property Change Scripts

  • Never return a value. OnPropertyChanged is a void event. Any return in the function body is a code smell — remove it. The runtime does not consume the return value, so the return is dead code that obscures intent.
  • Prefer the value parameter over item.<PropertyName>. The parameter is the value as the runtime sees it, already coerced to the property's declared type. Re-reading the property inside the handler can be a fraction of a frame stale on panels under load.
  • Guard against re-entry. If the handler writes to the same property on the same item, gate the write with a module-level flag to break the loop:
    let _guard = false;
    export function CurrentParameterSetID_OnPropertyChanged(item, value) {
        if (_guard) return;
        _guard = true;
        try {
            Tags("Mirror").Write(value);
        } finally {
            _guard = false;
        }
    }
    The finally clause ensures the guard clears even if the write throws.
  • Trace early, trace often. The cost of one HMIRuntime.Trace is negligible compared to the time spent debugging a silent script. Remove or guard with a verbose flag (HMIRuntime.Trace(debugFlag ? "..." : "")) for production. Leaving trace lines in production is acceptable on Unified Comfort Panels but doubles the log size; for PC runtime with long uptimes, guard them.
  • Use the global Tags() and HMIRuntime namespaces. Screen-local tags must be addressed through the screen's context object; the unqualified Tags() call refers to the project's HMI tag table. Mixing local and global tag namespaces is the second-most common source of "tag does not update" complaints after the boolean literal issue.
  • Keep the handler short. Heavy work (DB writes, network calls, file I/O) belongs in a system function triggered asynchronously, not in the property change callback. A long-running handler blocks the next frame of the screen and causes visible lag on Unified Comfort Panels. The runtime's watchdog can also kill scripts that exceed a default execution budget; the exact budget is firmware-dependent.
  • Use try/catch around tag writes. Tags().Write() can throw on connection loss. Wrap writes in try/catch and write a trace on failure to avoid silent drops in production logs.
  • Prefer await for async operations. Unified supports async function syntax. For tag reads that need to complete before a dependent write, use await Tags().ReadAsync() in an async handler.

10. Migration Notes, Edge Cases, and Platform Considerations

Engineers moving from classic WinCC (TIA Portal V13/V14/V15/V16) to Unified face three recurring traps:

  1. VBScript to JavaScript. TRUE, FALSE, Nothing, Empty, and the SmartTags COM object are gone. Replace SmartTags("Tag") with Tags("Tag"); replace TRUE with true or 1; replace Nothing with null.
  2. Variable scope. Classic WinCC's per-screen C/V action scope did not exist in VBScript's design; Unified's strict-mode global scope is a closer fit. Plan to centralize utility functions in a global module file (Project → Scripts → add a new JavaScript file) and import them via the project tree.
  3. Object access. Classic WinCC used HmiRuntime.Screens("Screen_1"); Unified uses Screen.Items().Item("ObjectName"). The path is longer but more consistent across screens.

Edge cases that surface only in production:

  • Property updated during screen close. If the property changes while the screen is in the process of closing, OnPropertyChanged may fire after the screen's destructor. Reading item.Name in the trace may show the object name; reading properties may throw. Wrap the trace in try/catch.
  • Bulk tag updates. If 50 tags update in the same screen cycle (e.g., from a PLC recipe load), 50 handlers fire synchronously and the screen stalls. Consider throttling with setTimeout or moving bulk reactions to a global tag change event that aggregates state.
  • Animation-driven property changes. Properties that are animated by the dynamization wizard fire OnPropertyChanged on every animation tick. For a high-frequency animation (e.g., a position interpolation at 100 ms), the handler fires 10 times per second. Make sure the handler is cheap.
  • Tag with no PLC backing. Tags("LocalOnly").Write(value) succeeds even without an S7 connection because the tag is held in panel memory. A common misconception is that the PLC must be online for any tag write to work — that is only true for tags configured as PLC tags with an active area pointer.

For the panel firmware, the TIA Portal Help includes a compatibility matrix under "WinCC Unified → Engineering → Compatibility". The matrix lists which panel firmware revisions support which TIA Portal version and which script features. When a script behaves differently on a customer panel than on a development panel, the firmware version is the first variable to check. The support portal at Siemens Industry Online Support hosts the latest compatibility matrix and is the authoritative reference for any version-specific question.

11. Frequently Asked Questions

Why does my OnPropertyChanged script not fire when I expected it to?

The property must be dynamized for the runtime to push value changes into it. A static value, set once at compile time, will not raise the event. Bind the property to an HMI tag or an animation, and the change script will fire on every value push from the source. Confirm the dynamization is active in the Properties pane — a greyed-out dynamization indicator means the binding was deleted.

Why does writing TRUE throw "Uncaught ReferenceError"?

WinCC Unified's JavaScript runtime is case-sensitive and recognizes only the lowercase literals true and false. VBScript's TRUE and FALSE do not exist in the JavaScript global scope. Replace TRUE with true for type-correct code, or with the integer 1 for maximum firmware portability across V17 and V18 panel revisions.

Can I return a value from an OnPropertyChanged handler?

No. The OnPropertyChanged event signature is void. Any return statement terminates the handler, and the return value is discarded by the runtime. To influence a value elsewhere, write to an HMI tag or call a system function (e.g., HMIRuntime.UI.SwitchToScreen) instead of attempting a return value.

How do I detect a recipe change from JavaScript in WinCC Unified?

Attach an OnPropertyChanged script to the ParameterSet control's CurrentParameterSetID property. The handler receives the new parameter set ID in the value argument. Mirror it to a status tag, log a Trace line for diagnostics, and optionally increment an audit counter. The ParameterSet control does not emit a dedicated recipe-selected system event, so the property change script is the standard detection point.

Where can I see script output during runtime?

Open Siemens TraceView (Start → Siemens Automation → WinCC Unified → TraceView) and set the filter to "User-defined trace" or "All". Output from HMIRuntime.Trace() in your script will appear there in real time. For richer debugging, enable remote debugging on the panel under Runtime Settings → Services and connect Chrome DevTools to port 9229.

How do I stop a property change script from re-firing itself?

Add a module-level boolean guard flag. Set the flag to true at the start of the handler, perform the write, and reset the flag in a finally block. This breaks the re-entry loop that occurs when a handler writes to its own watched property. The pattern is documented in section 9 of this article.

Back to blog