Thursday, 10 June 2010

Visual Studio-like GUI in your apps

Have you ever wanted to make your tools resemble Visual Studio in terms of GUI? If yes is the answer for this question, then go on with reading. I will describe WeifenLuo DockPanel control which makes it pretty simple to achieve. It's a fantastic and easy to use control, the only drawback of it is lack of documentation so to actually create something you either have to go through the sources of the control itself or of the provided sample (only one unfortunately). In nGENE Tech Toolset such a GUI is present since the first release. I'm writing this post because I've just updated it to the newest version available (namely 2.3.1). In case you don't know what I'm talking about here is a screenshot:


Everything is draggable, can be hidden (or auto-hiding can be enabled) etc. Exactly the same way you can do this in Visual Studio. It even looks very similar - the icons, the colours are all the same.

You can download WeifenLuo DockPanel 2.3.1 from here.

The best thing about this control is that if you already have toolset written in .NET framework it is very easy to adopt it. I will provide snippets of C++/CLI code to show the basic usage.

Dockable windows have to derive from DockContent class:

public ref class frmErrorList: public WeifenLuo::WinFormsUI::Docking::DockContent

By deriving from DockContent class frmErrorList form gets several new properties. The list of the most useful ones is provided below:
  • AllowEndUserDocking - if set to true then the window will be draggable,
  • DockAreas - it's a combination of flags specifying where the window can be docked (top, bottom, left, right, float, document),
  • TabText - text which will be visible on the tab.
Here is sample code:

this->AllowEndUserDocking = true;
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(698, 210);
this->Controls->Add(this->dgvLog);
this->Controls->Add(this->tstMsgType);
this->DockAreas = static_cast<WeifenLuo::WinFormsUI::Docking::DockAreas>((((WeifenLuo::WinFormsUI::Docking::DockAreas::Float
| WeifenLuo::WinFormsUI::Docking::DockAreas::DockTop)
| WeifenLuo::WinFormsUI::Docking::DockAreas::DockBottom)
| WeifenLuo::WinFormsUI::Docking::DockAreas::Document));
this->Name = L"frmErrorList";
this->ShowHint = WeifenLuo::WinFormsUI::Docking::DockState::DockBottom;
this->Text = L"Error List";
this->TabText = L"Error List";

What changes is how the windows are added and shown in the MDI form. You have to add a WeifenLuo::WinFormsUI::Docking::DockPanel control to your MDI form (which doesn't derive from any of the WeifenLuo controls, i.e. it's a regular WinForms form) and call its default constructor. Setting its parameters doesn't differ from the way it's done for other controls. Here's the code:

dockPanel->ActiveAutoHideContent = nullptr;
dockPanel->AllowEndUserDocking = true;
dockPanel->Dock = System::Windows::Forms::DockStyle::Fill;
dockPanel->Location = System::Drawing::Point(0, 74);
dockPanel->Margin = System::Windows::Forms::Padding(2);
dockPanel->Name = L"dockManager";
dockPanel->Size = System::Drawing::Size(1158, 456);
dockPanel->TabIndex = 3;


this->Controls->Add(this->dockPanel);

where dockPanel is of type DockPanel. Then to show any child window, just call:

frmErrorList->Show(dockPanel);

Show() method has several overloaded variants of which this one can be useful too:


frmErrorList->Show(dockPanel, DockState::Document);

This way you can specify that frmErrorList is displayed as a document.

It can also come in handy to save layout (eg. this way you can allow user to customize layout and restore it next time he starts the application) and then restore it. Saving it is straight forward - just call:

dockPanel->SaveAsXml("layout.xml");

This way layout.xml file is created and you can load it next time.

Restoring layout is a bit more complicated. You have to make following call:

dockPanel->LoadFromXml("layout.xml", deserializeDockContent);

deserializeDockContent is of type DeserializeDockContent which is created this way:

deserializeDockContent = gcnew DeserializeDockContent(this, &frmMDIForm::GetContentFromPersistString);

Here is example implementation of GetContentFromPersistString() method:

WeifenLuo::WinFormsUI::Docking::IDockContent^ GetContentFromPersistString(System::String^ persistString)
{
   if (persistString == frmErrorList::typeid->ToString())
      return frmError;
   else
     return nullptr;
}

Implementing it this way requires you to allocate memory for frmError before making a call to LoadFromXML().

3 comments:

  1. Wow. I tried to find something more about with Google help. You have already reached TOP3. :D Caffeine works?

    ReplyDelete
  2. Hi,
    We have used this in our application.
    but one major problme we are facing that thd DOC panel instance is not rleasing mamory of old instances

    ReplyDelete