This is great.
The more I become familiar with the help file the easier and more smooth things will develop as well.
I’ll go ahead and make another round of developments over the weekend and the beginning of next week before my next post.
I’ll have some new things and ideas to try and conquer.
Till next week!
Have a good weekend guys.
JokerMartini
Nice work John, looks like you’re getting up to speeds with dotNet very quickly Looking forward to the results you post up soon.
Now I’m wanting to combine these two ideas.
This first script snippet is the main base of the script.
The second snippet is the new addition I want to mix into the first. I want to use the second snippet to create all the children buttons so they are vertically aligned.
Special thanks to Paul Neal for the second snippet of code.
Snippet 1
global exampleDotNetTool
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 =
(
--//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
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 74 134 230),(color 119 187 68),(color 203 73 73))--//Blue,Green,Red,
newGroup = #("Button", groupName,groupColor[i], 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[i], 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
for b in childBtns do b.visible = false
initializeForm()
),
init_trigger = initializeControls() --auto-initialize the controls on struct creation
)
::exampleDotNetTool = exampleDotNetToolStr()
Snippet 2
try(form.close())catch()
Ccolor = dotnetclass "system.drawing.color"
bt=dotNetObject "button"
bt.flatStyle=bt.flatStyle.flat
bt.backColor=bt.backColor.blue
bt.flatappearance.bordercolor = Ccolor.blue
bt.flatappearance.mouseoverbackcolor = Ccolor.lightblue
bt.flatappearance.mousedownbackcolor = Ccolor.blue
bt.size=dotNetObject "system.drawing.size" 24 64
bt.location=dotNetObject "system.drawing.point" 10 10
font=dotNetObject "System.Drawing.Font" "Arial" 8
brush=dotNetObject "System.Drawing.SolidBrush" (dotNetClass "system.drawing.color").white
fn btPaint sender arg=
(
--Set a variable to make it easier to use for the graphics engine.
g=arg.graphics
--Move the starting point of the text over and down.
g.TranslateTransform 6 58
--Rotate the text -90 degrees.
g.RotateTransform -90
--Draw the text.
g.DrawString "Button" font brush (dotNetObject "system.drawing.pointF" 0 0)
)
dotNet.addEventHandler bt "paint" btPaint
dotNet.setLifeTimeControl bt #dotNet
form=dotNetObject "form"
form.controls.add bt
form.show()
Looks nice. By the way you may want to have a look at some other button controls that ship with 3ds Max. the default ones are OK but they don’t exactly take on the max theme.
Autodesk includes a few dll’s created by devExpress that are well worth checking out.
Try playing with this:
btn1 = dotNetObject "DevExpress.XtraEditors.simpleButton"
You can get really nice looking buttons with these settings that fit in with max 2010+:
btn1.lookandfeel.UseDefaultLookAndFeel = false
btn1.lookAndFeel.style = btn1.lookAndFeel.style.ultraFlat
btn1.imageList = --Add imageList here for icons
btn1.backGroundImageLayout = btn1.backGroundImageLayout.center
btn1.appearance.backColor = (dotNetClass "System.Drawing.Color").transparent
btn1.allowFocus = false
These look very slick.
I like the appearance of these buttons. Thanks Tim
It seems to bug out when I run the code on this line.
“btn1.lookAndFeel.style = btn1.lookAndFeel.style.ultraFlat”
btn1 = dotNetObject "DevExpress.XtraEditors.simpleButton"
btn1.lookandfeel.UseDefaultLookAndFeel = false
btn1.lookAndFeel.style = btn1.lookAndFeel.style.ultraFlat
btn1.imageList = --Add imageList here for icons
btn1.backGroundImageLayout = btn1.backGroundImageLayout.center
btn1.appearance.backColor = (dotNetClass "System.Drawing.Color").transparent
btn1.allowFocus = false
hForm = dotNetObject "System.Windows.Forms.Form"
hForm.controls.add btn1
hForm.topmost = true
dotNet.addEventHandler mButton "click" whenButtonIsPressed
--Finally display the image
hForm.show()
I wish the vertical aligning of the buttons was as easy as just having a rotate property on the button itself.
Why is the form dialog different from the dialog being created within the struc?
Is there limitations to one over the other.
Look at the line below that. It is asking for an imageList but i didn’t provide one. Get rid of that line if you don’t want to add an icon imagelist. If you do add an imageList you will also need to tell it what imageIndex icon to use as well.
None than I can find which is a shame. I believe they included the devexpress dll’s to create the scene explorer, which uses a devexpress treelist:
dotNetObject "DevExpress.XtraTreeList.TreeList"
Search your 3ds Max install dir for “devExpress” and it will show a few dll’s that Autodesk have included since version 2009 I believe.
Edit:
Although there is no documentation from Autodesk, there is a HUGE amount of documentation on the devexpress website. If I’ve ever had a problem the answer has usually been found in their online support ticket forum. http://www.devexpress.com/
That’s just insane! They give us this incredible set of tools for free but fail to inform us of their existence for 4 years!
What would be the equivalent of a subrollout using dotNet? I want to try and do something like this next.
When you click the Offset the script would do what is the equivelant of a
fileIn (in maxscript) and those controls would then be loaded into this panel where the user could adjust the desired settings and options before applying the changes to the scene. Keeping in mind that these other *.ms files would be written only for use within this master script. You wouldn’t be able to go file>run>offset.ms and it work properly.
In this case the fileIn *.ms would consist of something similar to this…which is just ui controls and whatnot. If it makes more sense I can create an example of what I’m after using maxscript. Just let me know.
form=dotNetObject "form"
nud=dotNetObject "System.Windows.Forms.NumericUpDown"
nud.DecimalPlaces=2
nud.Increment=.1
nud.maximum=10
nud.minimum=-10
nud.ReadOnly=false
enableAccelerators=false
showProperties nud
form.controls.add nud
form.show()
I went ahead and made an example file.
Just extract and check it out. Run the masterDialog.ms
Firstly, an unrelated comment on your example:
The first time you run the script you will get an exception (start max afresh and run masterDialog.ms). This is because of scope issues.
Consider the following two lines:
fileIn "rlOffset.ms"
AddSubRollout rlMasterDialog.srContainer rlOffset
The exception occurs because rlOffset is undefined. Intuitively, you’d expect it to be fine as you just defined it a line before within the rlOffset.ms script.
Unfortunately, this is not how maxscript works. Upon entering the event handler, it evaluates all the variable withing the handler, and determines not their value, but what they are referring to.
So when the event handler is entered, if tries to determine what the variable rlOffset is, and does not find any such variable, so it determines it is an undefined local variable.
You must specifically tell maxscript to look for this variable in the global scope. There are two ways to do this:
- explicitly declare the variable global: (“global rlOffset”)
- use the special :: prefix to declare it global:
fileIn "rlOffset.ms"
AddSubRollout rlMasterDialog.srContainer ::rlOffset
A more technical explanation on how maxscript handles scope is given by Bobo in a thread I can’t find at the moment.
To try to answer your actual question, A possible solution is to declare a panel control within the files, containing all the child controls, and simply use the form.controls.add and form.controls.remove to switch between them.
This is what I did recently in the stamping module of my frame buffer tool, you can see how it works there if you wish (though it’s implemented in c# there).
I think the optimal method of implementation and structure depend on the intended use of the subpanels.
Thank you lo for the explanation of things. I’ll look into you frame buffer and see what is written and how its written.
I’m going to do some tests on the remove and add as you mentioned above as well.
I think I had a semi-revelation today on grasping and understanding the help file for this stuff.
I built out a ui today and I was wondering if you could take a look at it and let me know how or what would be a good way to condense or better write the code. It seems a bit redundant and maybe there is an easier way to make a table or form with dropdown lists in the rows.
All the dropdown lists consist of variable defined or gathered using functions.
check it out.
fn dotnetcolor r g b = (
(dotNetClass "System.Drawing.Color").fromARGB r g b
)
TheFont = dotnetobject "System.Drawing.Font" "Arial" 9
lbProjects = dotnetobject "System.Windows.Forms.label" --width:130 height:24
lbProjects.bounds=dotNetObject "system.drawing.rectangle" 0 0 130 24
lbProjects.forecolor = lbProjects.forecolor.fromARGB 255 255 255
lbProjects.backcolor = lbProjects.backcolor.fromARGB 31 29 28
lbProjects.TextAlign = (getProperty lbProjects.TextAlign "MiddleLeft")
lbProjects.text = " Projects"
lbProjects.font = TheFont
lbProjects.location = dotNetObject "System.Drawing.Point" 15 15
dlProjects = dotnetobject "System.Windows.Forms.ComboBox"
dlProjects.bounds=dotNetObject "system.drawing.rectangle" 0 0 130 24
dlProjects.location = dotNetObject "System.Drawing.Point" 15 39
projectsArr = #("Nice","Cool","Bunnies")
dlProjects.items.addrange projectsArr
lbWorkspaces = dotnetobject "System.Windows.Forms.label" --width:130 height:24
lbWorkspaces.bounds=dotNetObject "system.drawing.rectangle" 0 0 210 24
lbWorkspaces.forecolor = lbWorkspaces.forecolor.fromARGB 255 255 255
lbWorkspaces.backcolor = lbWorkspaces.backcolor.fromARGB 31 29 28
lbWorkspaces.TextAlign = (getProperty lbWorkspaces.TextAlign "MiddleLeft")
lbWorkspaces.text = " Workspaces"
lbWorkspaces.font = TheFont
lbWorkspaces.location = dotNetObject "System.Drawing.Point" 146 15
dlWorkspaces = dotnetobject "System.Windows.Forms.ComboBox"
dlWorkspaces.bounds=dotNetObject "system.drawing.rectangle" 0 0 210 24
dlWorkspaces.location = dotNetObject "System.Drawing.Point" 146 39
workSpaceArr = #("Here","There","Everywhere")
dlWorkspaces.items.addrange workSpaceArr
lbJobName = dotnetobject "System.Windows.Forms.label" --width:130 height:24
lbJobName.bounds=dotNetObject "system.drawing.rectangle" 0 0 100 24
lbJobName.forecolor = lbJobName.forecolor.fromARGB 255 255 255
lbJobName.backcolor = lbJobName.backcolor.fromARGB 31 29 28
lbJobName.TextAlign = (getProperty lbJobName.TextAlign "MiddleLeft")
lbJobName.text = " Job Name"
lbJobName.font = TheFont
lbJobName.location = dotNetObject "System.Drawing.Point" 357 15
dlJobName = dotnetobject "System.Windows.Forms.ComboBox"
dlJobName.bounds=dotNetObject "system.drawing.rectangle" 0 0 100 24
dlJobName.location = dotNetObject "System.Drawing.Point" 358 39
jobNameArr = #("James","Mike","Kevin")
dlJobName.items.addrange jobNameArr
lbFileType = dotnetobject "System.Windows.Forms.label" --width:130 height:24
lbFileType.bounds=dotNetObject "system.drawing.rectangle" 0 0 75 24
lbFileType.forecolor = lbFileType.forecolor.fromARGB 255 255 255
lbFileType.backcolor = lbFileType.backcolor.fromARGB 31 29 28
lbFileType.TextAlign = (getProperty lbFileType.TextAlign "MiddleLeft")
lbFileType.text = " File Type"
lbFileType.font = TheFont
lbFileType.location = dotNetObject "System.Drawing.Point" 458 15
dlFileType = dotnetobject "System.Windows.Forms.ComboBox"
dlFileType.bounds=dotNetObject "system.drawing.rectangle" 0 0 75 24
dlFileType.location = dotNetObject "System.Drawing.Point" 459 39
fileTypeArr = #(".exr",".tga")
dlFileType.items.addrange fileTypeArr
TheForm = dotNetObject "System.Windows.Forms.Form"
TheForm.controls.add lbProjects
TheForm.controls.add dlProjects
TheForm.controls.add lbWorkspaces
TheForm.controls.add dlWorkspaces
TheForm.controls.add lbJobName
TheForm.controls.add dlJobName
TheForm.controls.add lbFileType
TheForm.controls.add dlFileType
TheForm.bounds = dotNetObject "system.drawing.rectangle" 0 0 560 200
TheForm.backColor = TheForm.backColor.fromARGB 153 153 153
TheForm.show()
There is a way to make a table with comboboxes as cells, it’s called DataGridView.
If you have one row and only 4 comboboxes though, I find it a bit redundant, but I guess it’s a matter of taste.
Tips regarding your code:
- There’s no need to specify bounds and a location, the first two coordinates of the bounds rectangle are the location (X,Y,W,H).
- The “System.Windows.Forms” prefix is not necessary
- Use factory functions to create large amounts of similar controls.
Here’s the same code you wrote but somewhat shorter.
theFont = dotnetobject "System.Drawing.Font" "Arial" 9
fn labelFactory X Y W H fCol bCol txt =
(
local theLab = dotNetObject "Label"
theLab.Font = theFont
theLab.Bounds = dotNetObject "System.Drawing.Rectangle" X Y W H
theLab.BackColor = (dotNetClass "System.Drawing.Color").fromARGB bCol[1] bCol[2] bCol[3]
theLab.ForeColor = (dotNetClass "System.Drawing.Color").fromARGB fCol[1] fCol[2] fCol[3]
theLab.TextAlign = theLab.TextAlign.MiddleLeft
theLab.Text = txt
theLab
)
fn cmbBoxFactory X Y W H items =
(
local theCmb = dotNetObject "ComboBox"
theCmb.Bounds = dotNetObject "System.Drawing.Rectangle" X Y W H
theCmb.DropDownStyle = theCmb.DropDownStyle.DropDownList
theCmb.items.addRange items
theCmb
)
lbProjects = labelFactory 15 15 130 24 [255,255,255] [31,29,28] "Projects"
dlProjects = cmbBoxFactory 15 39 130 24 #("Nice","Cool","Bunnies")
lbWorkspaces = labelFactory 146 15 210 24 [255,255,255] [31,29,28] "Workspaces"
dlWorkspaces = cmbBoxFactory 146 39 210 24 #("Here","There","Everywhere")
lbJobName = labelFactory 358 15 100 24 [255,255,255] [31,29,28] " Job Name"
dlJobName = cmbBoxFactory 358 39 100 24 #("James","Mike","Kevin")
lbFileType = labelFactory 459 15 75 24 [255,255,255] [31,29,28] " File Type"
dlFileType = cmbBoxFactory 459 39 75 24 #(".exr",".tga")
TheForm = dotNetObject "Form"
TheForm.controls.addRange #(lbProjects,dlProjects,lbWorkspaces,dlWorkspaces,lbJobName,dlJobName,lbFileType,dlFileType)
TheForm.bounds = dotNetObject "system.drawing.rectangle" 0 0 560 200
TheForm.backColor = TheForm.backColor.fromARGB 153 153 153
TheForm.show()