MAXScript Tools and Interaction with 3ds Max > Change Handlers and Callbacks >

# Change Handlers and When Constructs

| Callback Notifications - Quick Navigation

Change Handlers are used to detect when certain types of user events are performed on objects in the scene, allowing you to write scripts that respond to user operations like object moves, vertex edits, object deletions, name changes, etc.. Change Handlers are applied to individual MAXWrapper objects and specify an attribute to monitor for that object and an expression to evaluate when that attribute changes. Example attributes that can be monitored are geometry, name, transform, and parameters. Change Handlers are not stored in a 3ds Max scene.

The ChangeHandler : Value class instances represent change handler setups. ChangeHandler values are created using the when construct. Each time a when construct is executed, it creates and returns a new ChangeHandler instance. You should store this ChangeHandler value in a variable or array so that you can dismiss the handler when it is appropriate to do so.

The when construct defines a change handler function for a certain type of event on one or more objects. The system then automatically calls this function whenever the event occurs.

The syntax of the when construct has two forms as follows:

    when <attribute> <objects> change[s] [ id:<name> ] \
    [handleAt: #redrawViews|#timeChange] \
    [ <object_parameter> ] do <expr>

    when <objects> deleted [ id:<name> ] \
    [handleAt:#redrawViews|#timeChange] \
    [ <object_parameter> ] do <expr>

In the first form, the <attribute> can be one of:

     topology geometry name[s] transform select parameters subAnimStructure controller children

These specify the attribute of the given object(s) to be tracked for change (see below for more details).

The second form tracks object deletion by the user or other scripts. This is typically used if you have tables or other structures containing MAXScript object values and you want to remove deleted objects from them as the user modifies the scene.

NOTES:
The #selectedNodesPreDelete general callback is the far superior method to use to catch node deletion by the user. It is called before anything happens to the nodes and will report the deletion of any node- in contrast, the when construct operates only on nodes explicitly registered with the callback.
    mypot1 = teapot() --create a teapot
    fn whendeleted = --define a callback function
    (
    local deletedobjects = callbacks.notificationParam()
    format "Callback: %\n" deletedobjects
    -- This will printevery object that was deleted. -- By this point, the object isstillattached to it's parents -- and is still in groups etc... -- Nothing has been done to the nodeyet.
    )
    when mypot1 deleted id:#foo obj do --define a when construct
    (
    format "When Construct:%\n" obj
    )
    --unregisterany preDeletecallbacks
    callbacks.removeScripts #selectedNodesPreDelete
    --register the function as general callback
    callbacks.addscript #selectedNodesPreDelete "whendeleted()"
A regression in 3ds Max 9 caused a change in the way the when construct was called. In releases prior to 3ds Max 9, the when construct was called before the actual deletion of the object. In 3ds Max 9, the when callback in the above code would return <deleted scene node> instead. This behavior has been fixed in 3ds Max 2008.

In both forms, the <objects> argument can be one of the following:

- a single 3ds Max object, such as a scene node, controller, modifier, or material, etc.

- one of the object sets, such as 'selection', or 'cameras', etc.

- a wild-card pathname like $foo*.

- an array of 3ds Max objects.

If more than one object is specified, the handler is called each time the given attribute of any of the objects is signaled as changed by the 3ds Max core.

The optional id: parameter specifies an ID for one or more handlers as a name literal. The ID name can later be used to delete the handler and, if the same name is given to several handlers, they can be deleted as a group.

The optional handleAt: parameter signals that the change handler expression should not be executed immediately upon notification, but delayed until the either the 3ds Max viewports are redrawn ( #redrawViews ) or current 3ds Max animation time is changed ( #timeChange ). Delaying the processing of the change handler expression is highly recommended, as described in the Caution section below. An example usage of the handleAt: parameter is:when select $ changes id:#foo handleAt:#redrawViews do ...

The optional <object_parameter> lets you specify the name of a parameter variable that will be accessible in the do <expr> and will hold the actual object that has changed. You typically specify this parameter name if the handler will respond to changes in many objects, so you can determine which one has changed. The <expr> can be a single expression or block expression.

EXAMPLE
    when transform $box01 changes do
    $box02.pos = $box01.pos + delta
    when names selection change obj do update_name_table obj
    when #($foo, $baz, $bar) deleted obj do
    (
    messageBox "Warning!"
    deleteItem obj_table (findItem obj_table obj)
    )

The change attributes are interpreted as follows:

    topology

Signaled when the topology of an object changes in the Modify panel, such as via a mesh smooth, optimize, or vertex delete.

    geometry

Signaled when the geometry of an object changes, such as by moving a vertex or via an animated modifier.

    name or names

Signaled when the name of an object is changed if this occurs because a user edits the name in one of the 3ds Max command panels. The handler is called repeatedly with each character that is changed.

    transform

Signaled when the transform of an object is changed, such as by a move, rotate, or scale.

    select

Signaled when a scene node moves into or out of the current selection set. You should interrogate the <node>.isSelected property to determine the new state.

    parameters

Signaled when any parameters are changed in the object. This is something of a catch all, because the core signals this event in many situations.

    subAnimStructure

Signaled when the dynamic subAnim structure changes, such as when a new vertex becomes animated in an editable mesh, or when a new controller is added to a list controller. Also called when subAnims are reassigned, for example, when a material is changed in an object.

    controller

Signaled when a new controller is assigned to one of the object's tracks.

    children

Signaled when an object has immediate children added or removed.

## Deleting Change Handlers

You can use the following two methods for deleting change handlers:

    deleteChangeHandler <change_handler>

deletes specified change handler . change_handler is the value returned by a when construct.

    deleteAllChangeHandlers [ id:<name> ]

If optional id: parameter is not specified, deletes all change handlers. If optional id: parameter is specified, deletes all change handlers with the specified id.

FOR EXAMPLE:
    deleteAllChangeHandlers id:#foo

For efficiency reasons, you don't want irrelevant change handlers running in the background, so it is essential that you delete those that you no longer need.

Special Considerations

- If a runtime error occurs in the body of a change handler while it is being executed, an error message is displayed and that handler is permanently disabled.

- If all of the objects being tracked by a change handler are deleted, the change handler is also deleted.

- The do <expr> change handler code is executed in a special context and not in the context of the code that created it. This means that the <expr> code cannot contain references to local variables in outer code nestings surrounding the when , since those variables are on an execution stack that does not exist at the time the when handler is called. The compiler detects any illegal references to outer locals and generates a compiler message. An important exception to this is utility and rollout panel locals, such as local functions, rollout variables and nested rollouts. You can refer to them in change handler code inside rollout code as they are associated directly with their rollout or utility object.

- Change handlers are called only for user initiated events, and not for changes resulting from a change in controller values. For example, a change handler on the transform attribute of a node would be called when the user moves the node. If the position of the node is animated, and you play back the animation, the transform attribute change handler is not called.

- If you assign a change handler to an attribute on multiple objects, the change handler is called once per object if that object’s attribute changes.

FOR EXAMPLE, IF YOU SAY:
    when select $ changes obj do update_modifier_list obj
The function update_modifier_list is called whenever any object is selected or deselected. This is true even if you do an Edit / Select All. In this case, update_modifier_list will be called once for each object currently selected (as the objects are deselected), and then once for all objects in the scene (as the objects are selected). If update_modifier_list is at all processor-intensive, a significant system slowdown can occur.

- Change handlers are only applied to object already present in the scene. Change handlers are not automatically applied to newly created objects.

- If you have multiple change handlers defined, the order in which the change handlers are called is somewhat arbitrary.

- Due to the way that 3ds Max internally processes notification signals, the $ form of accessing selected objects is not recommended in a select change handler. To access the selected objects, you should use the selection objectset. This is because $ relies on information that has not yet been set in the selection processing whereas selection uses a different method of accessing selections and the information it uses has been set up.

CAUTION !
The change handler system is based on 3ds Max's internal notification system which essentially drives all animation and interactivity within 3ds Max. There are cases when the core sends many, many signals for the same change, so setting up change handlers on many objects that do vast amounts of computing can significantly slow down the system. Strictly speaking, change handlers are supposed to be used to just set "dirty flags" in a lightweight, order-independent way, and then use Redraw Views or Time Change callbacks to actually cause the triggered processing. You should attempt to use change handlers judiciously, and not, for example, as a simple way to get scripted controllers. If you do find that a desired setup is being flooded with unnecessary signals, you should use the handleAt: to delay the actual processing of the event handler script until a redrawViews or timeChange event occurs.

| Callback Notifications - Quick Navigation

#### See Also

- Change Handlers and Callbacks

- Time Change Callback Mechanism

- Viewport Redraw Callback Mechanism

- General Event Callback Mechanism