Notifications
Clear all

[Closed] dotNet UI

 lo1

I don’t like the ‘execute’ function, when possible it should be avoided.

you could more easily store #(255,0,0) in the tag instead of (color 255 0 0) as string, then recreate the color as color s.tag[1] s.tag[2] s.tag[3]

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

you can easily store max color in tag as dotnetmxsvalue:


  control.tag = dotnetmxsvalue red
  print control.tag.value
  

ps. i usually store structures in control’s tag

That is a good idea.
Execute is usually a last resort of things…super last resort.

To better explain things…this is what I’m after and going for.

The ability to target one master folder for example

C:\User\Scripts
Then within it are subfolders such as Animation,Rendering,Tools
Those subfolders are (as lo put it) the parent buttons and the children are the .ms scripts.

It would be like taking this and converting it so it creates the buttons instead of an rc menu.
Just change the “dir” to a targeted directory of scripts and run it.


(
	local fullPath = undefined
	dir = @"C:\Users\Crabs User\Desktop	estScripts" --//Main Scripts directory
	dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
	foldersArray = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders

	local cm = dotNetObject "System.Windows.Forms.ContextMenuStrip"
	local cursor = dotNetClass "System.Windows.Forms.Cursor"

	for i in foldersArray do
	(
		item = cm.Items.Add i
		item.name = i
	)

	fn onPopup s e = 
	(
		for b = 1 to foldersArray.count do
		(
			i = (s.Items.Find (foldersArray[b] as string) off)[1]
			i.DropDownItems.Clear()
			
			sFiles = #()
			sFiles = getFiles (dirArray[b] + "*.ms*")
			
			fn fnRunScript s e = --Run the selected script 
			(
				fileIn (s.tag)
			) 
			
			if sFiles.count > 0 then --//Add the scripts to appropriate list
			(
				for n in sFiles do
				(
					scriptName = getFIleNameFile n
					item = i.DropDownItems.Add scriptName
					item.name = scriptName
					item.tag = n -- variable "n" contains the fullpath extention
					
					dotnet.addEventHandler item "Click" fnRunScript
				)
			)
			else
			(
				item = i.DropDownItems.Add "No Scripts"
				item.enabled = off
			)
		) 
	)
	dotnet.addEventHandler cm "Opening" onPopup

	on execute do
	(
		cm.Show(cursor.Position)
	)
)

Now I’m going to look into how to procedural add a button on creation. Which the equivalent of that in maxscript would be rci.addControl #button #myButton “My Button”

 lo1

That’s easy in dotnet… just a simple loop. No need for a rollout creator.

Got step one.
Building each button based on a loop. This main buttons being the parents.
Next is building each button and then its children buttons.

For the time being I just stored a string of names in the Array instead of putting a directory. Next version I’ll being referencing a directory though.


parentFolders = #("Animation","Rendering","Modeling") --Main Folders

form=dotNetObject "form"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
form.show maxHandle
form.bounds=dotNetObject "system.drawing.rectangle" 10 90 400 400
form.text="DotNet UI" 
	
for i = 1 to parentFolders.count do (	
	btn = dotNetObject "system.windows.forms.button"
	btn.Text = (parentFolders[i] as string)
	btn.Left = 10
	btn.Top = 10 + (i*24)
 	form.Controls.Add(btn)
)

I’m starting to like this dotNet stuff more and more. I feel like I’ll never want to build a ui using rollouts in max.

 lo1

A word of warning: currently any events you assign to your buttons will be lost with the first maxscript garbage collection. This is because there is no reference to them in maxscript, only in the dotnet form.

You have two possible solutions for this:

  1. in max2010 and up, use the dotnet.setLifeTimeControl method.
  2. add all dotnet controls that you need to keep events for in an array, ideally within a struct. See the example I gave earlier in this thread. This is my preferred method because it doesn’t limit your script to 2010 and up.

This issue has been discussed to exhaustion in other threads on this forum, in case you seek more information on it.

Here is the initial code which takes a targeted folder and scans its subfolders for scripts and puts a number value at the beginning of the name to signify which group its it. That is only for the purpose of testing for right now.

This is what I’ve got so far.
Any improvements or comments or suggestions are welcome!


--//Variables
dir = @"C:\Users\IEVFX\Desktop	rash\scripts" --//Main Scripts directory
dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
parentFolders = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders


form=dotNetObject "form"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
form.show maxHandle
form.bounds=dotNetObject "system.drawing.rectangle" 10 90 400 400
form.text="DotNet UI" 


--//Creates the Parent buttons
for i = 1 to parentFolders.count do (	
	btn = dotNetObject "system.windows.forms.button"
	btn.Text = (parentFolders[i] as string)
	btn.Left = 10
	btn.Top = 10 + (i*24)
 	form.Controls.Add(btn)
)


--//Creates children buttons
for b = 1 to parentFolders.count do
(
	sFiles = #()
	sFiles = getFiles (dirArray[b] + "*.ms*")
	
	if sFiles.count > 0 then --//Add the scripts to appropriate list
	(
		for n = 1 to sFiles.count do
		(
 			scriptName = getFIleNameFile sFiles[n]
			btn = dotNetObject "system.windows.forms.button"
			btn.Text = (b as string + "_" + scriptName as string)
			btn.Left = 90 * b
			btn.Top = 10 + (n*24)
			btn.tag = sFiles[n]
			print btn.tag
			form.Controls.Add(btn)
		)
	)
) 


Hey lo,
So I’ve built the script into the structural format like you mentioned.
This is what I’ve got to far.
Just change the dir variable to a directory with folders containing scripts and it should be good to go.
All dynamically created controls.

So if you guys want to check it out and let me know your thoughts about it that would be great. Thanks


global exampleDotNetTool

	--//Variables
	dir = @"C:\Users\IEVFX\Desktop	rash\scripts" --//Main Scripts directory
	dirArray = GetDirectories (dir+"/*") --//Get subfolders in directory
	parentFolders = for d in dirArray collect (trimright (filenameFromPath d) "\\") --//Array which stores just the names of the subfolders
	
struct exampleDotNetToolStr
(
	--// components
	form,
	parentBtns,
	childBtns,
	
	
	--// handlers
	fn parentClicked s e =
	(
		for b in exampleDotNetTool.childBtns do b.visible = b.tag == s.tag
	),
	
	fn childClicked s e =
	(
		format "child of group % pressed
" s.tag
	),
	
	
	--// initialization functions
	fn controlFactory type text backColor X Y W H tag =
	(
		local ctr = dotNetObject type
		ctr.text = text
		ctr.bounds = dotNetObject "System.Drawing.Rectangle" X Y W H
		ctr.backColor = (dotNetClass "System.Drawing.Color").fromArgb backColor.a backColor.r backColor.g backColor.b
		ctr.tag = tag
		ctr
	),	
	
	fn initializeForm =
	(
		form = dotNetObject "maxCustomControls.maxForm"		
		form.showInTaskbar = false
		form.text = "Dotnet Tool Example"
		form.bounds = dotNetObject "System.Drawing.Rectangle" 100 100 250 300
		form.controls.addRange parentBtns
		form.controls.addRange childBtns		
		local sysPointer = dotNetObject "System.IntPtr" (windows.getMaxHWND())
		local maxHandle = dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
		form.show maxHandle
	),

	fn initializeControls =
	(		
		local parentBtnDefs = #()
		local childBtnDefs = #()
		local pos = 10
		
		--//Create Groups
		for i = 1 to parentFolders.count do (
			sFiles = #()
			sFiles = getFiles (dirArray[i] + "*.ms*")
			
			groupName = (parentFolders[i] as string)
			groupColor = (color (i*50) 130 150)
			
			newGroup = #("Button", groupName,groupColor, pos, 10, 70, 30, i)
			append parentBtnDefs newGroup
			
 			--// Create children for each group
 			if sFiles.count > 0 do
 			(
				cPos = 40
				
				for n = 1 to sFiles.count do
 				(
 					ChildName = getFIleNameFile sFiles[n]
					newChild = #("Button", ChildName,groupColor, pos, cPos, 70, 30, i)
					append childBtnDefs newChild
					
					cPos += 30
 				)
 			)
			pos +=70
		)
		
		parentBtns = for b in parentBtnDefs collect (controlFactory b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8])
		for b in parentBtns do dotnet.addEventHandler b "Click" parentClicked		
		childBtns = for b in childBtnDefs collect (controlFactory b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8])
		for b in childBtns do dotnet.addEventHandler b "Click" childClicked
		initializeForm()		
	),

	init_trigger = initializeControls() --auto-initialize the controls on struct creation
)

::exampleDotNetTool = exampleDotNetToolStr()

 lo1

A few suggestions:

  • groupColor = (color (i*50) 130 150) will only work for 5 groups or less, otherwise the red component will go over 255 and dotnet will raise an exception.

  • You will probably want to initialize all child buttons to be invisible at startup.

  • You can also move the first 3 global variables (dir, dirArray, parentFolders) into the struct. The nice thing about encompassing a dotnet tool in a struct is that you don’t pollute the global space and avoid collisions with other scripts. For example, leaving the variable ‘dir’ as a global variable is bound to get you in trouble at some point or another.

Thank you for overlooking that and giving the suggestions.

As far as the color goes I’m going to end up doing an array of colors because I’ll know ahead of time how many groups there will be and I’ll want to control it.

ex.


groupColor = #((color 74 134 230),(color 119 187 68),(color 203 73 73))--//Blue,Green,Red

I appreciate all your help with this lo.

I set the visible state right above the initializeForm() after it adds the event handler.

for b in childBtns do b.visible = false

As far as the global variables going into the structure that is a great point to make. It makes complete sense and a good mention.
I’m sure many people have used dir as a variable. I use it quite often.

Next would be connecting the actual script to the button. In doing so I would just store that along with everything else in the tag property correct?

newGroup = #(“Button”, groupName,groupColor[i], pos, 10, 70, 30, i, FilePath)

then when the tag is referenced for the visiblity property I’ll just change it to reference part one of the array in the tag.

b.tag = (groupVisibility,“c:\users\destop\scripts\Trees.ms”)

Page 3 / 18