Browsing"DotNet"

Joystick Control – A custom user control project

Aug 28, 2008 by     1 Comment     Posted under: 3dsMax, DotNet, User Controls

Integrating a VB.net custom UI control into 3dsMax

A while ago I decided to research making your own UI controls in Visual Studio. Why? Well, as a character animator by day I am always looking for better ways of providing input for an animator to set various character attributes. I always liked the old joystick manipulator by Borislav Petrov but wanted a way of integrating this into the UI rather than in the viewport. Until Dot net was integrated with max, this would have been impossible without writing one yourself in C++.

This page details the results so far. As a project it was very much in the deep end, and the creation of the control was limited to my research into programming VB at the time. I have been coding maxscript in some shape or form since R3 but this was my first foray into this type of coding.

A brief Recap – Why arguments are not always bad…

Before I talk in more depth about the control itself, i thought i’d mention the basics of how events are handled within Visual Basic so that you can understand how to set these up and use them correctly within the MaxScript/DotNet bridge. I’m sure the procedure is similar in C# but I will use examples in VB as this is what i use. (My programmer friend Chris said to me once that VB and C# are identical, except C# is spoken with an American accent)

There are many handlers aside from the standard mouse input handlers, but for a custom control, these (along with GDI+ paint handlers) are the important ones. In VB, when you specify a mousedown handler it passes two arguments to the handling subroutine.

Private Sub Button1_MouseDown (ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
  Handles Button1.MouseDown
  )
  )

This is useful since even when using a dotnetcontrol, you can pass the sender and event arguments and use them in a similar way. These arguments are important in regard to a control that uses the mouseposition to set positions of UI elements.

Writing a custom event class

With a custom control, you might not want to just return the mouse position in the event handler. Due to the nature of the control you might need another property that you can do something with. This is where writing your own event class comes in. You can specify what the handler uses as its ‘e’ argument. This is the basics of the new event class. It inherits the eventargs class so that it can use all of the properties of this class but augment them with others.

Public Class JoystickEventArgs

Inherits EventArgs

Private _location As Size
Private _X As Integer
Private _Y As Integer
Private _MorphValue As MorphValue

Public Property Location() As Size
Get
Return _location
End Get
Set(ByVal value As Size)
_location = value
End Set
End Property

Public Property X() As Integer
Get
Return _X
End Get
Set(ByVal value As Integer)
_X = value
End Set
End Property
Public Property Y() As Integer
Get
Return _Y
End Get
Set(ByVal value As Integer)
_Y = value
End Set
End Property

Public Property MorphValue() As MorphValue
Get
Return _MorphValue
End Get
Set(ByVal value As MorphValue)
_MorphValue = value
End Set
End Property

Public Sub New(ByVal Joystick As Joystick)
Location = Joystick.MovableLabel.Location
X = Me.Location.Width
Y = Me.Location.Height
MorphValue = GetMorphValuesLinear(Me.Location) End Sub
End Class


However, this class is not complete without another class. I created one called morphvalue, which is simply a container object to store four integer values for up,down,left and right. I also created an enumeration that you can retrieve in the handler to show which quadrant of the control you are in. This means you can use a case expression to control different objects on a per quadrant basis. The joystickeventarg creates an instance of this class each time it is called (within the new sub) and calls the getmorphvalueslinear() function. this is not listed but returns a value related to the mouse position.

Public Class MorphValue

Enum Quad
TopLeft
TopRight
TopCenter
BottomLeft
BottomRight
BottomCenter
CenterLeft
CenterRight
Reset
End Enum

Private _Left As Integer
Private _Right As Integer
Private _Up As Integer
Private _Down As Integer
Private _Area As Quad

Public Property Area() As Quad
Get
Return _Area
End Get
Set(ByVal value As Quad)
_Area = value
End Set
End Property

Public Property Left() As Integer
Get
Return _Left
End Get
Set(ByVal value As Integer)
_Left = value
End Set
End Property

Public Property Right() As Integer
Get
Return _Right
End Get
Set(ByVal value As Integer)
_Right = value
End Set
End Property

Public Property Up() As Integer
Get
Return _Up
End Get
Set(ByVal value As Integer)
_Up = value
End Set
End Property

Public Property Down() As Integer
Get
Return _Down
End Get
Set(ByVal value As Integer)
_Down = value
End Set
End Property

Public Sub New(ByVal Area As Quad, ByVal Value1 As Integer, ByVal Value2 As Integer, ByVal Value3 As Integer, ByVal Value4 As Integer)

Me.Up = Value1
Me.Down = Value2
Me.Left = Value3
Me.Right = Value4
Me.Area = Area
End Sub

End Class


so, within Visual studio and bound to the joystickmove handler on the joystick object, the new joystickeventargs class returns the following output –

Private Sub Joystick1_JoystickMove(ByVal sender As System.Object, ByVal e As LoneRobot.JoystickEventArgs) Handles Joystick1.JoystickMove

Dim Mretval As Size = Joystick1.MorphtoValue(e.MorphValue.Up, e.MorphValue.Down, e.MorphValue.Left, e.MorphValue.Right)

Label1.Text = e.X.ToString & "/" & e.Y.ToString & " || " & Mretval.Width.ToString & "/" & Mretval.Height.ToString
LabelUP.Text = e.MorphValue.Up.ToString
LabelDown.Text = e.MorphValue.Down.ToString
LabelLeft.Text = e.MorphValue.Left.ToString
LabelRight.Text = e.MorphValue.Right.ToString

LabelUP.Height = e.MorphValue.Up
LabelDown.Height = e.MorphValue.Down
LabelLeft.Height = e.MorphValue.Left
LabelRight.Height = e.MorphValue.Right
Label2.Text = e.MorphValue.Area
End Sub

you will see that the handler is now specifying "e As LoneRobot.JoystickEventArgs" meaning that it is returning this class.

If you are binding this into a max dotnetcontrol event, you just need to set the morph channels to the morphvalue.<direction> property of  the eventarg.

download

3ds Max File Drop via DotNet

Aug 26, 2008 by     3 Comments    Posted under: 3dsMax, DotNet, Tips and Tricks

If you need to mimic the way that max presents you with merge/open/xref options when you drag into a viewport, you can perform this yourself with DotNet. Max expects a dataobject containing a string array. Depending on the filetype that this string member points to will decide whether max will prompt you with a file open menu or a texture map as viewport background. The other option is you can also pass a map path to drag onto slots on the material editor.

Below is a snippet of the code for this operation. It is entirely handled within the mousedown handler of the control. It makes sense in this case as you wouldn’t put it in the mousemove handler for example as this would mean the code would be needlessly called multiple times.

on btndragdrop mouseDown sender args do 
( 
theIniFile = getdir #maxData + "3dsmax.ini" 
theKeys = getIniSetting theIniFile "FileList" 
maxfilearray = for o in theKeys where o != "MaxFiles" collect (getIniSetting theIniFIle "FileList" o)
intnum = dotnetobject "System.Int32" 0 
filenamestring= dotnetobject "System.string" maxfilearray[1] 
dropfile = dotnetobject "System.String[]" 1 
dropfile.setvalue filenamestring intnum 
DataObj = dotnetobject "DataObject" ((dotnetclass "DataFormats").filedrop) dropfile 
sender.dodragdrop Dataobj ((dotnetclass "DragDropEffects").Copy) 
)

The only thing to note is that the Dataobject takes an object as it’s first parameter. This can basically be anything you like, except this in this case. The SDK notes that it needs an array in this object. The square brackets after the dropfile variable denotes that it is an array of strings with 1 member, not a string. Passing a string object therefore has no effect and does not present you with the menu seen below, which is the result of the correct format of the DataObject.

Not the end of the story…

I noticed an odd behavior using drag and drop in max. When employed on a dotnetcontrol in a maxscript window, it classes it as being ‘inside’ max even though it is a dotnet object. Therefore you could in theory initialize the drag and drop onto the label control to perform a legitimate drop. This behavior is possibly not wanted, if you needed to perform other handlers – the drag drop might get triggered accidentally. The way around this is to use the mouseeventargs to check if the mouse has moved outside the client area of the control.

on DragDropOps open do 
( 
btndragdrop.allowdrop = false
btndragdrop.text = "Hooray! A Drag/Drop Enabled Label!!!\n\nTo drop a Texturemap, just pass the map path string in the dataobject instead of a max file path. This will also work if draging a map to the material editor" btndragdrop.borderstyle = (dotNetClass "System.Windows.Forms.BorderStyle").fixedsingle 
btndragdrop.backcolor = (dotnetclass "System.Drawing.Color").orangered 
btndragdrop.forecolor = (dotnetclass "System.Drawing.Color").yellow 
) 

on btndragdrop mousemove sender args do 
(
if (sender.clientrectangle.contains args.x args.y) then 
( 
setsyscur #arrow 
) 
else 
(
 setSysCur #move
) 
) 

on btndragdrop mouseup sender args do 
(
if (sender.clientrectangle.contains args.x args.y) then 
( ) 
else 
( 
theIniFile = getdir #maxData + "3dsmax.ini" 
theKeys = getIniSetting theIniFile "FileList" m
axfilearray = for o in theKeys where o != "MaxFiles" collect getIniSetting theIniFIle "FileList" o 
filenameString = maxfilearray[1] 
dropfile = dotnetobject "System.String[]" 1 
dropfile.setvalue filenameString 0 
DataObj = dotnetobject "DataObject" ((dotnetclass "DataFormats").filedrop) dropfile 
sender.dodragdrop Dataobj ((dotnetclass "DragDropEffects").Copy) ) )