Here's an excerpt of code to dock a .NET form inside a viewport: [Flags] internal enum WindowStyles : uint { WS_OVERLAPPED = 0x00000000, WS_POPUP = 0x80000000, WS_CHILD = 0x40000000, WS_MINIMIZE = 0x20000000, WS_VISIBLE = 0x10000000, WS_DISABLED = 0x08000000, WS_CLIPSIBLINGS = 0x04000000, WS_CLIPCHILDREN = 0x02000000, WS_MAXIMIZE = 0x01000000, WS_BORDER = 0x00800000, WS_DLGFRAME = 0x00400000, WS_VSCROLL = 0x00200000, WS_HSCROLL = 0x00100000, WS_SYSMENU = 0x00080000, WS_THICKFRAME = 0x00040000, WS_GROUP = 0x00020000, WS_TABSTOP = 0x00010000,
WS_MINIMIZEBOX = 0x00020000, WS_MAXIMIZEBOX = 0x00010000,
WS_CAPTION = WS_BORDER | WS_DLGFRAME, WS_TILED = WS_OVERLAPPED, WS_ICONIC = WS_MINIMIZE, WS_SIZEBOX = WS_THICKFRAME, WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, WS_CHILDWINDOW = WS_CHILD, }
public const Int32 GWL_STYLE = -16;
[DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] internal static extern IntPtr SetParent( IntPtr hWnd, IntPtr hWndParent ); [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] internal static extern IntPtr GetParent( IntPtr hWnd ); [DllImport( "user32.dll" )] internal static extern WindowStyles GetWindowLong( IntPtr hWnd, Int32 nIndex ); [DllImport( "user32.dll" )] internal static extern UInt32 SetWindowLong( IntPtr hWnd, Int32 nIndex, UInt32 dwNewLong ); //[DllImport( "user32.dll" )] //internal static extern IntPtr SetParent( IntPtr child, IntPtr newParent );
internal static WindowStyles GetWindowStyle( IntPtr hWnd ) { return (WindowStyles)GetWindowLong( hWnd, GWL_STYLE ); }
internal static void SetWindowStyle( IntPtr hWnd, WindowStyles windowStyle ) { SetWindowLong( hWnd, GWL_STYLE, (UInt32)windowStyle ); }
class MainViewWindow : global::Autodesk.Max.Plugins.ViewWindow { IntPtr previousParent; WindowStyles previousStyle; public override IntPtr CreateViewWindow( IntPtr hParent, int x, int y, int w, int h ) { this.previousParent = GetParent( Plugin.Instance.MainFrame.Handle ); this.previousStyle = GetWindowStyle( Plugin.Instance.MainFrame.Handle );
MainFrame mainFrame = Plugin.Instance.LaunchDefault_( hParent, Plugin.LayoutType.General ); SetWindowStyle( mainFrame.Handle, WindowStyles.WS_CHILD | WindowStyles.WS_VISIBLE | WindowStyles.WS_CLIPCHILDREN ); SetParent( mainFrame.Handle, hParent );
mainFrame.dockedInsideExtendedView = true; return mainFrame.Handle; }
public override void DestroyViewWindow( IntPtr hWnd ) { Plugin.Instance.MainFrame.dockedInsideExtendedView = false; SetWindowStyle( Plugin.Instance.MainFrame.Handle, this.previousStyle ); SetParent( Plugin.Instance.MainFrame.Handle, this.previousParent ); Plugin.Instance.MainFrame.FormBorderStyle = FormBorderStyle.Sizable; // NOTE: Do not call close here because when resetting a scene the layout is saved correctly, then this // method is called by Max and Close(...) here saves the layuout again, with an empty view thus causing // a blank layout to be saved Plugin.Instance.MainFrame.Hide(); }
public override string Name { get { return ProjectName + " Main Window"; } }
/// <summary> /// Can only have one mainframe! /// </summary> public override int NumberCanCreate { get { return 1; } }
public override bool CanCreate { get { // Cannot put Mainframe into another view without first undocking it from the previous one return !Plugin.Instance.MainFrame.dockedInsideExtendedView; } } } Marsel Khadiyev (Software Developer, EPHERE Inc.) |
| How/where do you get the Plugin property (e.g.: Plugin.Instance.MainFrame.Hide()) ? I could not find it and dont know how to provide it to the program! Thx in advance Michbeck |
| Plugin is a class I have that's derived from IPlugin, so it'd be something like: public class Plugin : Autodesk.Max.IPlugin { public void Initialize(...){} public void Cleanup(...){} } MainFrame is a Form-derived class that I use to display the GUI (i.e. public class MainFrame: System.Windows.Forms.Form) Marsel Khadiyev (Software Developer, EPHERE Inc.) |
| Hey Marsel, I found the interface IPlugin. So did you derive the class Plugin only for your project or is it part of the Max.Net Framework? Besides the two methods Initialize() and Cleanup() - where is the static member Plugin.Instance you are accessing? Or do I get this completely wrong? The explanation of IPlugin says every class "should implement a global class of IPlugin" - what does this involve and how is it done - never came across it since now. Thx Regards, Michbeck |
| Hey Michbeck, IPlugin is similar to a DLL 'entry' function, it serves two main purposes- to pass the IGlobal interface to a plugin, and to let it know when it is initialized and uninistalized. You don't need to derive every class from it. In Zookeeper, for example, I just have one class (called 'Plugin') derived from it. Inside it has a static property called 'Instance' and it looks something like: class Plugin: IPlugin { static Plugin instance; public static Plugin Instance { get { return instance; } } public void Initialize( IGlobal global ) { instance = this; } } Then anywhere inside my assembly I just use Plugin.Instance to access singleton properties of my plugin (for example, Plugin.Instance.Global gets the IGlobal interface passed to it). Hope this is a bit more clear. Marsel Khadiyev (Software Developer, EPHERE Inc.) |
| Thx very much for your help Marsel - it gets me closer to the solution. But I have further questions: What shall the method LaunchDefault_(...) of class Plugin do? I also dont know the class or type of the second parameter Plugin.LayoutType and its member or property "General". Sry, but I am completely new in C# programming, PInvoke, WindowsForms etc. :) Regards Michbeck |
| Hey Marsel, I have a further problem: In order to use windows forms api I have to add a reference to System.Windows.Forms in the .net project to make the .dll available for compiling. At runtime the plugin is also loaded properly but when I execute it (its a modifier plugin) the following error occurs: System.IO.FileNotFoundException: Die Datei oder Assembly "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" oder eine Abhängigkeit davon wurde nicht gefunden. Das System kann die angegebene Datei nicht finden. Dateiname: "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" bei WindowTestModifier.WindowTestModifier.ModifyObject(Int32 t, IModContext mc, IObjectState os, IINode node) bei Autodesk.Max.Wrappers.ManagedWrapperModifier.ModifyObject(ManagedWrapperModifier* , Int32 t, ModContext* mc, ObjectState* os, INode* node)
=== Zustandsinformationen vor Bindung === LOG: Benutzer = Michbeck-PC\Michbeck LOG: DisplayName = System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 (Fully-specified) LOG: Appbase = file:///C:/Program Files (x86)/Autodesk/3ds Max 2009/ LOG: Ursprünglicher PrivatePath = NULL Aufruf von Assembly : (Unknown). === LOG: Diese Bindung startet im default-Load-Kontext. LOG: Die Anwendungskonfigurationsdatei wird verwendet: C:\Program Files (x86)\Autodesk\3ds Max 2009\3dsmax.exe.Config LOG: Die Computerkonfigurationsdatei von C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config wird verwendet. LOG: Verweis nach der Richtlinie: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 LOG: Download von neuem URL file:///C:/Program Files (x86)/Autodesk/3ds Max 2009/System.Windows.Forms.DLL. LOG: Download von neuem URL file:///C:/Program Files (x86)/Autodesk/3ds Max 2009/System.Windows.Forms/System.Windows.Forms.DLL. LOG: Download von neuem URL file:///C:/Program Files (x86)/Autodesk/3ds Max 2009/System.Windows.Forms.EXE. LOG: Download von neuem URL file:///C:/Program Files (x86)/Autodesk/3ds Max 2009/System.Windows.Forms/System.Windows.Forms.EXE Sry for german: It means that the assembly could not be found by 3dsmax and therefore the plugin doesnt work. I tried it with several dlls and copied them to the plugin folder and 3dsmax folder as well, but it doesnt work - do you have an idea how to fix this? Thx and Regards Michbeck |
| Hi Michbeck, LaunchDefault and Layout properties are just some internal properties I use in ZK, its nothing special. LaunchDefault shows the mainframe window (ie. myForm.Show()) and layout determines its internal window layout, its something you shouldn't be concerned about. The error you've gotten seems to be because you're referencing .NET 4.0 version of forms dll. Try to reference 3.5 assemblies for now if possible, as I said in the other thread I will try to compile against 4.0 next time. Marsel Khadiyev (Software Developer, EPHERE Inc.) |
| Hey Marsel, thx for your help again! Actually I am confused now :) I am trying to open a Windows forms window within my Modifier plugin in 3dsmax. So I took the code you posted in this thread and just tried to make it compile properly. Thus I came across the problem that the Plugin class (derived from IPlugin as you said) is missing, the MainFrame class is missing - so I considered your messages and your code to create those classes: class Plugin : IPlugin { static Plugin instance;
public MyMainFrame mainframe; // static IObject layoutType;
public static IObject LayoutType { get { return layoutType; } }
public static Plugin Instance { get { return instance; } }
public MyMainFrame MainFrame { get { return mainframe; } }
public void Initialize(IGlobal global, ISynchronizeInvoke sync) { instance = this; }
public void Cleanup() { }
// public MyMainFrame LaunchDefault_(IntPtr parent, IObject layoutTypeGeneral) // { // return mainframe; // } }
class MyMainFrame : System.Windows.Forms.Form { public bool dockedInsideExtendedView; } But finally I dont know how to implement the method LaunchDefault_(...) - you said I should not be concerned about this - but without it I can't open the window. Furthermore I dont understand how/where/when the class PlugIn is instantiated to establish the connection to my code and set the instance member to the current Plugin! How does the docking mechanism work? It is not just done by adding the boolean variable dockedInsideExtendedView, is it? It seems to me that I have a major understanding problem here :) Would you please explain the correct way of doing this to me? Thx for the effort - I know you are quite busy due to upcoming Siggraph :) Greets Michbeck |
| Hi Michbeck, Showing forms in .NET is pretty easy, you need to do something like: mainframe = new MyMainFrame(); mainframe.Show(); You can check some articles on google that explain this better. Docking inside viewport requires some care taking, I've written a small article on that here: http://ephere.com/autodesk/max/docs/602.html Cheers Marsel Khadiyev (Software Developer, EPHERE Inc.) |