WinCC ScreenWindow: Get Changed Screen Object in OnChange Script

David Krause16 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

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.Screen property 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.
Note: The runtime identifier is what produces the 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:

  1. item.Name returns the engineering name of the ScreenWindow control (for example ScreenWindow_1), not the screen inside it. The value does not change when the contained screen changes, which is the behavior the original question observed.
  2. 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.Screen with If 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 return Nothing.
  • 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
Note: 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

  1. Nested ScreenWindows. When a ScreenWindow is hosted inside a screen that is itself hosted in another ScreenWindow (faceplate-of-faceplate pattern), item.Screen still returns the immediate child. The grand-child screen is reachable through item.Screen.ScreenWindow(1).Screen if the inner ScreenWindow is named.
  2. 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 ?1 suffix to the ScreenName, which forces the runtime to treat it as a different screen).
  3. Modal popups. ScreenWindows that are used as pop-up overlays (with Window > Template set to Global Screen) sometimes fire the OnChange of the host ScreenWindow as well. Null-check item.Screen defensively.
  4. Project transfer mid-runtime. After a re-transfer, the runtime prefix may briefly evaluate to an empty string. Always use the configured Config_RuntimePrefix tag rather than a hard-coded literal.
  5. VBScript 5.x quirks. InStr returns 0 (not -1) when the search string is not found, and Mid(s, 0, n) returns an empty string on older script engines. Always guard the index with If iSep > 0 Then.
  6. 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.
  7. 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.
  8. 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:

  1. Place two test screens, "Screen_A" and "Screen_B", and one host screen with a ScreenWindow.
  2. Add a button on the host screen that sets SmartTags("Nav_ScreenName") = "Screen_A", and a second button for "Screen_B".
  3. Wire the ScreenName property of the ScreenWindow to Nav_ScreenName via a tag-driven dynamization (or via a direct VBScript in the button's Click event).
  4. Attach the OnChange script shown above to the ScreenWindow.
  5. Open the HMI tags table in the runtime and add Nav_CurrentScreen, Nav_PrevScreen, Nav_ScreenChangedEdge to an HMI tag watch window or to a diagnostic faceplate.
  6. Click each button in turn. After each click, confirm: Nav_CurrentScreen matches the engineering name of the screen you just loaded; Nav_PrevScreen matches the previous one; Nav_ScreenChangedEdge pulses to 1 for one scan.
  7. Click the same button twice in a row. Confirm that Nav_ScreenChangedEdge does not pulse on the second click, and that Nav_CurrentScreen does not change.
  8. Stop and restart the runtime. Confirm that the first OnChange after restart also fires correctly and that item.Screen is not Nothing.

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.

Back to blog