dotNet + MXS


#21

Yes it can be a solution.
Here is a way of creating a .Net form containing a TextBox that looks visually similar to a a MAXScript dialog. One of the main difference is that the form cannot get messages when the main Max window is minimized, maximized, resized etc. It’s always displayed even if Max is minimized for example.You can set the parent if you create this form in C++/CLI in a plugin but not in MAXScript (it would be interesting to create a small patch to the dotNet bridge…).

Here is the code:

(
	-- Create TextBox
	hTextBox = dotNetObject "System.Windows.Forms.TextBox"
	hTextBox.Location = dotNetObject "System.Drawing.Point" 10 10
	hTextBox.Width = 280
        hTextBox.Height = 280
	hTextBox.Visible = true
	hTextBox.MultiLine = true
	ScrollBars = dotNetClass "System.Windows.Forms.ScrollBars"
	hTextBox.ScrollBars = ScrollBars.Vertical
	hTextBox.AcceptsReturn = true
	hTextBox.AcceptsTab = true
	hTextBox.WordWrap = true
	
	-- Create Form
	hForm = dotNetObject "System.Windows.Forms.Form"
	hForm.Size = dotNetObject "System.Drawing.Size" 310 335
	hForm.Text = ".Net 2.0 Form with TextBox"
	hForm.Controls.Add(hTextBox)
	hForm.TopMost = true
	FormBorderStyle = dotNetClass "System.Windows.Forms.FormBorderStyle"
	hForm.FormBorderStyle = FormBorderStyle.FixedDialog
	hForm.ShowInTaskbar = false
	hForm.MinimizeBox = false
	hForm.MaximizeBox = false
	
	-- Set appropriate Form background color
	maxBackColor = colorMan.getColor #background
	Color = dotNetClass "System.Drawing.Color"
	hForm.BackColor = Color.FromArgb (maxBackColor[1] * 255.0f) (maxBackColor[2] * 255.0f) (maxBackColor[3] * 255.0f)
    
	-- Show application Form
	hApp = dotNetClass "System.Windows.Forms.Application"
	hApp.Run hForm
)

Also, App.Run() is better than Form.Show() because it initializes well the dialog and the keyboard handling. With Form.Show(), Max intercepts all the keyboard messages when writting in the TextBox…


#22

Hi !

I am following Erilaz’s suggestion from another thread about message boxes. He asked me to paste a small test of mine with .NET standard dialog boxes into this thread as well. It is very basic and self-explanatory and could be useful if one needs those “fancy” icons or more functionality than the standard MaxScript messageBox(). :slight_smile:

Overall I am just getting into the whole .NET thing and I really love it ! It is so easy to get more functionality without all the cryptic ActiveX stuff. I remember that we used a RegEx class as well in C# some time back. Imagine having regular expressions in MaxScript or easy Database access, XML writing/parsing in MAXScript. Yay !!! :bounce:

Ok so here is my humble contribution. I hope it helps someone get started… :slight_smile:

/*
 ----------------------------------------------------------
 System.Windows.Forms.MessageBoxButtons (Enumeration)
 ----------------------------------------------------------
 .AbortRetryIgnore
 .OK
 .OKCancel
 .RetryCancel
 .YesNo
 .YesNoCancel
 
 ----------------------------------------------------------
 System.Windows.Forms.MessageBoxDefaultButton (Enumeration)
 ----------------------------------------------------------
 .Button1
 .Button2
 .Button3
 
 ----------------------------------------------------------
 System.Windows.Forms.MessageBoxIcon (Enumeration)
 ----------------------------------------------------------
 .Asterisk
 .Error
 .Exclamation
 .Hand
 .Information
 .None
 .Question
 .Stop
 .Warning
 
 ----------------------------------------------------------
 MessageBox.show (Method)
 ----------------------------------------------------------
 MessageBox.show ( text, caption, buttons, icon, defaultButton )
 
 */ 
 
 (
 	-- setup dotnet classes / objects
 	local mb = dotNetClass "System.Windows.Forms.MessageBox"
 	local buttons = dotNetClass "System.Windows.Forms.MessageBoxButtons"
 	local icons = dotNetClass "System.Windows.Forms.MessageBoxIcon"
 	local defaultButton = dotNetClass "System.Windows.Forms.MessageBoxDefaultButton"
 	local dialogResult = dotNetClass "System.Windows.Forms.DialogResult"
 	
 	local result = mb.show "Do you want to save before quitting ?" "Quit" buttons.YesNoCancel icons.Information defaultButton.Button3
 	
 	-- evaluate result of messageBox (which button has been pressed by the user)
 	if ( result == dialogResult.Yes ) then
 	(
 		format "YES
"
 	)
 	else if ( result == dialogResult.No ) then
 	(
 		format "NO
"
 	)
 	else if ( result == dialogResult.Cancel ) then
 	(
 		format "CANCEL
"
 	)
 	
 )

Btw, is anybody using a version control system for their scripts ? I recently setup a SVN and Apache server and I organized all my scripts into a system-independent directory structure. This way I can check out everything that is needed ( encrypted over SSH ) wherever the work place is (of course web access is required). It is pretty practical and wonder now why I have not installed something like that for such a long time… No more synchronisation between different computers required.

Just as a hint for someone who ( like me ) struggled organizing their files ! :slight_smile: If someone has other thoughts about organizing their tools I would really love to hear how you handle it. No thread hijacking intended though.


#23

Here’s the code for displaying data in a listbox… But I still can’t use DataSource and DisplayMember… it simply doesn’t work, any help on that would be great!


(
 
rollout rolmain "Dotnet" 
 
(
 
 
 
dotNetControl lst_test "System.Windows.Forms.Listbox" width:250 height:150 align:#center
 
 
 
on rolmain open do
 
(
 
dotnet.loadassembly "System"
 
constring = "data source=.\SQLEXPRESS;Initial Catalog=YOUR_DATABASE_HERE;Integrated Security=True;Connect Timeout=30"
 
con = dotNetObject "System.Data.SqlClient.SqlConnection" constring
 
 
 
dataset = dotNetObject "System.Data.Dataset"
 
tableadapter = dotNetObject "System.Data.SqlClient.SqlDataAdapter""SELECT * FROM tasks" constring
 
tableadapter.fill dataset "tasks"
 
 
 
--lst_test.DataSource = dataset.tables.item["tasks"]
 
--lst_test.DisplayMember = dotNetObject "System.String" "taskname"
 
 
 
for i=0 to dataset.tables.item["tasks"].rows.count-1 do
 
(
 
lst_test.items.Add (dotnetobject "System.String" dataset.tables.item["tasks"].rows.item[i].item["taskname"])
 
)
 
)
 
)
 
 
 
createDialog rolmain 300 300
 
)
 


#24

Well I think I’ve found something, I’ve created a form and used the listbox there, with the datasource, displaymember etc etc and it worked just fine… seems like a limitation when using the listbox inside a rollout or something…


#25

This is all realy good information guys. Looks like dotNet has some limitations if you include them with Max rollouts. Right now my tool is to far along to go back as I have to get it done but I will try a full dotNet UI next time and see what can be done… or not.


#26

Does anybody know of a resource online that lists a whole gaggle of different .net interface items all in one script with a simple action for each? Kind of like in the Maxscript documentation with every single interface item.

I would like to redo the Volumes interface in .net but I don’t really know where to start. If I can see a working script I’m very adept at reverse engineering.


#27

There is information in the Max help that covers how to set up .net controls. Have a look for converting axcontrols to .net and you should find it.

To get a list of everything that you can do with .net you will need to visit MSDN and look for the .net class library. Specificly to locate the UI items you will need to look up system.windows.forms and go from there. Here is a direct link to it.

http://msdn2.microsoft.com/en-us/library/system.windows.forms.aspx

Do the help files in the Max scritp help and then start looking at MSDN and it will start to make some sense.


#28

I created a new thread about code management, I’m very interrested in your setup, as I use turtoise svn for my code.

-Johan


#29

Is it possible to use a visual .Net forms tool and then import that code into Max? A .net IDE of some sort?


#30

The code generated by a Form designer is in C# or VB.NET so you have to convert it to MAXScript. The conversion is generally easy to implement; cf. the code I posted above.


#31

Yeah that’s actually the part I’m having the most trouble with right now. I don’t really know C#, JScript or Vbasic so converting the syntax is challenging :smiley:

I did find this. And it’s handy. Some form items like radiobuttons are pretty obvious but what to call a spinner based on a picture is useful.

http://www.oreillynet.com/wireless/2004/01/07/graphics/image002.jpg

Anywhere I see “new” in C# or VJ# I should replace that with ‘dotnetobject’? Seems to be working as a general rule so far…

Edit:
Good DataGrid reference:
http://samples.gotdotnet.com/quickstart/aspplus/samples/webforms/ctrlref/webctrl/datagrid/doc_datagrid.aspx


#32

Spinner in .Net Forms are called NumericUpDown.

Yes, this is a good rule. And you have to use dotNetClass to get static classes, enumerations.


#33

Ok I’m really stuck.

I can’t for the life of me figure out how to insert columns (or rows) into a datagrid.

I did get tabs working but can’t figure out how to fill out my datagrid.

 
(
 Color = dotNetClass "System.Drawing.Color"
 ScrollBars = dotNetClass "System.Windows.Forms.ScrollBars"
 
 -- Create Tabs Container 
 tabControl1 = dotnetobject "system.windows.forms.TabControl"
 tabControl1.size = dotnetobject "system.drawing.size" 395 195
 tabControl1.Location = dotnetobject "System.Drawing.Point" 0 0 
 
 --Create Tabs
 tabPage1 = dotnetobject "System.Windows.Forms.TabPage"
 tabPage1.text = "Goodbye World"
 tabPage2 = dotnetobject "System.Windows.Forms.TabPage"
 tabPage2.text = "Hello World"
 tabPage1.backColor = color.fromArgb 200 200 200 
 tabPage2.backColor = color.fromArgb 200 200 200 
 
 -- Create Datagrid
 
 
 DGrid1 = dotnetobject "System.Windows.Forms.DataGrid"
 DGrid1.Location = dotNetObject "System.Drawing.Point" 10 10
 DGrid1.Width = 365
 DGrid1.Height = 120
 DGrid1.Visible = true
 
 
 -- Add Some Columns to the Datagrid
 dt = dotnetobject "system.data.datatable"
 Column1 = dotnetclass "System.windows.data.DataColumn"
 
 -- Create TextBox
 TB1TextBox = dotNetObject "System.Windows.Forms.TextBox"
 TB1TextBox.Location = dotNetObject "System.Drawing.Point" 10 10
 TB1TextBox.Width = 365
 TB1TextBox.Height = 120
 TB1TextBox.Visible = true
 TB1TextBox.MultiLine = true
 TB1TextBox.ScrollBars = ScrollBars.Vertical
 TB1TextBox.AcceptsReturn = true
 TB1TextBox.AcceptsTab = true
 TB1TextBox.WordWrap = true
 
 -- Create Buttons
 button01 = dotnetobject "System.Windows.Forms.button"
 button01.text = "goodbye"
 button01.Location = dotNetObject "System.Drawing.Point" 300 138
 
 -- Create Form
 TabForm = dotNetObject "System.Windows.Forms.Form"
 TabForm.Size = dotNetObject "System.Drawing.Size" 400 225
 TabForm.Text = "Title"
 TabForm.TopMost = true
 FormBorderStyle = dotNetClass "System.Windows.Forms.FormBorderStyle"
 TabForm.FormBorderStyle = FormBorderStyle.FixedDialog
 TabForm.ShowInTaskbar = false
 TabForm.MinimizeBox = true
 TabForm.MaximizeBox = false
 Tabform.helpbutton = true
 
 -- Add UI elements to Tabs
 tabpage1.controls.add(TB1textbox)
 tabpage1.controls.add(button01)
 tabpage2.controls.add(DGrid1)
  
 -- Add Tabs to Tab Container
 tabcontrol1.controls.add(tabpage1)
 tabcontrol1.controls.add(tabpage2)
 -- Add Tab Container to UI Form
 TabForm.Controls.Add(tabcontrol1)
 -- Show application Form
 TTApp = dotNetClass "System.Windows.Forms.Application"
 TTApp.Run TabForm
)


#34

There you go dude, I’ve managed to add columns and rows, although I figured out how to use the System.Data.Datarow to declare a row and ended up using the NewRow… but after that I could’t change the values as in something like row.items[“id”]=i that didnt work, dont know why… and I ended up creating an array and adding that up to the datatable, seems to work fine. Cya and if anyone knows how to declare that row thingie please explain!

 
(
 
dotnet.loadassembly "System.Data"
 
 
 
Color = dotNetClass "System.Drawing.Color"
 
ScrollBars = dotNetClass "System.Windows.Forms.ScrollBars"
 
 
 
-- Create Tabs Container 
 
tabControl1 = dotnetobject "system.windows.forms.TabControl"
 
tabControl1.size = dotnetobject "system.drawing.size" 395 195
 
tabControl1.Location = dotnetobject "System.Drawing.Point" 0 0 
 
 
 
--Create Tabs
 
tabPage1 = dotnetobject "System.Windows.Forms.TabPage"
 
tabPage1.text = "Goodbye World"
 
tabPage2 = dotnetobject "System.Windows.Forms.TabPage"
 
tabPage2.text = "Hello World"
 
tabPage1.backColor = color.fromArgb 200 200 200 
 
tabPage2.backColor = color.fromArgb 200 200 200 
 
 
 
-- Add Some Columns to the Datagrid
 
dt = dotnetobject "system.data.DataTable""Test"
 
Column1 = dotnetobject "System.Data.DataColumn" "id" (dotnetclass "System.Int32")
 
Column2 = dotnetobject "System.Data.DataColumn" "Name" (dotnetclass "System.String")
 
dt.columns.add Column1
 
dt.columns.add Column2
 
 
 
for i=0 to 2 do
 
(
 
row=#(dotnetobject "System.Int32" i,dotnetobject "System.String" (i as string)) 
 
dt.rows.add row
 
)
 
 
 
-- Create Datagrid
 
DGrid1 = dotnetobject "System.Windows.Forms.DataGrid"
 
DGrid1.Location = dotNetObject "System.Drawing.Point" 10 10
 
DGrid1.Width = 365
 
DGrid1.Height = 120
 
DGrid1.Visible = true
 
DGrid1.DataSource = dt 
 
 
 
-- Create TextBox
 
TB1TextBox = dotNetObject "System.Windows.Forms.TextBox"
 
TB1TextBox.Location = dotNetObject "System.Drawing.Point" 10 10
 
TB1TextBox.Width = 365
 
TB1TextBox.Height = 120
 
TB1TextBox.Visible = true
 
TB1TextBox.MultiLine = true
 
TB1TextBox.ScrollBars = ScrollBars.Vertical
 
TB1TextBox.AcceptsReturn = true
 
TB1TextBox.AcceptsTab = true
 
TB1TextBox.WordWrap = true
 
 
 
-- Create Buttons
 
button01 = dotnetobject "System.Windows.Forms.button"
 
button01.text = "goodbye"
 
button01.Location = dotNetObject "System.Drawing.Point" 300 138
 
 
 
-- Create Form
 
TabForm = dotNetObject "System.Windows.Forms.Form"
 
TabForm.Size = dotNetObject "System.Drawing.Size" 400 225
 
TabForm.Text = "Title"
 
TabForm.TopMost = true
 
FormBorderStyle = dotNetClass "System.Windows.Forms.FormBorderStyle"
 
TabForm.FormBorderStyle = FormBorderStyle.FixedDialog
 
TabForm.ShowInTaskbar = false
 
TabForm.MinimizeBox = true
 
TabForm.MaximizeBox = false
 
Tabform.helpbutton = true
 
 
 
-- Add UI elements to Tabs
 
tabpage1.controls.add(TB1textbox)
 
tabpage1.controls.add(button01)
 
tabpage2.controls.add(DGrid1)
 
 
 
-- Add Tabs to Tab Container
 
tabcontrol1.controls.add(tabpage1)
 
tabcontrol1.controls.add(tabpage2)
 
-- Add Tab Container to UI Form
 
TabForm.Controls.Add(tabcontrol1)
 
-- Show application Form
 
TTApp = dotNetClass "System.Windows.Forms.Application"
 
TTApp.Run TabForm
 
)
 


#35

This is sort of spinning off from another thread, but it seems best to discuss it here. Can we talk about overall dotNet declaration?

A standard way to access a method is to call it’s class:


  myFile = dotNetClass "System.IO.File"
  myFile.methodInQuestion
  

The tricky part is handling all the things that go along with that. For example, relief7 has a large list of local class declarations for interface elements before anything can be build with the messagebox .show() method:


  -- setup dotnet classes / objects
   	local mb = dotNetClass "System.Windows.Forms.MessageBox"
   	local buttons = dotNetClass "System.Windows.Forms.MessageBoxButtons"
   	local icons = dotNetClass "System.Windows.Forms.MessageBoxIcon"
   	local defaultButton = dotNetClass "System.Windows.Forms.MessageBoxDefaultButton"
   	local dialogResult = dotNetClass "System.Windows.Forms.DialogResult"
  

Is there a simple explanation of when you need to call something as an object, property or method as opposed to just accessing it via a call you made previously?

Seeing as it was an interesting exercise, here’s the thread that resolved my issue in regards to reading tick values from a LastWriteTime property:
http://forums.cgsociety.org/showthread.php?f=98&t=555276


#36

MessageBoxButtons, MessageBoxIcon, MessageBoxDefaultButton and DialogResult are enumerations so it’s not needed to create an object; just get their values. It’s the same for the static Show() method of a MessageBox.
A static member belongs to the class rather than to the objects of the class. Static members are also known as class members and non-static members are known as instance members.

dotNetObject is used in the same manner than C# constructor new() :

C# code

FileInfo fileInfo = new FileInfo(fileName);

Equivalent MAXScript code:

fileInfo = dotNetObject "System.IO.FileInfo" fileName

So, when a value is returned from a method, there’s no need to worry about its creation.


#37

I just discovered that in 3ds Max 2008 there’s a .Net SDK including an assembly that contains specific UI controls for use in Max. And a MaxForm class was developed so now we can use it in Max 2008 instead of standard “System.Windows.Forms.Form”.

My previously example with the .Net Form TextBox (http://forums.cgsociety.org/showpost.php?p=4722595&postcount=21) can now be coded as this under Max 2008:

(
	-- Load MaxCustomControls assembly
	-- This assembly contains UI components built on the .NET Framework for use in 3ds Max
	dotNet.loadAssembly "MaxCustomControls.dll"

	-- Create TextBox
	hTextBox = dotNetObject "System.Windows.Forms.TextBox"
	hTextBox.Location = dotNetObject "System.Drawing.Point" 10 10
	hTextBox.Width = 280
	hTextBox.Height = 280
	hTextBox.Visible = true
	hTextBox.MultiLine = true
	ScrollBars = dotNetClass "System.Windows.Forms.ScrollBars"
	hTextBox.ScrollBars = ScrollBars.Vertical
	hTextBox.AcceptsReturn = true
	hTextBox.AcceptsTab = true
	hTextBox.WordWrap = true
	
	-- Create Max Form
	hMaxForm = dotNetObject "MaxCustomControls.MaxForm"
	hMaxForm.Size = dotNetObject "System.Drawing.Size" 310 335
	hMaxForm.Text = "Max Form with TextBox"
	hMaxForm.Controls.Add(hTextBox)
	hMaxForm.TopMost = true
	FormBorderStyle = dotNetClass "System.Windows.Forms.FormBorderStyle"
	hMaxForm.FormBorderStyle = FormBorderStyle.FixedDialog
	hMaxForm.ShowInTaskbar = false
	hMaxForm.MinimizeBox = false
	hMaxForm.MaximizeBox = false
    
	-- Show MaxForm
	hMaxForm.ShowModeless()
)

All the UI events from Max main window (it’s the parent window of the form) are now correctly handled by the MaxForm.

This is a first example of the use of these assemblies primarly developed for use in plugin code but I think they are also very useful at scripting :).


#38

Autodesk guys wrote these assemblies for Scene Explorer which is mostly coded in C# but hooked into Max using C++/SLI.

Light


#39

I recently came across the problem of needing to let the artests in out studio select multiple files to perform operations on within my script.

I started out using the max getOpenFileName function, but soon discovered that it does not provide for multiple selections…a bit of a problem when dealing with a large number of files…So I thought I’d see what dn could do for me.

So after some time spent reading through the msdn docs, I came up with this…

local ofd = dotnetobject "System.Windows.Forms.OpenFileDialog"
 ofd.Filter = "XML Files (*.xml)|*.xml"
 ofd.RestoreDirectory = false			
 ofd.multiselect = true
  			
 if ofd.showDialog() == (dotNetClass "System.Windows.Forms.DialogResult").OK then (
   local sFile = ofd.fileNames
   -- Process the file array here...
 )

It seems to have worked quite well for what I need.

I hope it helps some one :slight_smile:

Shane


#40

I would like to compile a list of dotNet controls and classes that people are finding usful here and post it on my site with the test code that is being shown here. Does any one have a problem with me using the test code right from here so that I don’t have to rewrite it? I will post links to the origainal authors of the code if they have sites as well.

It would be good if we had a reference page of useful tid bits that others can start to reference.