Docking

Win32++

Docking

Description

The CDocker class adds both splitter windows and docking to the Win32++ framework. CFrame is inherited from CDocker, so frames and MDI frames supportd docking directly.

Splitter windows have a moveable splitter bar between the windows. Windows which have docking enabled have the moveable splitter bar too, but they can also be dragged away from the view, or undocked. Undocked docking windows can be dragged over the view window and docked.

The CDocker class creates specialized windows called "Dockers" capable of allowing other Dockers to dock to and undock from them. When docking, the undocked Docker is dragged over another Docker. Various visual clues such has the dock targeting (small arrow-like images), and the dock hinting (where a portion of the destination window turns blue) provide a hint as to where the Docker will be docked. To facilitate undocking, the caption of the docked window is dragged and dropped.

Every Docker has a view window. These views can be any resizable child window, and are set in the same way as views for Frames and MDI children. DockContainers (provided by the CDockContainer class) are a specialized view which add additional docking features when used as the view window for a Docker. DockContainers are decribed in more detail below.

A Docker which is docked within another Docker is said to be a "dock child" of that Docker. There is no theoretical limit to how many dock children a Docker may have. There is also no theoretical limit as to the depth of the child/parent relationship. That's to say there can be any number of dock children within dock children within dock children etc.

The primary or first Docker is referred to as the Dock Ancestor. This would typically be the frame or MDI frame window. Other Dockers are added to this dock hierarchy using the AddDockedChild or AddUndockedChild functions. This group of Dockers is said to be "related" or in the same docking group. These Dockers can dock and undock from each other.

A Docker will dock to any one of the four sides of another related Docker (left, right, top or bottom). When a Docker is docked within a child Docker, this is referred to as "inner docking". When a Docker is docked to the side of the Dock Ancestor, this is referred to as "outer docking". Outer docking and inner docking have different dock targeting visual cues.

Docker Components

The following image illustrates some of the components of Docker windows. In this case the window being docked is dragged over a Docker with a Container view. Hence we see the center target option within the Inner Dock Targets for Container within Container docking.

 

Setting the Docker's view

The following code shows how the Docker's view is assigned, and how to set the width of the splitter bar.

CDockClasses::CDockClasses() 
{ 
  SetView(m_Classes);
  
  // Set the width of the splitter bar
  SetBarWidth(8); 
}

Dockers can have a number of styles.  These styles can determine where the Docker is docked, whether or not it can be undocked, and where dock children are permitted to dock. The dock style is initially specified when the Docker is added to the docking hierarchy . The complete set of dock styles are as follows:

DS_DOCKED_LEFT Dock the child left
DS_DOCKED_RIGHT Dock the child right
DS_DOCKED_TOP  Dock the child top 
DS_DOCKED_BOTTOM Dock the child bottom
DS_NO_DOCKCHILD_LEFT Prevent a child docking left
DS_NO_DOCKCHILD_RIGHT Prevent a child docking right
DS_NO_DOCKCHILD_TOP Prevent a child docking at the top
DS_NO_DOCKCHILD_BOTTOM Prevent a child docking at the bottom
DS_NO_RESIZE Prevent resizing
DS_NO_CAPTION Prevent display of caption when docked
DS_NO_CLOSE Prevent closing of a docker while docked
DS_NO_UNDOCK Prevent undocking of the docker
DS_CLIENTEDGE Has a 3D border when docked
DS_NO_FIXED_RESIZE Resize dock children proportionally. Unless set, dock children have a fixed size when the dock parent is resized.
DS_DOCKED_CONTAINER Dock a container within a container
DS_DOCKED_LEFTMOST Leftmost outer docking
DS_DOCKED_RIGHTMOST Rightmost outer docking
DS_DOCKED_TOPMOST Topmost outer docking
DS_DOCKED_BOTTOMMOST Bottommost outer docking

Adding Dock Children

The following code shows how to add dock children.  Note that the newly added Docker can be a dock child of the dock ancestor or a dock child of any other Docker descendant of the to the dock ancestor.

void CMainFrame::LoadDefaultDockers()
{
  // Note: The  DockIDs are used for saving/restoring the dockers state in the registry

  DWORD dwStyle = DS_CLIENTEDGE; // The style added to each docker
	
  // Add the parent dockers
  CDocker* pDockRight  = AddDockedChild(new CDockClasses, DS_DOCKED_RIGHT | dwStyle, 200, ID_DOCK_CLASSES1);	
  CDocker* pDockBottom = AddDockedChild(new CDockText, DS_DOCKED_BOTTOM | dwStyle, 100, ID_DOCK_TEXT1);

  // Add the remaining dockers
  pDockRight->AddDockedChild(new CDockFiles, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_FILES1);
  pDockRight->AddDockedChild(new CDockClasses, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_CLASSES2);
  pDockRight->AddDockedChild(new CDockFiles, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_FILES2);

  pDockBottom->AddDockedChild(new CDockOutput, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_OUTPUT1);
  pDockBottom->AddDockedChild(new CDockText, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_TEXT2);
  pDockBottom->AddDockedChild(new CDockOutput, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_OUTPUT2);
}

Saving the Dockers in the Registry

CDocker provides built in support for saving the Dockers in the registry.  The functions which perform this task are CDocker::LoadDockRegistrySettings and CDocker::SaveDockRegistrySettings. 

The first step in using these functions is to override CDocker::NewDockerFromID for the Dock Ancestor. This function can create a new Docker from the specified Docker ID.  In this case CDockSimple is the CDocker class for our Dock Ancestor, so the NewDockerFromID function looks like this:

CDocker* CMainFrame::NewDockerFromID(int nID)
{
  CDocker* pDock = NULL;
  switch(nID)
  {
  case ID_DOCK_CLASSES1:
    pDock = new CDockClasses;
    break;
  case ID_DOCK_CLASSES2:
    pDock = new CDockClasses;					
    break;
  case ID_DOCK_FILES1:
    pDock = new CDockFiles;
    break;
  case ID_DOCK_FILES2:
    pDock = new CDockFiles;
    break;
  case ID_DOCK_OUTPUT1:
    pDock = new CDockOutput;
    break;
  case ID_DOCK_OUTPUT2:
    pDock = new CDockOutput;
    break;
  case ID_DOCK_TEXT1:
    pDock = new CDockText;
    break;
  case ID_DOCK_TEXT2:
    pDock = new CDockText;
    break;
  default:
    TRACE(_T("Unknown Dock ID\n"));
    break;
  }

  return pDock;
}

All that remains now is to use the CDocker's LoadDockRegistrySettings and SaveDockRegistrySettings in CMainFrame.  The following code demonstrates how this might be done.

void CMainFrame::OnInitialUpdate()
{
  m_DockView.SetDockStyle(DS_CLIENTEDGE);

  // Load dock settings
  if (!LoadDockRegistrySettings(GetRegistryKeyName()))
    LoadDefaultDockers();

  // PreCreate initially set the window as invisible, so show it now.
  ShowWindow();
}

void CMainFrame::SaveRegistrySettings()
{
  CFrame::SaveRegistrySettings();
  SaveDockRegistrySettings(GetRegistryKeyName());
}

Refer to the DockSimple, DockContainer and DockTabbedMDI samples for a demonstration of docking.

Dock Containers

DockContainers are a specialized view window for Dockers.  It is a tab control which has been designed to co-operate with docking.  While Dockers allows any child window to be used as the view window, DockContainers add additional features when used as the view window for Dockers.  These additional features include container within container docking (where the newly docked DockContainer adds another tab), as well as additions to the dock targeting and dock hinting visual cues.  DockContainers can also have a toolbar. The use of a toolbar is optional, but when used they are set up in the same way as toolbars for Frames.

The CDockContainer class adds DockContainers to the Win32++ framework. Each DockContainer has a single view window of its own.  This view window can be any child window, and is set in the same way as views for Frames and MDI children.

The various attributes of the DockContainer are typically set in its constructor. The following code demonstrates how to specify a DockContainer's tab text, tab icon, view window, and the text which will be displayed in the docked caption.

CContainClasses::CContainClasses() 
{
  // Note: CContainClasses inherits from CDockContainer
  
  SetTabText(_T("ClassView"));
  SetTabIcon(IDI_CLASSVIEW);
  SetDockCaption (_T("Class View - Docking container"));
  SetView(m_ViewClasses);
}

If the DockContainer has a toolbar, it is set up in the same way as the toolbar for a Frame. The following code demonstrates how the SetupToolBar function is used to specify the ToolBar's bitmap and resource IDs, as well as how to configure the toolbar themes to match those of the Frame.

void CContainClasses::SetupToolBar()
{
  // Set the Bitmap resource for the toolbar
  GetToolBar().SetImages(RGB(192,192,192), IDW_MAIN, 0, 0);

  // Set the Resource IDs for the toolbar buttons
  AddToolBarButton( IDM_FILE_NEW         );
  AddToolBarButton( IDM_FILE_OPEN, FALSE );
	
  AddToolBarButton( 0 );	// Separator
  AddToolBarButton( IDM_FILE_SAVE, FALSE );
	
  AddToolBarButton( 0 );	// Separator
  AddToolBarButton( IDM_EDIT_CUT         );
  AddToolBarButton( IDM_EDIT_COPY        );
  AddToolBarButton( IDM_EDIT_PASTE       );
	
  AddToolBarButton( 0 );	// Separator
  AddToolBarButton( IDM_FILE_PRINT, FALSE );
	
  AddToolBarButton( 0 );	// Separator
  AddToolBarButton( IDM_HELP_ABOUT       );

  // Add the ComboBarEx control to the toolbar
  AddCombo();
}

The following example demonstrates how to add DockContainers to an exiting Docker.  Note that the DS_DOCKED_CONTAINER style is used to dock a container within a container.  Only DockContainers support this style.

void CMainFrame::LoadDefaultDockers()
{
  // Note: The  DockIDs are used for saving/restoring the dockers state in the registry
  // Note: CDockClasses, CDockFiles, CDockOutput and CDockText inherit from CDocker, and
           have a view which inherits from CDockContainer.

  DWORD dwStyle = DS_CLIENTEDGE; // The style added to each docker
	
  // Add the parent dockers
  CDocker* pDockRight  = AddDockedChild(new CDockClasses, DS_DOCKED_RIGHT | dwStyle, 200, ID_DOCK_CLASSES1);	
  CDocker* pDockBottom = AddDockedChild(new CDockText, DS_DOCKED_BOTTOM | dwStyle, 100, ID_DOCK_TEXT1);

  // Add the remaining dockers
  pDockRight->AddDockedChild(new CDockFiles, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_FILES1);
  pDockRight->AddDockedChild(new CDockClasses, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_CLASSES2);
  pDockRight->AddDockedChild(new CDockFiles, DS_DOCKED_CONTAINER | dwStyle, 200, ID_DOCK_FILES2);

  pDockBottom->AddDockedChild(new CDockOutput, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_OUTPUT1);
  pDockBottom->AddDockedChild(new CDockText, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_TEXT2);
  pDockBottom->AddDockedChild(new CDockOutput, DS_DOCKED_CONTAINER | dwStyle, 100, ID_DOCK_OUTPUT2);
}