It’s coming along. Still very early, since there’s no control points, but this is the most stable it’s been so far.
macroScript laniCharacterStudioDotNet category:"Lani Scripts" buttonText: "Lani Character Studio DotNet"
(
/*
CREDIT WHERE IT'S DUE:
Most of this was taken from/started out as Paul Neale's excellent tutorials on Dotnet
http://paulneale.com/tutorials/dotNet/form/index.htm
Also many thanks to lo, denisT, JokerMartini and LoneRobot on cgsociety.org
*/
--Create a form and display it in Max.
--Read about this process in the form & MaxForm tutorial.
global laniCharStudio_DotnetVisible
if laniCharStudio_DotnetVisible == undefined then laniCharStudio_DotnetVisible = false
global laniCharacterStudioFormGlbl
global laniCharStudioLastPos
laniCharacterStudioForm=dotNetObject "MaxCustomControls.MaxForm"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
laniCharacterStudioForm.bounds=dotNetObject "system.drawing.rectangle" 10 90 410 512
laniCharacterStudioForm.text="Lani Character Studio - DotNet Drawing Edition(tm)"
borderStyle=(dotNetClass "System.Windows.Forms.FormBorderStyle").Sizable
selectionColor = ((dotNetClass "system.drawing.color").fromArgb 80 102 153 255)
charStudioBG = (dotNetclass "System.Drawing.Image").fromfile @"X:\3dsmax\2013\MaxScript\images\characterStudioBackground.png"
laniCharacterStudioForm.formBorderStyle=borderStyle
laniCharacterStudioForm.ShowInTaskbar = false
pBox=dotNetObject "pictureBox" --"system.windows.forms.pictureBox"
--Set the size and location of the pictureBox
pBox.bounds=dotNetObject "System.drawing.rectangle" 0 0 410 512
--Set the background color
pBox.backColor=(dotNetClass "system.drawing.color").gray
--Add the pictureBox to the form
laniCharacterStudioForm.controls.add pBox
--Rectangle and brush for painted box
boxRec=dotNetObject "system.drawing.rectangle" 10 10 100 100
boxBrush=dotNetObject "system.drawing.solidBrush" selectionColor
pBox.backgroundImage = charStudioBG
--Add the paint function
fn pBoxPaint sender arg=
(
--formatProps arg.graphics
--Create graphics object
g=arg.graphics
--Paint a filled rectangle into the pictureBox
g.fillRectangle boxBrush boxrec
)
--Add the paint event handler
dotNet.addEventHandler pBox "paint" pBoxPaint
mouseIsDown=false
movingWindow=false
mO = #(0,0)
--hold the starting position of the drag
dragStartX = -1
dragStartY = -1
--Add event handlers for the form.
fn formMouseDown sender arg=
(
--Check to see which mouse button is being pressed.
if arg.button==arg.button.left do (
--LEFT button is down
--formatProps arg;
mouseIsDown=true
dragStartX = arg.x
dragStartY = arg.y
)
if arg.button==arg.button.middle do (
print #middle
movingWindow = true
mO = #(arg.x,arg.y)
)
if arg.button==arg.button.right do (
--RIGHT button is down
print #right
)
)
fn formMouseUp sender arg=
(
mouseIsDown=false
movingWindow=false
laniCharStudioLastPos = #(laniCharacterStudioForm.Left,laniCharacterStudioForm.Top)
)
fn formMouseMove sender arg=
(
if mouseIsDown do
(
boxRec=NormalizeRectangle dragStartX dragStartY arg.x arg.y
pbox.refresh()
)
if movingWindow do
(
laniCharacterStudioForm.Left = mouse.screenpos.x-mO[1]
laniCharacterStudioForm.Top = mouse.screenpos.y-mO[2]
)
)
--Add the event handlers.
dotNet.addEventHandler pBox "mouseDown" formMouseDown
dotNet.addEventHandler pBox "mouseUp" formMouseUp
dotNet.addEventHandler pBox "mouseMove" formMouseMove
--Set the life time control
dotNet.setLifeTimeControl pBox #dotNet
--Force an update to the form to fire the paint event
if (laniCharStudio_DotnetVisible == false) then (
laniCharStudio_DotnetVisible = true
laniCharacterStudioForm.show maxHandle
laniCharacterStudioFormGlbl = laniCharacterStudioForm
if (laniCharStudioLastPos != undefined) then (
laniCharacterStudioForm.Left = laniCharStudioLastPos[1]
laniCharacterStudioForm.Top = laniCharStudioLastPos[2]
)
) else (
laniCharStudio_DotnetVisible = false
laniCharacterStudioFormGlbl.hide()
)
laniCharacterStudioForm.refresh()
)
This function is also needed, but I moved it to a separate file for clarity (since I will need more functions later)
fn NormalizeRectangle hX hY sX sY =
(
local X = amin sX hX
local Y = amin sY hY
local w = abs (hX - sX)
local h = abs (hY - sY)
dotnetObject "system.drawing.rectangle" x y w h
)
Results in the following (so a looong way to go – but progress, so yay!)
Hmm, I must be missing something – but how would I go about creating X drawings of ellipses using just one iterative function? My current code is this:
update: is this a good way to do it?
macroScript laniCharacterStudioDotNet category:"Lani Scripts" buttonText: "Lani Character Studio DotNet"
(
/*
CREDIT WHERE IT'S DUE:
Most of this was taken from/started out as Paul Neale's excellent tutorials on Dotnet
http://paulneale.com/tutorials/dotNet/form/index.htm
Also many thanks to lo, denisT, JokerMartini and LoneRobot on cgsociety.org
*/
--Create a form and display it in Max.
--Read about this process in the form & MaxForm tutorial.
global laniCharStudio_DotnetVisible
if laniCharStudio_DotnetVisible == undefined then laniCharStudio_DotnetVisible = false
global laniCharacterStudioFormGlbl
global laniCharStudioLastPos
laniCharacterStudioForm=dotNetObject "MaxCustomControls.MaxForm"
sysPointer=dotNetObject "system.intPtr" (windows.getMaxHWND())
maxHandle=dotNetObject "maxCustomControls.win32HandleWrapper" sysPointer
laniCharacterStudioForm.bounds=dotNetObject "system.drawing.rectangle" 10 90 425 540
laniCharacterStudioForm.text="Lani Character Studio - DotNet Drawing Edition(tm)"
borderStyle=(dotNetClass "System.Windows.Forms.FormBorderStyle").None
selectionColor = ((dotNetClass "system.drawing.color").fromArgb 80 102 153 255)
charStudioBG = (dotNetclass "System.Drawing.Image").fromfile @"X:\3dsmax\2013\MaxScript\images\characterStudioBackground.png"
unselPointColor = ((dotNetClass "system.drawing.color").fromArgb 80 255 255 255)
selPointColor = ((dotNetClass "system.drawing.color").fromArgb 80 0 255 0)
ellipseRec = dotNetObject "system.drawing.rectangle" 0 0 20 20
ellipseBrushUnSel = dotNetObject "system.drawing.solidBrush" unselPointColor
ellipsePenUnSel = dotNetObject "system.drawing.pen" ellipseBrushUnSel 1
laniCharacterStudioForm.formBorderStyle=borderStyle
laniCharacterStudioForm.ShowInTaskbar = false
pBox=dotNetObject "pictureBox" --"system.windows.forms.pictureBox"
--Set the size and location of the pictureBox
pBox.bounds=dotNetObject "System.drawing.rectangle" 0 0 425 540
--Set the background color
pBox.backColor=(dotNetClass "system.drawing.color").gray
--Add the pictureBox to the form
laniCharacterStudioForm.controls.add pBox
--Rectangle and brush for painted box
boxRec=dotNetObject "system.drawing.rectangle" 10 10 0 0
boxBrush=dotNetObject "system.drawing.solidBrush" selectionColor
pBox.backgroundImage = charStudioBG
--Add the paint function
fn pBoxPaint sender arg=
(
--formatProps arg.graphics
--Create graphics object
g=arg.graphics
--Paint a filled rectangle into the pictureBox
g.fillRectangle boxBrush boxrec
for i=1 to 5 do (
try(
g.DrawEllipse ellipsePenUnSel (dotNetObject "system.drawing.rectangle" (i*25) (i*25) 20 20)
)catch(print(getCurrentException()))
)
)
--Add the paint event handler
dotNet.addEventHandler pBox "paint" pBoxPaint
mouseIsDown=false
movingWindow=false
mO = #(0,0)
--hold the starting position of the drag
dragStartX = -1
dragStartY = -1
--Add event handlers for the form.
fn formMouseDown sender arg=
(
--Check to see which mouse button is being pressed.
if arg.button==arg.button.left do (
--LEFT button is down
--formatProps arg;
mouseIsDown=true
dragStartX = arg.x
dragStartY = arg.y
print("clicked at point: "+dragStartX as string+","+dragStartY as string)
)
if arg.button==arg.button.middle do (
print #middle
movingWindow = true
mO = #(arg.x,arg.y)
)
if arg.button==arg.button.right do (
--RIGHT button is down
print #right
)
)
fn formMouseUp sender arg=
(
mouseIsDown=false
movingWindow=false
laniCharStudioLastPos = #(laniCharacterStudioForm.Left,laniCharacterStudioForm.Top)
boxRec.Width = 0
boxRec.Height = 0
pbox.refresh()
)
fn formMouseMove sender arg=
(
if mouseIsDown do
(
boxRec=NormalizeRectangle dragStartX dragStartY arg.x arg.y
pbox.refresh()
)
if movingWindow do
(
laniCharacterStudioForm.Left = mouse.screenpos.x-mO[1]
laniCharacterStudioForm.Top = mouse.screenpos.y-mO[2]
)
)
--Add the event handlers.
dotNet.addEventHandler pBox "mouseDown" formMouseDown
dotNet.addEventHandler pBox "mouseUp" formMouseUp
dotNet.addEventHandler pBox "mouseMove" formMouseMove
--Set the life time control
dotNet.setLifeTimeControl pBox #dotNet
--Force an update to the form to fire the paint event
if (laniCharStudio_DotnetVisible == false) then (
laniCharStudio_DotnetVisible = true
laniCharacterStudioForm.show(maxHandle)
laniCharacterStudioFormGlbl = laniCharacterStudioForm
if (laniCharStudioLastPos != undefined) then (
laniCharacterStudioForm.Left = laniCharStudioLastPos[1]
laniCharacterStudioForm.Top = laniCharStudioLastPos[2]
)
) else (
laniCharStudio_DotnetVisible = false
laniCharacterStudioFormGlbl.hide()
)
laniCharacterStudioForm.refresh()
)
Any pointers would be much appreciated
That is the basic method. See my first code example in this thread.
I would avoid registering two different functions for the paint event. You can never be sure which one will be called first. Handle all your code in one function. If necessary, have this function call other sub-functions using the graphics object as a parameter.
To be clear, this is an over-simplified example of what I mean:
struct ctrlPoint
(
bounds,
selected
)
dnFm = dotnetobject "form"
dnPnl = dotnetObject "panel"
rctCls = dotnetClass "System.Drawing.Rectangle"
ptCls = dotnetClass "System.Drawing.Point"
penCls = dotnetClass "System.Drawing.Pen"
brshCls = dotnetClass "System.Drawing.SolidBrush"
clrCls = dotnetClass "System.Drawing.Color"
dnPnl.dock = dnPnl.dock.Fill
ptPen = dotnetObject penCls (clrCls.black)
ptBrush = dotnetObject brshCls (clrCls.red)
selPtBrush = dotnetObject brshCls (clrCls.green)
ctrlPoints = #( \
ctrlPoint (dotnetObject rctCls 100 100 20 20) off,
ctrlPoint (dotnetObject rctCls 20 30 20 20) off,
ctrlPoint (dotnetObject rctCls 150 180 20 20) off,
ctrlPoint (dotnetObject rctCls 150 220 20 20) off,
ctrlPoint (dotnetObject rctCls 300 10 20 20) off
)
fn paintFn s e =
(
e.graphics.smoothingMode = e.graphics.smoothingMode.highQuality
for p in ctrlPoints do
(
e.graphics.fillEllipse (if p.selected then selPtBrush else ptBrush) p.bounds
e.graphics.drawEllipse ptPen p.bounds
)
)
fn mouseUp s e =
(
for p in ctrlPoints where p.bounds.contains e.location do
(
p.selected = not p.selected
s.invalidate p.bounds
)
)
dnFm.controls.add dnPnl
dotnet.addEventHandler dnPnl "MouseUp" mouseUp
dotnet.addEventHandler dnPnl "Paint" paintFn
dnFm.size = dotnetObject "System.Drawing.Size" 400 400
dnFm.show()
if you like this way of making UI probably you have to make friends with WPF… it will help you to design any control without deriving from the existent.
Don’t get me wrong, I won’t recreate a checkbox or datagridview just for fun, that’s just unnecessary tedium. Only when there are obvious benefits.
I’ve been meaning to make friends with WPF for a long time, it’s somewhere on my list
I’m still working on this, but it’s not really in a postable state right now heh. Making progress, though – many thanks to everyone for the help so far!
What’s this WPF though?
http://en.wikipedia.org/wiki/Windows_Presentation_Foundation
Think of it as an evolution of system.windows.forms.
Thanks, not sure if it’s something I’ll even try to incorporate right now, but there’s yet another thing to look into
edit: and why the HELL have I never used structs before – those things are great! :banghead: