XML LayoutPanel – Another Custom control for 3dsMax

Mar 24, 2009 by     No Comments    Posted under: DotNet, Technical Research, User Controls

Recently, I was using a FlowLayoutPanel to provide a UI to load XML presets on a Character Pose system. The FlowlayoutPanel is a really useful UI component available in the DotNet toolbox. It is basically a container for you to place other controls into, and as a result, is perfect for dynamic interfaces. The problem in Max is that DotNet event handlers need to be specified in GLOBAL scope in order for them to register. This creates issues when using them in as part of a custom attribute or modifier plugin, the very thing (regrettably) that I wanted to do.

The way around this to build your own class that inherits the FlowLayoutPanel and adds the handlers outside of Max. Since I was going to the trouble of doing this, I thought I’d add a couple of methods I’d previously put into my HitchHiker control. Here is the control in the interface –

The control to the right is a node storage system for all of the facial bones, and the left hand interface is the custom FlowLayoutPanel. Clicking a button gives access to the XML file where the stored transforms for various facial expressions are stored.

The Panel has a single method, populate <path string> ,which searches the folder and adds a button to the layout pane for each file it finds. It will also look for a related image. This is how I wanted to show the presets. If there is a png file with the same name as the file in the folder, it will put that on the button. If that isn’t present, it checks if there is a file called “Default.png”. This was so I could put a rough icon for the storage type if I wanted. If not, the default XML icon is used. There is a compact (pictured) and larger icon and it depends on the button size if the larger one gets used. Take a look at the class diagram below for more information on other properties you can set –

Setting up the control properties in 3dsMax

Here are some examples of setting properties using this class within 3dsMax – Note the namespace of the control –

dotNetControl XMLPanel “LoneRobot.UI.Character.XMLLayoutPanel” pos:[0,186] width:161 height:336

Also note the use of the + sign when using a custom enumeration in 3dsMax –

XMLPanel.thumbsize = dotNetObject “system.drawing.size” 140 30
XMLPanel.selectedborderwidth = 2
XMLPanel.border = true
XMLPanel.textalign=(dotnetclass “System.Drawing.ContentAlignment”).middleright
XMLPanel.imagealign=(dotnetclass “System.Drawing.ContentAlignment”).middleleft
XMLPanel.sortby=(dotnetclass “LoneRobot.UI.Character.XMLLayoutPanel+SortByOptions”).filename
XMLPanel.sortby=(dotnetclass “LoneRobot.UI.Character.XMLLayoutPanel+SortByOptions”).length
XMLPanel.populate DirectoryPath

Top of the Class

The more observant may have noticed that in the event section of the class diagram was an event called Picked. If you did, then award yourself a biscuit. Picked isn’t a standard dotnet event for the FlowLayoutPanel. It is in XMLLayoutPanel though, so what is this and how does it work?

You may already be familiar with the concept of Object Orientated Programming, or OOP. It’s kind of fundamental to working with DotNet, and getting a handle on it will really improve how you approach a task. If you’re not 100% straight on what all this OOP malarkey is, I’ll try to give a brief explanation. The DotNet framework is a big orchard of classes. Almost everything (with some exceptions naturally!) is a class in dotnet. One way to look at it is that if a class was a banana, the dotnetclass would be a blueprint for what a banana is. A dotnetobject is the actual banana. And there are multiple bananas.

Buttons are classes, Forms are classes, BackgroundWorkers are classes. Each time you use a dotnetobject, you are using an instance of that class.

Now this is all very straightforward so far, and is the sort of thing that really impresses girls when you talk to them about it.

Let’s go on by looking at what happens when you press a button in dotnet. In order to handle the event, you have an eventarg attached to it. This has relevant information pertaining to the button being pressed, like the x and y position of the mouse etc. However, on an XMLLayoutPanel, that sort of information isn’t really useful. What you need is something that will convey the data you need after the event is fired.

You’ll remember at the start that I talked about inheriting classes like the FlowLayoutPanel to make the XMLLayoutPanel. Where it gets more interesting is when you realize that the eventarg is also a class. This means you can inherit them also, and change the functionality to something you want to use.

For the XMLLayoutPanel, all that you really need is the path and filename to the XML file you have picked. Since the system.eventarg tied to a button press doesn’t have that kind of information in it, you can write a custom eventarg to provide it. Once you have defined the event name in Visual Studio, you will need to write the event handler class. Here is the setup of this in VB – don’t worry if it makes no sense, i’ll try to explain the process of what is going on.

Public Class XMLPickedEventArgs
Inherits EventArgs
Private _fileName As String
Public Property FileName() As String
Return _fileName
End Get
Set(ByVal value As String)
_fileName = value
End Set
End Property
Public Class XMLPickedEventArgs
Public Sub New(ByRef control As XMLLayoutPanel, ByVal fileName As String)
control.CurrentItem = fileName
Me.FileName = fileName
End Sub
End Class

XMLPickedEventArgs is created with two constructors, a reference to me ,the base control itself (not the sender in this case, which would be the button) and the Tag property of the button, which is the string path of the XML file. Now that this object has been instantiated, it means the XML filename has been stored in the eventargs filename property, and it can now be passed to raise the event.

If you look at this class in Visual studio, you will see that the control has a new custom registered event in the properties window that isn’t part of the FlowlayoutPanel –

Even better, Intellisense tells us the arguments in our custom class. As the class has just one property, that is all we see. However, this is the only one we need! So from this, we know that if we access the filename property of the eventarg, we will get the path to the XML file we just clicked. This load string can then be handled in max and passed to whatever XML function you wish.

You can do the same when using the class in max by calling showproperties on the eventarg in max – Notice the structure is the same, with sender being the custom control itself, and args being the eventarg. Depending on what you pass from the assembly you could do other things, for example , In the sender variable you could have passed a reference to the button from the event, meaning that you could use the event to change the state of it (i.e. backcolor, border etc), or you could keep the sender as the XMLPanel and add a property to the eventarg to refer to the button. It’s really up to you.

on XMLPanel picked sender args do
showproperties args
listener output –
.FileName : <System.String>
.Empty : <System.EventArgs>, read-only, static

In short, with Visual Studio you can build a control as broad or as specialized as you like. In this case, I really wanted something for this particular task, and was able to build it. The main advantage is it bypasses the event handler issues of the 3dsMax command pane. Of course this approach could be done for a variety of filetypes

I hope this has given you a background as to why you would write your own event handler, and how to use it in Max. Feel free to download the control and use it in your projects. I’m using this control more and more in the utilities I write; I think that it gives a good visual feel to the UI and allows for dynamic layouts to be made where the layout logic is completely handled by the control, not by you.

Which is nice.

download script

Got anything to say? Go ahead and leave a comment!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>