Phone: 905 409-1589
Email: info@penproductions.ca
RSS LinkedIn Twitter Twitter
Tab Control
Navigate:
Overview:
Tab controls are used for breaking up your user interface (UI) into sections. This can be seen in my Rigging Utils script where I used the Active X tab control (not been updated to dotnet as of yet) to simplify the UI in the defined sections.

Dot Net tab controls work best when left in the horizontal format. For some reason when Microsoft wrote the Dot Net tab controls they didn't follow the conventions that were used in the Active X version. If you use the dotnet version in a vertical format it will produce a strange result. There are third party tab controls that can be used for this sort of setup.

Getting Started:
To add a tab control to a Max script rollout we will use the line

 

 

Code:
dotNetControl tabs "system.windows.forms.tabControl"
                                

To start the script we need to create a rollout with a sub rollout in it. The sub rollout will hold rollouts that will be associated with each of the tabs. When using a Max rollout instead of using a Dotnet dialog we can't directly place rollouts in the Tab Control so we will fake it will sub rollouts.

 

Code:
--Destroy the dialog if it is already open                               
try(destroyDialog testR)catch()

--Create a rollout 
rollout testR "Test" width:300
(
	--Add a dot net tab control
    dotNetControl tabs "system.windows.forms.tabControl" width:(testR.width-30) height:25 
    
    --Add a sub rollout. 
    subRollout subRoll "Sub" width:(testR.width-30) height:200 offset:[0,-5]
)
--Create the dialog. 
createDialog testR
                                

 

Result:

Initializing Tab Control:
I always start with a showProps function for dotNet objects so that I can easily print out any information about the control that I might need. The function takes one argument "obj" that is passed any dotNet object. We can use the command "showProps tabs" in the open event handler of the rollout to get all the properties we will need to get started.

 

Code:
	fn showProps obj=
	(
		clearListener()
		format "Properties:\n"
		showProperties obj
		format "\nMethods:\n"
		showMethods obj
		format "\nEvents:\n"
		showEvents obj
	)                                
                                

The intiTabs function will take two arguments, first the tab control that we want to initialize and then an array of names in the for of strings, one for each tab.

Once we have inspected the properties of the control we will be able to determine what properties need to be set. In the case of the tab control we first one to use a method that will clear any existing tabs. This is just in case we need to call an update on the tabs later in the script without rebuilding the whole UI.

The size mode needs to be set if we want to be able to control the width of the tabs. We are setting the size mode to fixed and then we set the itemSize using a dotNetObject called "system.drawing.size". This dotNet object takes two arguments, the x and y or width and height and is then used to set the itemSize. To know what sort of value needs to be passed to any given property all we need to do is print it to the listener and we will be able to see what type it is. We will use the width of the tab control divided by the number of tabs to be added and we also have to subtract a couple of pixels just so it doesn't get to close to the edge.

All we need to do next is to loop through the array of tab names and create a tab for each one. We access that through the tabPages property and use the .add method. Remember to check each property for more properties, methods and events using the showProps function as you go.

 

Code:
	fn initTabs tab labels:#() =
	(
		--Clear any existing tabs incase we do an update of them at some point. 
		tab.tabPages.clear()
		--Set the size mode so that we can control their width. 
		tab.sizeMode=tab.sizeMode.fixed
		--Set the width of every tab.
		tab.itemSize=dotnetObject "System.Drawing.Size" ((tab.width/labels.count)-2) 25
		
		--Loop through all the labels that we pass to the function and add a tab by the same name. 
 		for x in labels do tab.tabPages.add x
	)
                                

The current script will look something like this. In the on open event handler for the rollout we are calling the showProps function so that we know what we have to work with. This will be commented out in the final script. Then we are calling the initTabs function, passing it the tab control and a list of tab names.

 

Code:
--Destroy the dialog if it is already open 
try(destroyDialog testR)catch()

--Create a rollout
rollout testR "Test" width:300
(
	--Add a dot net tab control
	dotNetControl tabs "system.windows.forms.tabControl" width:(testR.width-30) height:25 
	--Add a sub rollout.
	subRollout subRoll "Sub" width:(testR.width-30) height:200 offset:[0,-5]
	
	--Function to show all properties, methods and events for any dotNet object
	fn showProps obj=
	(
		clearListener()
		format "Properties:\n"
		showProperties obj
		format "\nMethods:\n"
		showMethods obj
		format "\nEvents:\n"
		showEvents obj
	)
	
	--Tab initilization function.
	fn initTabs tab labels:#() =
	(
		--Clear any existing tabs incase we do an update of them at some point. 
		tab.tabPages.clear()
		--Set the size mode so that we can control their width. 
		tab.sizeMode=tab.sizeMode.fixed
		--Set the width of every tab.
		tab.itemSize=dotnetObject "System.Drawing.Size" ((tab.width/labels.count)-2) 25
		
		--Loop through all the labels that we pass to the function and add a tab by the same name. 
 		for x in labels do tab.tabPages.add x
	)
	
	--When the rollout opens initilize the tab control
	on testR open do
	(
 		showProps tabs
		
		initTabs tabs labels:#("Main", "Settings", "User")
		addTabRollout 0
	)
)
--Create the dialog
createDialog testR                                
                                

 

Result:

Event Handlers:
Now that we have the UI set up and the tabs initialized we can add the event handler that will track when a user has clicked on one of the tabs.

We need to find out what options for the event handlers that we have so again we can use the showProps function to discover that. Some of the most used event handlers are the mouse options, we will use the mouseUp option for this example.

 

Listener:
on <control_name> MouseUp <System.Windows.Forms.MouseEventArgs>e do ( ... )

This event handler is looking for the name of the tab control and then will also take two arguments. The first will hold a reference to the tab control and the second will return properties that can be used in the event. Lets write the handler and find out what those properties are and if we can use them.

Add the code below to the script below the initTabs function, run the script and then select one of the tabs.

 

Code:
	on tabs MouseUp senderArg arg do
	(
 		showProps arg
	)
                                

The results below should be printed to the listener. There is no direct reference returned here that tells us which tab has been pressed. We could use some of the information to get it like the "Location" and then from that we could find which tab is at that location. This would how ever be the long root. If check the properties of the tab control you can see that there is a property called "SelectedIndex", this will return to us an index value of the tab that was selected. Remember that dotNet uses 0 based arrays so the first one would have an index of 0.

 

Listener:
Properties:
  .Button : <System.Windows.Forms.MouseButtons>, read-only
  .Clicks : <System.Int32>, read-only
  .Delta : <System.Int32>, read-only
  .Location : <System.Drawing.Point>, read-only
  .X : <System.Int32>, read-only
  .Y : <System.Int32>, read-only
  .Empty : <System.EventArgs>, read-only, static

Methods:
  .<System.Boolean>Equals <System.Object>obj
  .[static]<System.Boolean>Equals <System.Object>objA <System.Object>objB
  .<System.Int32>GetHashCode()
  .<System.Type>GetType()
  .[static]<System.Boolean>ReferenceEquals <System.Object>objA <System.Object>objB
  .<System.String>ToString()

Events:
                                

Use the line below to print the index of the currently selected tab. "senderArg" is the first argument in the event handler and holds a direct reference to the tab control. We could also just use the line "tabs.SelectedIndex" but if we use the senderArg then it makes it easier to cut and paste the code for other tab controls. Now when a tab is selected it also prints the index of the selected tab to the listener.

 

Code:
	on tabs MouseUp senderArg arg do
	(
 		showProps arg
		print senderArg.SelectedIndex
	)
                                

Adding Rollouts:
We will need to add this function that uses a case statement to determine which rollout to create and add to the subRollout. Add the function below the showProps function in the script. The function will take one argument and that is the index of the tab that has been selected. I don't like to directly access the index of the tab control in the function since it can be reused and called in other ways if we pass the index to it.

The first thing the function does is remove any existing rollouts so that we don't get duplicates or multiple rollouts being shown at the same time.

The case statement then determines which rollout to create and add based on the index. Notice that I don't have a case of "0", we will use the default case instead, this way if the tab index is unknown it will just add the first one. If the index 0 is supplied it will not be able to find a case for it and still will use the default.

 

Code:
	--Add rollouts to the subRollout based on which tab is currently selected. 
	fn addTabRollout index=
	(
		--Remove any existing rollouts first. 
 		for x in subRoll.rollouts do removeSubRollout subRoll x
		
		sub=case index of
		(
			default:
			(
				rollout sub01 "Sub 01"
				(
					button testBt "test Button"
					on testBt pressed do (messageBox "You pressed the button" title:"Alert!")
				)
			)
			1:
			(
				rollout sub01 "Sub 02"
				(
					button testBt "test Button"
					on testBt pressed do (messageBox "You pressed the button" title:"Alert!")
				)
			)
			2:
			(
				rollout sub01 "Sub 03"
				(
					button testBt "test Button"
					on testBt pressed do (messageBox "You pressed the button" title:"Alert!")
				)
			)
		)
		addSubRollout subRoll sub
	)
                                

In the mouseUp event we can now call the addTabRollout and pass it the selectedIndex property of the control. Also to initialize the UI fully on open we also call the addTabRollout and pass it a value of 0 so that the default rollout is added to start with.

 

Code:
	--When the mouse button is released on a tab get the selected tab and pass it to our addTabRollout function. 
	on tabs MouseUp senderArg arg do
	(
-- 		showProps arg
		addTabRollout senderArg.SelectedIndex
	)
	
	--When the rollout opens initilize the tab control
	on testR open do
	(
-- 		showProps tabs
		
		initTabs tabs labels:#("Main", "Settings", "User")
		addTabRollout 0
	)
                                

 

Result: