Browsing"User Controls"

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.sortby=(dotnetclass”LoneRobot.UI.Character.XMLLayoutPanel+SortByOptions”).lastwritetime
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
Get
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

Sort by Hue, I should Coco

Nov 26, 2008 by     No Comments    Posted under: 3dsMax, DotNet, User Controls

Here is an update to the color chart article. Since custom UI controls are kinda my thang, I decided to make one for this. The above left is with the colors sorted by Hue (More about this below). On the right is the previous arrangement where they are sorted by name. The Hue sort isn’t perfect, but it does at least group similar colors together which makes it easier to pick rather than the random sort by name on the right. You pick colors generally by Hue, not by their name.

The advantages of building a custom control are a simpler maxscript implementation. As you can see below this is the code for building the ColorChart in 3ds Max –


dotnet.LoadAssembly "C:LoneRobotclasslibMaxColorControl.dll"
rollout ColorChart "LoneRobot MaxColorChart" width:340 height:610
(
dotNetControl LRColorChart "LoneRobot.UI.MaxColorControl" pos:[0,0] width:338 height:610
)
createdialog colorchart

Now that is minimal code! 😉

I was looking into using an IComparer interface for performing a Hue based color sort and found this article –

http://www.codeproject.com/KB/selection/ColorPaletteControl.aspx

Thanks goes to Ruud van Eeghem for this posting.

Rather than writing one from scratch, I liked his GDI+ method of generating the color swatches, and since it was in C# I converted it to VB so that I could use it. Once that was done I added a few extra properties and methods to the control, namely taking the picked pixel color and checking to see if it was a ‘named’ dotnet color. Once I had this I made a composite control to extend the functionality and provide the clipboard method seen before. Bingo!

Update –

After a nights’ sleep I realized I wanted a more compact version. The control still displays the color name, but not the full maxscript string. The full string is still copied to the clipboard. You could register a dockable window for this. The new control is embedded into the same assembly, just change the dotnetcontrol declaration to “LoneRobot.UI.MaxColorControlMini”

download script


Using Inheritance to build a custom control

Nov 19, 2008 by     No Comments    Posted under: DotNet, Maxscript, User Controls

One of the really good things about writing your own controls in Visual Studio is the ability to not actually write one. This is great because you don’t have to do any of the hard work, you just pick the control that is closest to the functionality you want, then inherit the class. This is much the same if as using extends in a scripted plugin. What this does is gives you a fully featured version of the control and allows you to build extra properties and methods for the bits you want to add, or override the existing properties to do something flash.

In this case, i’m not doing this at all, but I am just tailoring the button control to be integrated into 3dsMax in a more direct way. If you use a dotnetcontrol constructor to implement a dotnet button onto a MXS form, you will get a default instance of a windows.forms.button, which you then have to alter in the rollout’s load event. This isn’t problem really, but i wanted to see if it was possible to build a class that returned a button in the correct visual state from the word go, leaving very few, if any, things to do in the load event. The first property I wanted to implement was to return a flat button with a background color the same as 3dsMax’s default UI color. The other things I experimented with was the idea of using embedded resources to hold common UI icons, rather like the examples on the GDI+ article i wrote a while ago. I also wanted a one line method to align the text and image around in a logical way.

It doesn’t avoid the issue of having to perform some UI stuff in the rollout open handler, but it does most of what you need for you. You also avoid the flicker of the controls as they redraw themselves with the correct background color. If you still want to build MXS rollout rather than dedicated Dotnet rollouts this could be a reasonable solution. (Don’t forget, if you build a dotnet form with the MaxForm class in 3dsMax, the controls will take on the correct UI color automatically.)

Using DotNet Enums in MaxScript

With any custom class objects, it’s highly likely Enums will be used, as they are a way of organizing custom properties into easily recognizable names, rather like structs in MXS have members to describe elements so that you don’t have to resort to array elements with confusing index numbers.

To use an enum in 3DSMax, you use a plus symbol in between the class string and the enum type. therefore, in the maxbutton class, to get the enums used you would type the following –

(dotnetclass "LoneRobot.Controls.MaxButton+CustomIcons").NewDocument
(dotnetclass "LoneRobot.Controls.MaxButton+LayoutStyles").Top
(dotnetclass "LoneRobot.Controls.MaxButton+ColorPresets").MaxUIDark

you would then use this to set the custom properties in the class.

Here is the full class diagram of the Maxbutton Control –

I hope this article has been of interest to you. Please download the class library and try it for yourself. there is a basic MXS script that builds the rollout at the top of the page. You can explore trying the different enums for yourself!

download script

TrackBot – A Max Custom UI Control for Characters

Oct 27, 2008 by     No Comments    Posted under: 3dsMax, Characters, DotNet, Technical Research, User Controls

This control was made to try and build some UI shortcuts when using morph controls on a character. I wanted to be able to add a control that gave me the ability to reset, nudge and scrub the morph values. In the past I would have to build each button control in MXS and make sure they all worked.

With this control, I am hoping it will work well enough to use in a facial rig setup.

There are a few extras with this control of use. Firstly, the slider, once focused can be moved by rolling the mouse wheel. This makes it good for fine tuning expressions. And secondly, the label color can be changed so that you can visually group expressions together by type. The nudge buttons can be used with shift and control to nudge values of 10 and 5 respectively. I will add a property to set these soon, as you may wish to specify max and min values smaller than this.

The buttons are drawn with GDI+, with the exception of the key icon, which is embedded as a resource in the control. I would like the button images to fill with the control color eventually too.

Apart from the Valuechanged event, there is a setkey event that is fired every time – yup, you guessed it – the setkey button is pressed.

Here is the class diagram so that you can see all the properties and events-

  • BarColor – The color of the right hand side of the slider
  • ElapsedColor – The color of the left hand side of the slider
  • ResetValue – Specify a value for reset, in case your slider goes negative for example.
  • Title – The text on the control
  • LabelColor – the text background

the slider is a C# control from CodeProject. I have used it because it implemented the great mouse wheel functionality. I have mirrored some of the properties in the control to allow the user to change the slider ui colors. I am currently trying to convert the source to VB so that i can embed it within the control. You can see the article here

One thing i have noticed with running dotnet controls in the max command panel is panel seems to refresh after the controls, and subsequently makes them dissapear. The only way to get them back is to drag the command panel out and in again. While i am not exactly sure why this is happening, it can be fixed in a slightly hacky way by placing a timer with an interval of 1 in the rollout which ticks once, invalidating the controls via the refresh() method, like so –

rollout Trackbot "" width:170 height:249
(
dotNetControl TBslider1 "LoneRobot.Trackbot" pos:[1,3] width:156 height:54
dotNetControl TBslider2 "LoneRobot.Trackbot" pos:[1,61] width:156 height:54
dotNetControl TBslider3 "LoneRobot.Trackbot" pos:[1,119] width:156 height:54
dotNetControl TBslider4 "LoneRobot.Trackbot" pos:[1,174] width:156 height:54
timer refresh "" interval:1

on Trackbot open do refresh.active = true

on refresh tick do
(
TBslider1.refresh()
TBslider2.refresh()
TBslider3.refresh()
TBslider4.refresh()
refresh.active = false
)

)

Update!

Hello again, In my recent article about ColorMatrix, I included an update to my TrackBot slider assembly. Previously, it had included a C# slider component that I handled via a custom event. Using this method can be really useful, as you can make a composite control of many other existing elements. The UI is still the same although I changed the appearance of the slider slightly. (In the picture below, I’ve hidden the lower controls by making the UI height of the dotnetcontrol smaller)

Click the image if you want to read the ColorMatrix article, and get the code for Image adjustments in Max.

I’ve updated the assembly with a VB conversion of the slider component, which means that it is now integrated into one assembly. I’ve changed the way the event is triggered also, so let me know if it doesn’t function as you expect. As usual, it is a download below.

download script

BotNet Framework – Packing dotnet objects into functions for ease of use

Oct 26, 2008 by     No Comments    Posted under: 3dsMax, DotNet, Maxscript, User Controls


I wanted to script a library of functions to help me set up certain DotNet UI objects without having to type everything out each time i wanted to use them. I obviously could not resist the pun either. One of the methods of setting up a DotNetControl in 3DS Max in order to have a cohesive UI look you need to set multiple properties for each. Please note this is very much a work in progress. If you feel the need to add a function to this please do so, but be sure to let me know so that I can update it!

For example, to setup a button with a see-through background image, you can call the following botnet function –


fn imagebutton btn image style bgcolor mobg mdbg tcolor:undefined =
-- tcolor is transparent color to be passed to the imagelist
(
btn.backcolor = colorpreset bgcolor
btn.flatstyle = style
btn.FlatAppearance.MouseOverBackColor = colorpreset mobg
btn.FlatAppearance.MouseDownBackColor = colorpreset mdbg
if tcolor != undefined then (btn.imagelist = (imagelist image invisible:tcolor)) else (btn.imagelist =(imagelist image))
btn.imageindex = 0
image = nothing
)

The variable tcolor is passed to an imagelist control object and used as the mask color. In dotnet you can pass an image as the button background color, but in order to get a transparent image, you need to pass an imagelist.

Note that the easy way to do this is to use a 32 bit PNG for the button image. Windows will recognise the transparency information and render the button correctly. However, if you only have a 24 bit image, this method is the way, so that you can supply the background color as a matte.

When placed within the on open event of the rollout, the code looks like this. Note i am using an image with a white background in my scripts folder called “addremoveicons.bmp”



rollout DNimagebuttons "BotNet Button Methods()" width:190 height:220
(
dotNetControl btnclose "button" pos:[9,9] width:80 height:45
dotNetControl msgbox "button" pos:[9,70] width:175 height:45
dotNetControl msgbox2 "button" pos:[9,125] width:95 height:85

on DNimagebuttons open do
(
BotNet.imagebutton btnclose ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #maxactive #crimson tcolor:#white BotNet.imagetextbutton msgbox "A Message Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #slategray #maxactive #limegreen tcolor:#white fontcolor:#red textalignment:botnet.bnfalign.BottomRight imagealignment:botnet.bnfalign.TopLeft BotNet.imagetextbutton msgbox2 "or Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #yellow #limegreen tcolor:#white fontcolor:#purple textalignment:botnet.bnfalign.BottomCenter imagealignment:botnet.bnfalign.TopCenter
)

on msgbox click do
(
local msgstring = "Waheeeey, a funky dotnet button"
Botnet.messagebox " LoneRobot.BotNetFramework" msgstring
)

)
createdialog DNimagebuttons style:#(#style_toolwindow, #style_sysmenu)

Depending on which alignment option you provide can yield a variety of different button styles. I have set the button style to flat, i just prefer this but you could change it to system buttons should you prefer. At the moment it is limited to these UI functions, i hope in the future to add many more.

download script

you will need this logo in your scripts directory if you want to use the BotNet MessageBox function. You can obviously replace this with another image but if you want, right click this one and download…