Barras de control acoplables (paneles)
Casi todas las aplicaciones MFC tienen una barra de herramientas y una barra de estado: tipos especiales de barra de control que se acoplan a la parte superior e inferior del marco principal de la aplicación. Pero a menudo, la lógica de la aplicación requiere más barras de información acopladas a algún lado del marco, por ejemplo, puede ser la barra de propiedades o la barra de clases, la barra de vista previa, la barra de salida y muchos otros. Classic MFC tiene una buena solución solo para barras de herramientas y otros controles que no se pueden cambiar de tamaño dinámicamente. Pero MFC Feature Pack tiene un administrador de acoplamiento avanzado que permite:
- Cambiar dinámicamente el tamaño de las barras ancladas.
- Reacoplamiento a cualquier lado del marco con vista previa en vivo.
- Serializar el estado y la posición de las barras acopladas en el registro.
- Acoplamiento de barras al mismo lado del marco y recolección de las mismas en el panel con pestañas.
Todo lo que necesita es heredar su barra de acoplamiento de la clase CDockablePane.
Panel de acoplamiento al lado izquierdo del marco.
// Main frame class declaration
class CMainFrame
: public CMDIFrameWndEx
{
DECLARE_DYNAMIC(CMainFrame)
protected:
// declare our pane
CDockablePane m_wndPane;
// .... other class memebers
public:
CMainFrame();
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
// Main frame class implementation
IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWndEx)
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx)
ON_WM_CREATE()
END_MESSAGE_MAP()
CMainFrame::CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;
// enable frame docking to any sides
EnableDocking(CBRS_ALIGN_ANY);
// set the visual manager used to draw all user interface elements
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// enable smart docking style window behavior
CDockingManager::SetDockingMode(DT_SMART);
// Other frame initialization code
// ....
// Creating the pane.
// ID_VIEW_PANE_ID - pane ID, must be declared in resource.h
// CRect(0, 0, 100, 100) - default pane size in floating state (pane is not docked to any frame sides).
// CBRS_LEFT - default side for pane docking.
if (!m_wndPane.Create(_T("My Pane"), this, CRect(0, 0, 100, 100), TRUE, ID_VIEW_PANE_ID, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI)) {
TRACE0("Failed to create Pane\n");
return -1; // failed to create
}
// Enable docking and redocking pane to frame any sides (you can pass a combination of CBRS_ALIGN_ flags)
m_wndPane.EnableDocking(CBRS_ALIGN_ANY);
// Dock pane to the default (left) side of the frame.
DockPane(&m_wndPane);
return 0;
}
Acoplamiento de paneles al marco secundario.
Algunas veces, la aplicación debe tener paneles acoplados no al marco principal, sino al marco secundario. Por lo general, es una aplicación MDI. En el paquete de características de MFC, dicho marco secundario se hereda de la clase CMDIChildWndEx
y como marco principal (heredado de CMDIFrameWndEx
) tiene todo el código necesario para dicho acoplamiento.
Pero hay algunos trucos para el marco infantil. Y este ejemplo los muestra.
// Declare child frame
class CChildFrame : public CMDIChildWndEx
{
DECLARE_DYNCREATE(CChildFrame)
protected:
// declare our pane
CDockablePane m_wndPane;
public:
CChildFrame();
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// CMDIChildWndEx class haven't serialization for state of the docking manager,
// so we need to realize it manually.
//
// Docking state serialization methods:
virtual void SaveBarState(LPCTSTR lpszProfileName) const;
virtual void LoadBarState(LPCTSTR lpszProfileName);
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
};
// CChildFrame Implementation
IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWndEx)
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWndEx)
ON_WM_CREATE()
ON_WM_DESTROY()
END_MESSAGE_MAP()
CChildFrame::CChildFrame()
{
// Trick#1: Add this line for enable floating toolbars
m_bEnableFloatingBars = TRUE;
}
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CMDIChildWndEx::PreCreateWindow(cs) )
return FALSE;
cs.style = WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
| FWS_ADDTOTITLE | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE;
// Trick#2: Add this line for remove the ugly client edge of the child frame.
cs.dwExStyle &= (~WS_EX_CLIENTEDGE);
return TRUE;
}
void CChildFrame::SaveBarState(LPCTSTR lpszProfileName) const
{
const_cast<CChildFrame*>(this)->GetDockingManager()->SaveState(lpszProfileName);
// Trick#3: we need to call serialization method of CMFCToolBar panes that may be docked to the child frame.
CObList list;
const_cast<CChildFrame*>(this)->GetDockingManager()->GetPaneList(list, FALSE, NULL, FALSE);
if (list.GetCount() > 0) {
POSITION pos = list.GetTailPosition();
while (pos != NULL) {
CMFCToolBar* pToolBar = DYNAMIC_DOWNCAST(CMFCToolBar, list.GetPrev(pos));
if (pToolBar != nullptr) {
pToolBar->SaveState(lpszProfileName);
}
}
}
}
void CChildFrame::LoadBarState(LPCTSTR lpszProfileName)
{
// Trick#3: we need to call serialization method of CMFCToolBar panes that may be docked to the child frame.
CObList list;
GetDockingManager()->GetPaneList(list, FALSE, NULL, FALSE);
if (list.GetCount() > 0) {
POSITION pos = list.GetTailPosition();
while (pos != NULL) {
CMFCToolBar* pToolBar = DYNAMIC_DOWNCAST(CMFCToolBar, list.GetPrev(pos));
if (pToolBar != nullptr) {
pToolBar->LoadState(lpszProfileName);
}
}
}
GetDockingManager()->LoadState(lpszProfileName);
GetDockingManager()->SetDockState();
GetDockingManager()->ShowDelayShowMiniFrames(TRUE);
// Trick#4: MFC BUGFIX: force assigning the child frame docking manager to all miniframes (for details look at http://stackoverflow.com/q/39253843/987850).
for (POSITION pos = GetDockingManager()->GetMiniFrames().GetHeadPosition(); pos != NULL;)
{
CWnd* pWndNext = (CWnd*)GetDockingManager()->GetMiniFrames().GetNext(pos);
if (pWndNext != nullptr && pWndNext->IsKindOf(RUNTIME_CLASS(CPaneFrameWnd))) {
STATIC_DOWNCAST(CPaneFrameWnd, pWndNext)->SetDockingManager(GetDockingManager());
}
}
}
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
bool bRes = CMDIChildWndEx::OnCreate(lpCreateStruct) == 0;
if (bRes)
{
// enable docking
EnableDocking(CBRS_ALIGN_ANY);
// enable Visual Studio 2005 style docking window behavior
CDockingManager::SetDockingMode(DT_SMART);
// Creating the pane.
// ID_VIEW_PANE_ID - pane ID, must be declared in resource.h
// CRect(0, 0, 100, 100) - default pane size in floating state (pane is not docked to any frame sides).
// CBRS_LEFT - default side for pane docking.
if (!m_wndPane.Create(_T("My Pane"), this, CRect(0, 0, 100, 100), TRUE, ID_VIEW_PANE_ID, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI)) {
TRACE0("Failed to create Pane\n");
return -1; // failed to create
}
// Enable docking and redocking pane to frame any sides (you can pass a combination of CBRS_ALIGN_ flags)
m_wndPane.EnableDocking(CBRS_ALIGN_ANY);
// Dock pane to the default (left) side of the frame.
DockPane(&m_wndPane);
}
// Loading dock manager state
if (bRes) {
LoadBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
}
return bRes ? 0 : 1;
}
void CChildFrame::OnDestroy()
{
// Save dock manager state
SaveBarState(theApp.GetRegSectionPath(_T("ChildFrame")));
CMDIChildWndEx::OnDestroy();
}