Friday, February 16, 2007

VB.NET 2005 MDI Docking to Floating Window

Have you ever wanted to create a tool panel that can dock on your GUI and free float as a tool window anywhere no the screen?

You may think that you can just create a child window and set its mdi parent and dock it to the left. But there are issues with this approach when you make it floating and try to redock it. Especially if you have a child window that is using that area of the screen. What I propose here is to to use panels to pull off the trick.



The first step is to create an MDI Parent window. Just create a new project in vs2005 (VB or C#) and set the "IsMdiContainer" property to True. Go ahead and add a menustrip and add a view option with sub options of Left, Right, Floating.

Next, we want to add a panel to the form and dock it. Go ahead and add panel1. Dock it to the left and throw a textbox on it for testing. Write the code in the menu events to change the dock property of your form. Leave the floating event blank. We'll get to that... If you run your app, you should be able to switch the docking to left and right.

Create a new form called frmTools. This window will be the container for our panel when we want it to float. Change the FormBorderStyle to SizableToolWindow. Now hit F7 to go to the code view. Add 2 member variable. 1 panel called mPanel and 1 Form1 call mParent. Modify the constructor of frmTools so that you can pass in a reference to Form1 and a reference to a panel. Set these variables before "Initializecomponent."

Next, create a private function call InitPanel(). What we'll do here is add the panel to the Controls Collection so that it can be rendered. You'll also want to set the Dock property to DockStyle.Fill and remember to use the SuspendLayout() and ResumeLayout(). Essentially, what we've done is create a secondary "Initializecomponent" function. I wouldn't recommend changing the "Initializecomponent" to do this code as it could cause issues during design time in Visual Studio 2005 designer. Once you've created this function call it in your constructor function after "InitializeComponent()."

Now, lets go back to Form1. In the event for View->Floating we'll create the frmTools window and call Show().

private void floatingToolStripMenuItem_Click(object sender, EventArgs e)
{
Form1 form = this;
frmTools tools = new frmTools(ref form, ref panel1);

this.SuspendLayout();
this.Controls.Remove(this.panel1);
this.ResumeLayout(false);
this.PerformLayout();
tools.Show();

}

Notice that I created a form and set it to "this" and then passed the reference to form into the function. There are other ways of referencing the main form. This was just a quick and dirty way. Maybe we'll get into that on another day. :)
In addition, we removed the item from the controls collection. Now, in testing I was able to remove this code completely and got the same results but lets play it safe. (BTW: If you know why this is so, please comment on the blog, thanks.)
Anyway, if you run your application you get your panel to float! Not to bad. One problem remains though. If you close the tool window your panel is now gone. Its been destroyed with the frmTool Control collection. So, we need to let the Parent window know that we are done with the panel. And we have to code a function in the parent window so that it can reinitialize the panel.
In Form1, create a new function called SetToolPanel(ref Panel panel).

public void SetToolPanel(ref Panel panel)
{
this.SuspendLayout();
this.panel1 = panel;
//set the dockstate to default.
panel1.Dock = DockStyle.Left;

this.Controls.Add(this.panel1);
this.Controls.SetChildIndex(this.panel1, 0);
this.ResumeLayout(false);
this.PerformLayout();
}

What we're doing here is something similar to "InitializeComponent()." We're just adding this control back in. Notice that I called "Controls.SetChildIndex." I did this because if you don't have these controls in the right order it could affect other docking controls. If you were to comment out this line and test, it would mess up the docking on your menustrip. So, remember to set it properly in your app.
Next, we need to modify the frmTools window so that it passes this panel back to the parent when its closing. In the properties window of frmTools, click the Events lightning bolt and override the "FormClosing" event under "Behavior." Call it onClosing. In this function we're only going to call "SetToolPanel" and remove the panel from the control collection so it doesn't get wiped out.

private void onClosing(object sender, FormClosingEventArgs e)
{
//Pass it off to the parent.
mParent.SetToolPanel(ref mPanel);
//Remove the control so it doesn't get ditched.
this.Controls.Remove(mPanel);
}
Thats it. Running the program you'll have a floatable dockable panel for your app. While testing, type something in the panel's textbox to show that its the same panel when it cycles through its states.
If you need source code let me know...
Happy Coding!
Jas

21 comments:

  1. Niels Oltmanns6/07/2007

    Wow, very cool. You saved my day :-)

    ReplyDelete
  2. Very much appreciated. Gives me a good place to start... :)

    ReplyDelete
  3. Graham9/17/2007

    This helps me a lot. I've been strugling to make an mdi application with tool windows. This is an excellent start.

    I do have one question though. I need to have toolwindows much like an image editor (think fireworks or photoshop...). I think your panel idea will work nicely only I want them to stay on top even when the active window is the document (image document). I also noticed that an mdi child that is maximized seems to go behind the panel on the mdi parent. Any idea how to make it stop at the panel's edge?

    ReplyDelete
  4. Anonymous9/19/2007

    Hello,

    im sorry but this tutorial is a little complicatet for my skills.
    Is it possible to get all the code you added to the project?

    Regards
    Marcus

    ReplyDelete
  5. Hi Graham,
    Off the top of my head I would probably use the topmost property of the child window. However, this link suggested something different. hope it helps.

    gamedev article

    Happy Coding!
    Jas

    ReplyDelete
  6. Marcus,
    I would be happy to send you sample code. Go to my website and fill out the contact information and I'll get back to you.

    contact me here.

    ReplyDelete
  7. Hi,
    I did try what you suggested in the article but somehow I cannot achive the same. Can you please provide sample code. Thanks in advance.

    ReplyDelete
  8. Keep trying you'll get it. The concept is that you are really using two windows and passing data between them to maintain state. You can't really dock a created window. The properties of that window is defined when created. So, the only way to dock a window is to make a new one. Its the panel that doesn't die. The window is just a container.

    Hope this helps.
    Many days and few bugs to you.
    Jas

    ReplyDelete
  9. Hi jason,

    I am also new to VB.Net. Can I get a copy of the code or project to perform this task.

    Thanks,

    Amir

    ReplyDelete
  10. Anonymous2/04/2008

    Since i don't have much introduction about Windows Forms, I feel little bit difficult to understand this article. If you send the source code, this would be really helpful. Thanks

    ReplyDelete
  11. Anonymous2/04/2008

    Since i don't have much introduction about Windows Forms, I feel little bit difficult to understand this article. If you send the source code, this would be really helpful. Thanks

    ReplyDelete
  12. Anonymous5/22/2008

    plz
    If you send the source code, this would be really helpful. Thanks
    rajan.singh@contakt.co.in

    ReplyDelete
  13. Anonymous8/05/2008

    Nice example, with some trial and error was able to work through using VB.Net. I wish to build on this and create many panels/tool windows and even capture some of the VS Studio-like feautures (dock panel style). Any advice/examples to review that you can offer? Thanks for sharing, KER

    ReplyDelete
  14. - Use Visual Studio SDK if you want to create an editor of some sort.

    - Another option is to purchase the controls from Infragistics or another vender or company. I hear RightBytes can provide you with such a panel system. :)

    - Or, roll you own. If you do roll your own you shouldn't have too much of a problem. You might want to consider plug-in architecture if you think you'll be developing more panels as time goes on. Hope it works out!

    Regards,
    Jason

    ReplyDelete
  15. hello..i m beginner for vb net. Can teach me how to make MDI step by step...

    ReplyDelete
  16. Anonymous9/29/2008

    hello... this is very cool... im learning vb again as i stopped programming in it for a while and want to create a new tool... could you please send me the code for this to pa_ri_os_02@hotmail.com?

    ReplyDelete
  17. it work ~~ thank you very much DX ..

    ReplyDelete
  18. Anonymous8/10/2009

    someone please post the code at constructor of the toolform

    Regards
    G

    ReplyDelete
  19. Anonymous2/20/2012

    Very cute, thank you so much!

    ReplyDelete
  20. good please send me source code to my email id manivannangd@hotmail.com

    thanks
    Manivannan

    ReplyDelete
  21. Daniel4/11/2014

    Hi Manivannan

    Please VBNET has the code for this example??

    I can forward it to d_salum@hotmail.com??

    thank you very much for sharing your knowledge
    regards
    Daniel
    (Argentina)

    ReplyDelete