How To > Use ActiveX Controls > Develop a Selected Objects Inspector - Part Two |
This second tutorial will demonstrate how to customize the Listview ActiveX Control defined in Part One and update the display automatically when the selection set changes.
ActiveX Controls have been deprecated by Microsoft in the latest versions of the Windows operating system in favor of the DotNet framework and its controls.
While MAXScript still supports ActiveX controls, these have to be installed and registered on the system to be accessible to MAXScript.
As a replacement of ActiveX controls, MAXScript supports DotNet controls in 3ds Max 9 and higher.
Please see the topic Converting ActiveX ListView Control to DotNet ListView Control
ActiveX Controls in MAXScript Rollouts
ListView ActiveX Control Example
General Event Callback Mechanism
In order to be able to access the rollout from outside of the macroScript scope (in our case a callback script), we have to define the rollout variable as global.
rollout listview_rollout "ListView Selected" ( fn initListView lv = ( lv.gridLines = true lv.View = #lvwReport lv.fullRowSelect = true lv.backColor = color 225 215 210
We assign a new color to the backColor property of the Listview.
We will also enable the built-in checkboxes of the Listview control and use them to represent the renderable node-level property.
layout_def = #(#("On",28), #("Object Name",120), #("Object Class",80), #("Verts",45), #("Faces",45), #("Material",120))
We will extend the existing version of the layout definition array. Instead of storing only the names of the colums, we will store sub-arrays containing both the name and the width of the column. In the future, you could store any additional per-column data like bold text Boolean flag, foreground color of the text etc.)
We define two user variables which will contain the first column's address and the message id to be sent to the windows handle of the Listview control responsible for setting the column width.This is "esoteric knowledge" available from literature on general Microsoft Windows and ActiveX Controls programming. Just use it the way it is provided here.
for i = 0 to layout_def.count-1 do windows.sendMessage lv.hwnd LV_SETCOLUMNWIDTH i layout_def[1+i][2]
Now we loop from 0 to the number of columns minus one. This is because the windows.sendMessage method expects the index of the column to be set as a 0-based index, but MAXScript array indices are 1-based.
The windows.sendMessage method expects the windows handle of the ActiveX control (lv.hwnd), the message to be sent, the index of the column to set the width and the width value (which is stored in the 1-based array at index (i+1), in the second item of the sub-array)
Before we fill in the data into the Listview, we should make sure the list is emptied first. This is because this time around, the list will be updated on the fly without closing and reopening the dialog!
The first column will contain only the checkbox. We set the .checked property to the boolean value returned by the node-level .renderable property of the current object.
The former first column with the name of the object is now the second column, so we have to create a sub list item for it.
We set the .text property of the sub list item to the name of the object just like before.
sub_li = li.ListSubItems.add() sub_li.text = (classof o) as string sub_li = li.ListSubItems.add() sub_li.text = try((o.mesh.numverts) as string)catch("--") sub_li = li.ListSubItems.add() sub_li.text = try((o.mesh.numfaces) as string)catch("--") sub_li = li.ListSubItems.add() sub_li.text = (o.material) as string ) ) activeXControl lv_objects "MSComctlLib.ListViewCtrl" width:490 height:190 align:#center on listview_rollout open do ( initListView lv_objects fillInSpreadSheet lv_objects ) on listview_rollout close do callbacks.removeScripts #selectionSetChanged id:#SceneListView
Whenever the user closes the dialog by either pressing the [X] button in the upper right corner of the titlebar or by calling the MacroScript again, we will have to make sure that the callback we will register to update the display when the selection set changes is removed. Otherwise, selecting objects in the scene would cause the callback to try to access an already closed rollout and throw an error!
) try(destroyDialog listview_rollout)catch() createDialoglistview_rollout 500 200 callbacks.addScript #selectionSetChanged "listview_rollout.fillInSpreadSheet listview_rollout.lv_objects" id:#SceneListView
Finally, we register the callback to update the Listview whenever the user changes the selection set in the scene.
The callbacks.addScript function tells MAXScript that we are registering a new callback.
#selectionSetChanged is the name of the notification message broadcast by 3ds Max whenever the selection changes.
The string contains the actual script executed when the callback is activated, it accesses the Listview as a property of the now global rollout definition and calls the fillInSpreadSheet function we defined.
The id:#SceneListView is a user-defined name to be able to affect only this special callback (for example in the callbacks.removeScripts call above) without affecting other callbacks defined by other developers or the shipping 3ds Max version itself.
After evaluating the script, the SceneListView ActionItem in the "HowTo" category defined by the first part of the tutorial will be updated.
Press the button/select the menu item or press the keyboard shortcut corresponding to the SceneListView script - the dialog with the listView ActiveX control should appear. If there are no objects selected, the list will be empty.
Now select some object and watch the list updating automatically.Change the selection set again and the list will update dynamically! Select an object, right-click, go to Properties... and uncheck the Renderable checkbox. Note that the checkbox in the Listview will be unchecked, too!
The next logical step would be to add an even handler to link the checkbox in the Listview bi-directionally to the .Renderable property, so changing the state in the Listview would affect the node property, too!
Other things to try include adding more column definitions to the layout array to show other object properties you might be interested in.