[Closed] DotNet Form – Problem with local scope and PBlock
Hello,
I have a scope issue with using a dotnet maxform within a scripted custom attribute. Usually, a rollout created with the attribute can be flaoted as a UI whilst keeping the connection to the parameter block of the plugin. Id ideally like to replace the maxrollout with a dotnet form as there are some nice resizing and anchoring features built in.
Problem seems to stem from the fact that the form creates fine, but I can’t add any event handlers in the local scope. The dotnet ui code run outside the attribute plugin creates the events fine. I really need the Pblock to be visible to the form too without having to make things global. Here is an example, albeit without a pblock to be added to a scene node.
test_Attr = attributes EventTest
(
fn OnLoad sender args =
(
print "LoadEvent"
)
fn UI =
(
dnUI = dotNetObject "MaxCustomControls.MaxForm"
dnUI.ClientSize = dotNetObject "System.Drawing.Size" 118 156
dnUI.FormBorderStyle = (dotNetClass "System.Windows.Forms.FormBorderStyle").SizableToolWindow
dotNet.addEventHandler DnUI "Load" OnLoad
dotNet.setLifetimeControl DnUI #dotnet
dnUI.showmodeless()
)
rollout unnamedRollout "Untitled" width:162 height:33
(
button btn1 "floater" pos:[9,4] width:147 height:25
on btn1 pressed do ui()
)
)
custAttributes.add (modPanel.getCurrentObject() ) test_Attr
I havent really used dotnetform within max for this reason, prefering dotnetcontrol with standard maxrollout as i do a lot of UI based embedded attibutes on characters.
Any workarounds or suggestions?
This is the same with any event handler that you are trying to add in the modifier panel. It just doesn’t work correctly. There are all sorts of limitations that I have run into when trying to use dotnet in the modifier panel in script plugins and CA defs.
What I do is fileIn a script that has the UI to float or run a function that builds it and launches it. Then store an instance of the modifier in the dialog that you have floated. When I do it from a file that I have fileIn’d I usually have that built as a struct and store an instance of the modifier in the struct. This way I can drive the parameters with out it being name dependent.
Hi Paul,
That’s good to know as I do the same at the moment with other tools – i actually run a struct version of the interface from the network. All the character’s attributes point at this file in case i need to tweak the functionality of the UI over the course of production.
I do like the neatness of the rollout/pblock link in plugin scope though, i thought i’d check as it seemed a more elegant way of operating.
Hi Pete and Paul,
The 3ds Max SDK documentation at the page “Using .NET for User Interface” has this note:
3ds Max does not support hosting .NET UI controls directly in the 3ds Max application window.
That’s why it’s tricky to use them inside scripted plugins, rollups etc.
The best way to use .NET controls in Max is inside a MaxForm. At the same page of the SDK documentation, it is written:
There are some compatibility issues in handling window events correctly between the native 3ds Max application and any managed Forms opened from it. The specialized handling required to work around these compatibility issues is encapsulated in the MaxCustomControls.MaxForm class, which is derived from System.Windows.Forms.Form. To benefit from this compatibility support, any .NET plug-ins using a managed UI should host their controls in a MaxCustomControls.MaxForm window instead of the .NET Framework standard Form class.
So we’ll have to wait a complete .NET Forms UI, I hope in a future 3ds Max version with XBR UI improvements.
hi Yannick,
actually, i will clarify – I haven’t used the filein UI method with a maxform/dotnetform, only with dotnetcontrols on a max rollout. With a maxform, It actually doesn’t work at at all.
It seems to always evaluate the dotnetform in local scope to the attribute call, not global. If you save the following into your scripts directory as fileIn.ms
(
fn OnLoad sender args = print "Fired Load Event!!"
dnUI = dotNetObject "MaxCustomControls.MaxForm"
dnUI.ClientSize = dotNetObject "System.Drawing.Size" 118 156
dnUI.FormBorderStyle = (dotNetClass "System.Windows.Forms.FormBorderStyle").SizableToolWindow
dotNet.addEventHandler DnUI "Load" OnLoad
dotNet.setLifetimeControl DnUI #dotnet
dnUI.showmodeless()
)
if you run this script, you should get a maxform and the printed output in the listener.
then add this attribute to an object and press “floater”, you get zip.
test_Attr = attributes EventTest
(
rollout evtest "" width:162 height:33
(
button btnfloat "floater" pos:[9,4] width:147 height:25 border:false
on btnfloat pressed do filein (getdir #scripts + "/FileinUI.ms")
)
)
custAttributes.add (modPanel.getCurrentObject() ) test_Attr
Running the FileinUI.ms works okay on it’s own. However, the loadevent does not fire when called via filein on the attribute.
This seems to be different to maxrollouts, which fire in the correct scope.
I think despite odd UI redraw flashes, the best way to use dotnet components within max is to use a maxrollout and dotnetcontrol IMHO.
You are then at least able to tie the events in explicitly within the attribute.
Working with a struct between the custom attribute and the MaxForm would be the best in your case I think.
Yes, it’s the best way to insert a .NET control in a MAXScript rollout for display in main Max window (scripted attributes, plugins, utilities etc.) because it’s the only way…
Also as you were speaking about PBlocks, you can’t link a PBlock parameter and a .NET control.
I had some compatibility issues between .NET controls and MAXscript rollout. For example XtraTreeList control and its columns chooser dialog that raises an error at open.
Cheers yannick,
Even calling a global struct from a scripted attibute has the same conclusion, zero events. Boo.
Really, I thought that I had done that. Then again I’m almost always using a script plugin modifier or object. I wonder if that makes a difference.
I’ll ask Mat to jump in here as he is just writing something for us that will be doing this exact thing so it is more fresh in his mind.
Mat?
Maybe I’m being a bit dim. Are you calling the filein form code from the body of the struct itself? I’m not at my computer anymore but that would make sense as the struct is global it would run correctly. I am instantiating a global variable to a struct but from within the attribute. Perhaps that’s the difference. Thanks for chiming in on this everyone, BTW
Hey guys,
I’m still a bit green with dotNet, but I was running into this issue just the other day. I’m still a bit confused about exactly what is going on with this issue, but I was able to transfer the exact code that was not working inside my modifier plugin over to a struct and it worked fine when running in the struct.
What things look like for me: I have a regular maxscript rollout using createDialog. A big dotNet label on the rollout as a dotNetControl. The event handlers would work for this label. This label then has a number of smaller labels embedded inside of it as dotNetObjects, and these labels event handlers would not work when the code existed inside the scope of the modifier plugin.
I don’t have it yet where it does a fileIn on the struct script, I have it currently so an instance of the struct already exists and when the rollout is created that hosts the big dotNet label I stuff the instance of the modifier plugin inside of the global struct. This seems to work fine for me now.
Out of curiosity though, when everything worked fine, I decided to try stuffing an instance of the rollout back into the instance of the modifier after it had been created using the struct. This actually seemed to once again break the event handlers of the embedded label controls. I thought this was pretty strange since I don’t think anything had really changed.
Once I got it working I continued forward with developing the actual tool. Although, now I feel like going back and playing with it a bit more.
I have called the file in from the plugin modifier and then launched the UI from the struct, then place an instance of the modifier in the struct that called it. This way the struct knows which modifier opened it.
Yeah, this is what I have right now, except for the fileIn.
The only thing I currently have remaining inside the actual modifier plugin is a rollout with a button to launch the rollout that has all the dotNet goodness.