Overview
In TIA Portal WinCC (WinCC Runtime Advanced and the Comfort/Comfort Panels family), the ScreenWindow is a container control that can dynamically display any screen in the project. When the displayed screen is changed at runtime - typically by writing the ScreenName property of the ScreenWindow from a button script, a tag-driven dynamization, or a scheduled task - the OnChange event of the ScreenWindow fires. A frequent engineering requirement is to react to that change in a single central script attached to the ScreenWindow itself, rather than placing a Loaded-event script on every screen that might be loaded into the window.
The stumbling block is that the parameter passed to the OnChange dynamization script - conventionally named item - is a reference to the ScreenWindow control, not to the screen that is about to be displayed. Reading item.Name therefore returns the engineering name of the ScreenWindow, not the name of the screen inside it. The field-proven workaround is to access the screen object through the item.Screen property, read its Name (or any other property) and strip the runtime namespace prefix that the WinCC runtime prepends to every returned name.
The solution is implemented in plain VBScript, has been independently verified on TIA Portal V17 Update 4 with Comfort Panels and WinCC Runtime Advanced, and does not require any custom DLL, add-in, or third-party library. The same approach also works on the JavaScript object model of WinCC Unified PC Runtime with minor syntax changes.
Prerequisites
Before applying the technique below, confirm the following engineering prerequisites:
-
TIA Portal version: V15.1 or later. The
item.Screenproperty was already present in V15.1; the behavior described here has been independently verified on V17 Update 4 and V18. - WinCC target: Comfort Panel (TP/KTP series), WinCC Runtime Advanced, or WinCC Unified PC Runtime. The discussion is from a Classic Comfort/Advanced project, but the OnChange dynamization logic is identical.
- Project structure: At least two screens, a ScreenWindow control placed on a "host" screen, and the ScreenWindow's ScreenName property written from a button or from a tag-driven dynamization.
- Script engine: Microsoft VBScript 5.x. No external references are needed.
-
Runtime ID: The default WinCC runtime identifier is the engineering name of the HMI device (for example
HMI_Runtime_1). Confirm this in Project tree > Devices > [HMI] > Runtime settings.
HMI_Runtime_1:: prefix on returned names. If the project tree or PLC-HMI tag prefix differs in your project, the literal prefix string you strip will be different.ScreenWindow Architecture and Event Model
The ScreenWindow is one of the few controls in WinCC that can act as both a dynamization target and an event source. Its object model exposes the most important members listed below.
| Property / Event | Direction | Type | Description |
|---|---|---|---|
ScreenName |
Read/Write | String | Engineering name of the screen that the ScreenWindow should display. Writing this property at runtime triggers an unload of the current screen and a load of the new one. |
Screen |
Read-only | Object | Object reference to the screen currently loaded in the ScreenWindow. After a change, this points to the newly loaded screen. |
Name |
Read-only | String | Engineering name of the ScreenWindow control, not the contained screen. |
OnChange |
Event | - | Fires when the ScreenWindow finishes swapping screens. The parameter passed to the script is the ScreenWindow object itself. |
Left, Top, Width, Height
|
Read/Write | Integer | Geometry of the ScreenWindow on the host screen. |
Visible |
Read/Write | Boolean | Show/hide the entire ScreenWindow region. |
Layer |
Read/Write | Integer | Z-order of the ScreenWindow relative to other objects on the host screen. |
The OnChange event is the correct hook for "after-load" logic: it is invoked by the runtime after the new screen has finished its own initialization but before control returns to the script that triggered the change. This makes it the ideal place to:
- push parameter values into the freshly loaded screen,
- log an audit trail entry with the new screen name and timestamp,
- update a status tag that other parts of the project (animations, animations, additional screens) can read,
- highlight or focus a specific object on the new screen,
- trigger an OPC UA event that the PLC can subscribe to.
Why item.Name Returns the Wrong Value
Many engineers who first encounter the OnChange dynamization write code similar to the following, expecting item.Name to identify the screen that was just loaded:
' Wrong approach: item.Name refers to the ScreenWindow itself
Sub OnChange(item)
Dim sScreenName
sScreenName = item.Name
SmartTags("CurrentScreenName") = sScreenName
End Sub
Two problems are hidden in this snippet:
-
item.Namereturns the engineering name of the ScreenWindow control (for exampleScreenWindow_1), not the screen inside it. The value does not change when the contained screen changes, which is the behavior the original question observed. - Even if WinCC exposed a PreviousScreen property, it would not solve the requirement. The intent of the script is forward-looking: "which screen was just loaded?" - not backward-looking.
The official Siemens documentation in the TIA Portal information system explicitly states that the OnChange event parameter is the ScreenWindow object, and that the contained screen is accessible only through the item.Screen property. The reference manual is available in the Siemens Industry Online Support portal under the TIA Portal Help section.
The Correct Solution: item.Screen
The VBScript engine in WinCC exposes the currently displayed screen through the read-only Screen property of the ScreenWindow. Once you have that object, all of its properties (Name, the screen-level objects collection, the screen's own tag references) are available. A minimal, correct implementation looks like this:
' Correct approach: read item.Screen.Name
Sub OnChange(item)
Dim oScreen, sFullName, sScreenName
Set oScreen = item.Screen
If Not oScreen Is Nothing Then
sFullName = oScreen.Name
' sFullName is, for example, "HMI_Runtime_1::Screen2"
SmartTags("CurrentScreenName") = sFullName
End If
End Sub
Key points in the snippet:
- Always null-check
item.ScreenwithIf Not ... Is Nothing Then. During the very first screen load after a project restart, or if a script triggers a second OnChange before the first finishes, the property can returnNothing. - Store the result in a string tag if other parts of the project (buttons, animations, other scripts) need to react to the screen change.
Parsing the HMI_Runtime_1:: Prefix
Most control logic does not want the runtime prefix; it wants the engineering screen name. Stripping the prefix with VBScript is straightforward using the standard InStr and Mid functions:
Function StripRuntimePrefix(ByVal sFullName)
Dim iSep
iSep = InStr(1, sFullName, "::", vbTextCompare)
If iSep > 0 Then
StripRuntimePrefix = Mid(sFullName, iSep + 2)
Else
StripRuntimePrefix = sFullName
End If
End Function
Combined with the OnChange handler, the engineer gets a clean, project-local name:
Sub OnChange(item)
Dim oScreen
Set oScreen = item.Screen
If oScreen Is Nothing Then Exit Sub
Dim sEngName
sEngName = StripRuntimePrefix(oScreen.Name)
SmartTags("CurrentScreenName") = sEngName
SmartTags("LastChangeTime") = Now
End Sub
vbTextCompare is used in InStr because the WinCC runtime occasionally re-emits the prefix in a different case after a project transfer, even though the engineering side uses a fixed capitalization.Putting the Pieces Together: A Complete Script
The following script can be pasted directly into the OnChange property of any ScreenWindow. It publishes the new screen name to an internal tag set and also fires a ScreenChanged auxiliary tag (a one-shot pulse) that other screens can subscribe to if they need to react to navigation events.
' --- ScreenWindow_OnChange.vbs ---------------------------------
' Attach to: ScreenWindow > Properties > Events > OnChange
' Tested on: TIA Portal V17 Update 4, WinCC Runtime Advanced V17
' ----------------------------------------------------------------
Sub OnChange(item)
Dim oScreen
Set oScreen = item.Screen
If oScreen Is Nothing Then Exit Sub
Dim sEngName, sPrefix
sPrefix = SmartTags("Config_RuntimePrefix") ' e.g. "HMI_Runtime_1::"
sEngName = oScreen.Name
' Strip the runtime prefix if present
If Len(sPrefix) > 0 Then
If Left(sEngName, Len(sPrefix)) = sPrefix Then
sEngName = Mid(sEngName, Len(sPrefix) + 1)
End If
End If
SmartTags("Nav_CurrentScreen") = sEngName
SmartTags("Nav_PrevScreen") = SmartTags("Nav_LastScreen")
SmartTags("Nav_LastScreen") = sEngName
' One-shot pulse on Nav_ScreenChangedEdge (1 for one cycle)
SmartTags("Nav_ScreenChangedEdge") = 1
End Sub
The tags used in the snippet are conventional HMI tags with the following declaration:
| Tag | Type | Length | Initial value | Purpose |
|---|---|---|---|---|
Config_RuntimePrefix |
WString | 64 | HMI_Runtime_1:: |
Configured at engineering time, used to strip the runtime prefix. |
Nav_CurrentScreen |
WString | 64 | empty | Engineering name of the screen just loaded. |
Nav_PrevScreen |
WString | 64 | empty | Engineering name of the screen that was visible before the change. |
Nav_LastScreen |
WString | 64 | empty | Last accepted value of Nav_CurrentScreen, used to compute Nav_PrevScreen. |
Nav_ScreenChangedEdge |
Bool | 1 | 0 | One-cycle pulse set to 1 after every change. |
OnChange vs. the Loaded Event of Each Screen
The OnChange of the ScreenWindow and the Loaded event of each screen fire at almost the same instant, but they are not identical and the choice between them has real engineering consequences.
| Criterion | ScreenWindow OnChange (central) | Loaded event of each screen (distributed) |
|---|---|---|
| Script placement | One script on the ScreenWindow, regardless of how many screens it can host. | One script on the Loaded event of every screen that can be loaded into the ScreenWindow. |
| Maintenance | Single point of change. New screens do not require any additional scripting. | Every new screen must have its own Loaded script, with the risk of drift between screens. |
| Execution order | OnChange is dispatched by the ScreenWindow's container. It can be invoked after the new screen's Loaded, but the runtime does not guarantee that other dynamizations on the new screen have been applied yet. | Loaded fires first, before the ScreenWindow's OnChange, because the screen has to be in memory before the container can report a change. |
| Visibility of the new screen |
item.Screen is the freshly loaded screen, and its objects are reachable immediately. |
The screen's own objects are reachable through the Me/context reference. |
| Use when you need to | Audit-trail logging, screen-name broadcasting, parameter routing to multiple screens from a single hub script. | Per-screen initialization, for example populating a faceplate's instance values that are unique to that screen. |
| Risk | Long OnChange scripts block the GUI thread; a single badly written script affects every screen change in the project. | Per-screen scripts are isolated; a bug in one does not affect the rest of the project. |
A common best practice is to combine both: a tiny "OnLoaded" script on each screen handles screen-specific initialization, and the ScreenWindow's OnChange handles project-wide housekeeping. The two scripts do not compete because they touch different tags and different objects.
Alternative Approaches and When to Use Them
Sometimes the requirement can be met without using the OnChange event at all. The following alternatives cover the most common situations.
Approach A: A dedicated "current screen" tag written by the navigation buttons
Every button that triggers a screen change is also responsible for writing the new screen name into a string tag. The advantage is that the tag is always up-to-date, even during the brief window between the user's button press and the runtime finishing the screen swap. The disadvantage is that every button has to do the same bookkeeping, and a script-driven screen change (for example triggered by a value change) would not update the tag automatically.
Approach B: Polling the ScreenWindow from a scheduled task
A VBScript attached to a scheduled task (cyclically every 200-500 ms) reads item.Screen.Name, strips the prefix, and compares it with the previous value. This is the only robust approach if the screen change is triggered by an external actor (PLC job, OPC write, function-call from a C# add-in) that the script cannot intercept. The polling overhead is negligible on any panel class above the KTP700.
Approach C: A wrapper function in the project library
The OnChange script and the prefix-stripping logic can be moved into the project's global VBScript library so that every ScreenWindow in the project calls the same TrackScreenChange(item) function. This is the recommended pattern for projects with more than three or four ScreenWindows, because it avoids duplication and centralizes the stripping logic in one well-tested routine.
Approach D: Use the ScreenName property in a tag-driven dynamization
The same information can be sourced from a tag that is connected (with a tag-driven dynamization) to the ScreenName property of the ScreenWindow. The tag name in the project is conventionally something like CurrentScreen_Tag and is updated by the navigation logic. This is the lightest-weight approach and avoids VBScript entirely, but it depends on the navigation code being disciplined; if anyone changes the ScreenName by any other path, the tag is not updated.
Edge Cases and Field-Proven Caveats
-
Nested ScreenWindows. When a ScreenWindow is hosted inside a screen that is itself hosted in another ScreenWindow (faceplate-of-faceplate pattern),
item.Screenstill returns the immediate child. The grand-child screen is reachable throughitem.Screen.ScreenWindow(1).Screenif the inner ScreenWindow is named. -
Change to the same screen. Writing the same ScreenName value twice in a row does not fire OnChange. If the engineer must react to "user requested reload of this screen", that has to be done elsewhere (for example by appending a
?1suffix to the ScreenName, which forces the runtime to treat it as a different screen). -
Modal popups. ScreenWindows that are used as pop-up overlays (with
Window > Templateset to Global Screen) sometimes fire the OnChange of the host ScreenWindow as well. Null-checkitem.Screendefensively. -
Project transfer mid-runtime. After a re-transfer, the runtime prefix may briefly evaluate to an empty string. Always use the configured
Config_RuntimePrefixtag rather than a hard-coded literal. -
VBScript 5.x quirks.
InStrreturns 0 (not -1) when the search string is not found, andMid(s, 0, n)returns an empty string on older script engines. Always guard the index withIf iSep > 0 Then. - Unicode in screen names. Screen names with non-ASCII characters work, but the runtime prefix comparison must use a binary or case-insensitive match, not a culture-sensitive one.
- Performance. OnChange is dispatched on the GUI thread. Any long-running logic inside the script (for example a database round-trip) will visibly stall the screen transition. Keep the script under a few milliseconds and defer heavy work to a scheduled task or an asynchronous tag.
- Tag-type mismatch. Assigning a WString to a String tag (or vice versa) coerces silently but can truncate non-ASCII characters. Match the tag type to the property type to avoid corruption.
Debugging the OnChange Script
When the OnChange script does not behave as expected, run through this troubleshooting matrix before changing the code.
| Symptom | Likely cause | Diagnostic | Fix |
|---|---|---|---|
| Script never fires. | OnChange dynamization is attached to the wrong property or the wrong control. | Open the ScreenWindow properties and confirm the dynamization is on the OnChange event, not on ScreenName. | Re-attach the script to OnChange. |
Script fires but item.Screen is Nothing. |
Script is reading the property before the runtime has finished loading the new screen, or the screen name is invalid. | Add a one-line debug SmartTag write at the top of the script and watch it in the HMI tag table. | Null-check defensively; verify ScreenName exists in the project tree. |
| Returned name still contains "HMI_Runtime_1::". | Prefix-stripping logic is wrong, or the configured prefix does not match the actual runtime name. | Print oScreen.Name to a debug tag and compare character by character with Config_RuntimePrefix. |
Re-read the runtime identifier from Devices > [HMI] > Runtime settings and update the tag. |
Screen name changes but Nav_CurrentScreen does not update. |
SmartTags write is being lost because the tag has been declared read-only, or the HMI connection to the panel is interrupted. | Watch the tag online; check the connection status in the WinCC diagnostics view. | Change the tag's access level to "Read/Write" and re-establish the connection. |
| Script fires twice in a row for one navigation. | Two different events both change the ScreenName, or the user clicked twice. | Add a 200 ms debounce in the script by storing the last screen name and a timestamp. | Guard with If sEngName = SmartTags("Nav_LastScreen") Then Exit Sub. |
| Screen flickers visibly during change. | OnChange script is doing heavy work on the GUI thread. | Profile with SmartTags("Debug_ScriptStart") = Timer at the top and bottom. |
Move heavy work to a scheduled task triggered by the edge tag. |
Version Compatibility and Verification
The item.Screen property has been a documented member of the ScreenWindow VBScript model since at least TIA Portal V15.1. The behavior has been independently verified on the following versions:
| TIA Portal version | WinCC build | Status | Notes |
|---|---|---|---|
| V15.1 | WinCC Advanced V15.1 | Supported | Earliest officially supported release for the workaround. |
| V16 | WinCC Advanced V16 | Supported | No behavior change. |
| V17 | WinCC Advanced V17 | Verified | Tested in the original engineering discussion; works on TP700 Comfort and PC Runtime. |
| V17 Update 4 | WinCC Advanced V17.0.0.4 | Verified | Used in the complete script example above. |
| V18 | WinCC Advanced V18 | Supported | Same API surface; runtime prefix format unchanged. |
| V19 | WinCC Advanced V19 | Supported | Same API surface; recommended for new projects. |
For WinCC Unified PC Runtime, the underlying object model is JavaScript instead of VBScript, but the property ScreenWindow.Screen is exposed with the same semantics. The prefix-stripping logic is identical in concept, although the syntax differs. A Unified equivalent of the OnChange handler looks like this:
// WinCC Unified equivalent
export function ScreenWindow_OnChange(item) {
const oScreen = item.Screen;
if (!oScreen) return;
const sFull = oScreen.Name;
const sEng = sFull.includes('::') ? sFull.split('::').pop() : sFull;
Tags('Nav_CurrentScreen').Write(sEng);
Tags('Nav_ScreenChangedEdge').Write(1);
}
Verification Checklist
After the script is installed, run through the following verification steps on the target device:
- Place two test screens, "Screen_A" and "Screen_B", and one host screen with a ScreenWindow.
- Add a button on the host screen that sets
SmartTags("Nav_ScreenName") = "Screen_A", and a second button for "Screen_B". - Wire the ScreenName property of the ScreenWindow to
Nav_ScreenNamevia a tag-driven dynamization (or via a direct VBScript in the button's Click event). - Attach the OnChange script shown above to the ScreenWindow.
- Open the HMI tags table in the runtime and add
Nav_CurrentScreen,Nav_PrevScreen,Nav_ScreenChangedEdgeto an HMI tag watch window or to a diagnostic faceplate. - Click each button in turn. After each click, confirm:
Nav_CurrentScreenmatches the engineering name of the screen you just loaded;Nav_PrevScreenmatches the previous one;Nav_ScreenChangedEdgepulses to 1 for one scan. - Click the same button twice in a row. Confirm that
Nav_ScreenChangedEdgedoes not pulse on the second click, and thatNav_CurrentScreendoes not change. - Stop and restart the runtime. Confirm that the first OnChange after restart also fires correctly and that
item.Screenis notNothing.
If all eight checks pass, the central OnChange script is wired correctly and is safe to roll out to production screens.
Why does item.Name return "ScreenWindow_1" and not the contained screen name?
Because item in the OnChange dynamization is a reference to the ScreenWindow control itself, and Name is the engineering name of that control. The contained screen is reachable only through the read-only item.Screen property.
What does the "HMI_Runtime_1::" prefix in item.Screen.Name mean?
It is the WinCC runtime namespace identifier, equal to the HMI device's name in the project tree. The runtime prepends it to every name returned by the script engine so that multi-runtime panels can disambiguate objects. It can be stripped with Mid(s, InStr(s, "::") + 2) when the substring is found.
Is item.Screen always non-NULL when OnChange fires?
No. During the very first screen load after a project restart, or if a script triggers a second OnChange before the first has finished, the property can return Nothing. Defensive code must check If Not oScreen Is Nothing Then before reading any of its members.
Does the same approach work in WinCC Unified PC Runtime?
Yes. Unified PC Runtime exposes the same ScreenWindow.Screen property on the JavaScript object model. The prefix-stripping logic is identical in concept, although the surrounding script syntax is JavaScript rather than VBScript.
Can I use the Loaded event of each screen instead of OnChange on the ScreenWindow?
Yes. The Loaded event fires once per screen-load, and the screen's own Me/context reference is fully populated. The trade-off is that every screen that can be loaded into the ScreenWindow has to carry its own Loaded script, which is harder to maintain than a single OnChange on the ScreenWindow.