Match .NET controls with a custom ui in 3ds Max

When developing scripts for user one goal is to make the script interface look nice to the user. Though it’s not the most important thing, it helps the user feel more confident using the script. I’m using a method to match colors in my script interfaces, especially in .NET controls, with a custom ui color. I’d like to share that here.

.NET controls

.NET controls are a great way to extend functions in your ui’s. Though they don’t always blend nicely with the rest of the interface. Mostly it’s just about matching colors. If you want something more custom, take a look at these maxcontrols by Rotem Schiffman.

.NET controls don't always blend nicely.
.NET controls don’t always blend nicely.

Background color

Users can customize their user interface in 3ds Max, also the colors. These colors are stored in either an ini-file or an xml-file depending on the max-version. Getting the background color from these files is not that straightforward. I’ve bundled the method I’m using in a small demo-rollout.

These controls have a label which blends in nicely
These controls have a label which blends in nicely

Here’s the method I’m sharing

function fn_getRolloutBackgroundColor type:#dotnet =
(
    /*<FUNCTION>
    Description
        gets the rollout background color from the current cui color file, or uses a default value if the user hasn't customized the colors before
        Supports the clrx <xml> and clr <ini> files.
        the colors are stored as an an integer in these files which can be converted by dotnet to a normal color value
    Arguments
        <name> #type: the type of output color #max | #dotnet
    Return
        <color> the background color
    <FUNCTION>*/
    
    --The custom colors are stored in a file. This file is named in the max ini file.
    local clrxFilePath = getIniSetting (getMAXIniFile()) "CuiConfiguration" "ColorFileName" --max 2013 and up
    local clrFilePath = getIniSetting (getMAXIniFile()) "CustomColors" "FileName" --max 2012 and down
        
    --try to get the background color value from either colorfile
    local theDecimalColor = case of
    (
        (doesFileExist clrxFilePath): --colors are stored in an xml format for max 2013 and up 
        (
            dotnet.loadAssembly "system.xml"
            local xmlDoc = dotNetObject "system.xml.xmlDocument"
            xmlDoc.load clrxFilePath

            local theStoredValue = xmlDoc.selectSingleNode "//ADSK_CLR/CustomColors/color[@name='Background' and @category='Appearance']/@ColorRef"
            theStoredValue.value as integer 
        )
        (doesFileExist clrFilePath): --colors are stored in an ini-format in max 2012 and down
        (
            local theStoredValue = getIniSetting clrFilePath "CustomColors" "Color_0"
            theStoredValue as integer
        )
    )
    
    if classof theDecimalColor != integer do theDecimalColor = 4473924 --if the user hasn't customized the colors before, we use a default value, that's rgb 68 68 68 from the dark theme
    local theTransparentColor = (dotnetclass "system.Drawing.color").FromArgb theDecimalColor --it's transparent by default, we don't want that since it turns the control white
    local theColor = (dotnetclass "system.Drawing.color").FromArgb 255 theTransparentColor.b theTransparentColor.g theTransparentColor.r --make a new color based on the old one but fully opaque
    if type == #max do theColor = color theColor.r theColor.g theColor.b --convert the dotnet color to a 3dsmax color value if needed
        
    theColor
)

3 Comments

Join the discussion and tell us your opinion.

  • 2014-06-23 at 20:18

    Hi Klaas,

    I use something different like:

    local backColor = colorMan.getColor #background
    local ColVal = color (backColor[1] * 255.) (backColor[2] * 255.) (backColor[3] * 255.)
    local textColor = colorMan.getColor #text
    local txtColVal = color (textColor[1] * 255.) (textColor[2] * 255.) (textColor[3] * 255.)

    In a control I do something like

    BackColor = (dotNetClass “System.Drawing.Color”).FromArgb (theBackColor.x * 255.) (theBackColor.y * 255.) (theBackColor.z * 255.)

    The 255 mult. is so I can also use the 255 values for regular max components, but you could leave that.

    Am I missing something over your solution?
    Thanks!
    -Johan

  • 2014-06-23 at 22:03

    doh! I never have used the colorman interface before so it’s really a blind spot!
    Your code seems to do the same and is a lot cleaner. Maybe there’s a slight difference because I’m taking the color from a file on disk and you’re taking it from a struct in max. I think they’d give the same results though.

    I’m still using (and extending) your rolloutsettings system by the way. Works great!

  • 2014-06-30 at 17:05

    No worries, blind spots all around 🙂

    I’d be interested to see what you extended to the rolloutsettings! If you can share, I’d love to see it, been dabling some with it too, maybe we could put on github or something..

Leave a reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.