CDockablePane and CanBeClosed()

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.
Post Reply
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

CDockablePane and CanBeClosed()

Post by mesajflaviu »

Am o aplicatie MDI, iar in CChildFrame am pus 2 CDockablePane, cu proprietatea sa nu pota fi inchise:

Code: Select all

virtual BOOL CanBeClosed() const {return FALSE;}
Cand sant dockate separate, functioneaza ok:
http://imgur.com/8fL9iV2
Cand insa sant dockate sub acelasi tab, reapare acel buton de close:
http://imgur.com/ULMWMA9
si evident, se pot inchide aceste paneluri ...
Este in bug in MFC ? Cum as putea rezolva aceasta problema ?
Atasez in mic proiect de test, care ilustreaza aceasta problema ....
Attachments
Model.zip
(225.74 KiB) Downloaded 728 times


mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

Nu stiu cum s-ar putea rezolva acest lucru, pentru ca daca unul dintre panel-uri are setat butonul de inchidere, iar celalalt nu, combinatia lor intr-un singur tab, ce sa faca ? Sa arate sau nu butonul de inchidere ?
User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDockablePane and CanBeClosed()

Post by Ovidiu Cucu »

La grea treaba te-ai inhamat... :)

Asazisul "Feature Pack" cu riboane, paneluri docabile, tabbed views si alte cularai (preluat de la BCGSoft) este minunat atata timp cat il folosesti asa cum e, insa poate fi un adevarat overkill daca vrei sa-l customizezi. Nu prea am avut de-a face cu el insa mi-a scos peri albi o biblioteca similara de la o "firma concurenta" numita Codejock.
La urma urmei, (aproape) nimic nu-i imposibil, insa necesita putintica transpiratie.
Si noroc ca in Feature Pack nu si-a bagat coada un super-architect de Codejock care sa faca viata amara programerilor.
Asa ca hai sa vedem! :)

Nu trebuie sa suprascrii CBasePane::CanBeClosed. O lasi in plata Domnului asa cum e de la mama ei si scoti flag-ul AFX_CBRS_CLOSE din parametrul dwControlBarStyle pasat functiei CDockablePane::Create. In cazul in care panelul a fost deja creat de framework cu stilul AFX_DEFAULT_DOCKING_PANE_STYLE (care contine si AFX_CBRS_CLOSE), atunci poti apela CBasePane::SetControlBarStyle.

Exemplu:

Code: Select all

#define AFX_NON_CLOSE_DOCKING_PANE_STYLE   AFX_CBRS_FLOAT | AFX_CBRS_RESIZE | AFX_CBRS_AUTOHIDE

Code: Select all

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CMDIChildWndEx::OnCreate(lpCreateStruct) == -1)
        return -1;

    EnableDocking(CBRS_ALIGN_ANY);

    CRect rcPane(0, 0, 200, 200);
    CString strCaption;

    VERIFY(strCaption.LoadString(IDS_FILE_VIEW));
    VERIFY(m_wndFileView.Create(strCaption, this, rcPane, TRUE, ID_VIEW_FILEVIEW, 
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI,
        AFX_CBRS_REGULAR_TABS, 
        AFX_NON_CLOSE_DOCKING_PANE_STYLE));

    VERIFY(strCaption.LoadString(IDS_CLASS_VIEW));
    VERIFY(m_wndClassView.Create(strCaption, this, rcPane, TRUE, ID_VIEW_CLASSVIEW, 
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI,
        AFX_CBRS_REGULAR_TABS, 
        AFX_NON_CLOSE_DOCKING_PANE_STYLE));

    m_wndFileView.EnableDocking(CBRS_ALIGN_ANY);
    m_wndClassView.EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndFileView);
    CDockablePane* pTabbedBar = NULL;
    CDockablePane* pTabbedPane = m_wndClassView.AttachToTabWnd(&m_wndFileView, DM_SHOW, TRUE, &pTabbedBar);

    if (NULL == pTabbedBar)
        return -1;

    // remove AFX_CBRS_CLOSE flag
    pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);

    return 0;
}
Nota: de observat ca am dat ID-uri valide panelurilor.

A mai ramas o mica chestie de facut deoarece CDockablePane::CreateTabbedPane copie stilul din panel insa pe control bar style il seteaza tot cu AFX_DEFAULT_DOCKING_PANE_STYLE. Noroc ca-i virtuala.

Code: Select all

CTabbedPane* CClassView::CreateTabbedPane()
{
    CTabbedPane* pTabbedPane = __super::CreateTabbedPane();
    ASSERT_VALID(pTabbedPane);
    // remove AFX_CBRS_CLOSE flag;
    pTabbedPane->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);
    return pTabbedPane;
}
Faci asta pentru toate panelurile care nu trebuie sa contina butonel de close sau mai bine derivezi dintr-o clasa comuna care suprascrie CreateTabbedPane.

Simplu...
Vezi acum daca merje! :)
User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: CDockablePane and CanBeClosed()

Post by Ovidiu Cucu »

Ne apropiem, insa tot se mai poate inchide un panel flotant, daca este activ iar userul apala ALT + F4.
Asta se poate evita supraescriind PreTranslateMessage.

Code: Select all

BOOL CNonClosableDockablePane::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_SYSKEYDOWN  && pMsg->wParam == VK_F4)
    {
        // prevent closing the floating pane when the user presses Alt + F4
        return TRUE;
    }
    return CDockablePane::PreTranslateMessage(pMsg);
}
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

Multumesc mult pentru solutie.

Merge ok, mai am in singur lucru de rezolvat, in momentul cand folosesc cod pentru salvarea acelor panel-uri, intra in conflict cu codul care scaote optiunea de close al pTabbedBar.

Pentru a salva pozitia si starea panel-urilor, folosesc:

Code: Select all

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CMDIChildWndEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	.........

	m_dockManager.LoadState(theApp.GetRegSectionPath(_T("ChildFrame")));
	m_dockManager.SetDockState();

	return 0;
}
si

Code: Select all

void CChildFrame::OnDestroy()
{
	m_dockManager.SaveState(theApp.GetRegSectionPath(_T("ChildFrame")));

	CMDIChildWndEx::OnDestroy();
}
Problema vine in momentul cand, dupa ce am restaurat panelu', apelez

Code: Select all

pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);
adica:

Code: Select all

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CMDIChildWndEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// enable Visual Studio 2005 style docking window behavior
	CDockingManager::SetDockingMode(DT_SMART);
	// enable Visual Studio 2005 style docking window auto-hide behavior
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	CMDIChildWndEx::m_bEnableFloatingBars = TRUE;

	// Create properties window
	CString strPropertiesWnd;
	BOOL bNameValid = strPropertiesWnd.LoadString(IDS_PROPERTIES_WND);
	ASSERT(bNameValid);
	if(! m_wndProperties.Create(strPropertiesWnd, this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_PROPERTIESWND, 
		WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI, 
		AFX_CBRS_REGULAR_TABS, AFX_NON_CLOSE_DOCKING_PANE_STYLE))
	{
		TRACE(_T("Failed to create Properties window\n"));
		return FALSE; // failed to create
	}
	if(! m_wndFilter.Create(_T("Filter"), this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_FILTER, 
		WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI, 
		AFX_CBRS_REGULAR_TABS, AFX_NON_CLOSE_DOCKING_PANE_STYLE))
	{
		TRACE(_T("Failed to create Filter window\n"));
		return FALSE; // failed to create
	}

	SetDockingWindowIcons(theApp.m_bHiColorIcons);

	AddPane(&m_wndFilter);
	AddPane(&m_wndProperties);
	m_wndFilter.EnableDocking(CBRS_ALIGN_ANY);
	m_wndProperties.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_wndFilter);
	CDockablePane* pTabbedBar = NULL;
	m_wndProperties.AttachToTabWnd(&m_wndFilter, DM_SHOW, TRUE, &pTabbedBar);
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	m_dockManager.LoadState(theApp.GetRegSectionPath(_T("ChildFrame")));
	m_dockManager.SetDockState();

	if(NULL != pTabbedBar)	// remove AFX_CBRS_CLOSE flag
		pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);    // <--- An unhandled exception was encountered during a user callback.

	return 0;
}
Attachments
Model.zip
(217.66 KiB) Downloaded 641 times
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

Am cautat in sursele MFC, care "dockeaza" panel-urile:

Code: Select all

void CPane::SetDockState(CDockingManager* pDockManager)
{
	ASSERT_VALID(this);

	if (!m_bRecentFloatingState)
	{
		CDockSite* pDockBar = pDockManager->FindDockSite(m_recentDockInfo.m_dwRecentAlignmentToFrame, TRUE);

		if (pDockBar != NULL)
		{
			pDockManager->DockPane(this, pDockBar->GetDockSiteID(), m_recentDockInfo.m_recentSliderInfo.m_rectDockedRect);
		}

		if (m_pParentDockBar != NULL)
		{
			m_pParentDockBar->ShowPane(this, GetRecentVisibleState(), TRUE, FALSE);
			if (m_pDockBarRow != NULL)
			{
				m_pDockBarRow->ExpandStretchedPanes();
			}
		}
	}
}
Nimic care sa influienteze stilul panelui ... nici in cel care incarca panelul:

Code: Select all

BOOL CPane::LoadState(LPCTSTR lpszProfileName, int nIndex, UINT uiID)
{
	CString strProfileName = ::AFXGetRegPath(strControlBarProfile, lpszProfileName);

	if (nIndex == -1)
	{
		nIndex = GetDlgCtrlID();
	}

	CString strSection;
	if (uiID == (UINT) -1)
	{
		strSection.Format(AFX_REG_SECTION_FMT, (LPCTSTR)strProfileName, nIndex);
	}
	else
	{
		strSection.Format(AFX_REG_SECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
	}

	CSettingsStoreSP regSP;
	CSettingsStore& reg = regSP.Create(FALSE, TRUE);

	if (!reg.Open(strSection))
	{
		return FALSE;
	}

	reg.Read(_T("ID"), (int&) m_nID);

	reg.Read(_T("RectRecentFloat"), m_recentDockInfo.m_rectRecentFloatingRect);
	reg.Read(_T("RectRecentDocked"), m_rectSavedDockedRect);

	// !!!!!! change to appropriate handling for slider/frame
	m_recentDockInfo.m_recentSliderInfo.m_rectDockedRect = m_rectSavedDockedRect;

	reg.Read(_T("RecentFrameAlignment"), m_recentDockInfo.m_dwRecentAlignmentToFrame);
	reg.Read(_T("RecentRowIndex"), m_recentDockInfo.m_nRecentRowIndex);
	reg.Read(_T("IsFloating"), m_bRecentFloatingState);
	reg.Read(_T("MRUWidth"), m_nMRUWidth);
	reg.Read(_T("PinState"), m_bPinState);

	return CBasePane::LoadState(lpszProfileName, nIndex, uiID);
}
si acel painter, pTabbedPane, este valid la acel apel.
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

In sursele CBasePane::LoadState nu este nimic special, in stare sa crape acolo ...

Code: Select all

BOOL CBasePane::LoadState(LPCTSTR lpszProfileName, int nIndex, UINT uiID)
{
	CString strProfileName = ::AFXGetRegPath(strBaseControlBarProfile, lpszProfileName);

	if (nIndex == -1)
	{
		nIndex = GetDlgCtrlID();
	}

	CString strSection;
	if (uiID == (UINT) -1)
	{
		strSection.Format(AFX_REG_SECTION_FMT, (LPCTSTR)strProfileName, nIndex);
	}
	else
	{
		strSection.Format(AFX_REG_SECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
	}

	CSettingsStoreSP regSP;
	CSettingsStore& reg = regSP.Create(FALSE, TRUE);

	if (!reg.Open(strSection))
	{
		return FALSE;
	}

	reg.Read(_T("IsVisible"), m_bRecentVisibleState);
	m_bIsRestoredFromRegistry = TRUE;

	return TRUE;
}
Revin cu noutati, dupa sapaturi noi ...
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

N-o fi bine cum salvez starea si pozitia panelurilor din childframe ?

Code: Select all

m_dockManager.LoadState(theApp.GetRegSectionPath(_T("ChildFrame")));
m_dockManager.SetDockState();
Pana la urma, msdn-ul nu da nici in exemplu care sa sugereze asta, ci doar situatia cand panelu este parte a CMainFrame-ului ...
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

Am gasit in workaround, care aproape functioneaza :)

In CChildFrame::OnCreate am lasat doar restaurarea panelurilor:

Code: Select all

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CMDIChildWndEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// enable Visual Studio 2005 style docking window behavior
	CDockingManager::SetDockingMode(DT_SMART);
	// enable Visual Studio 2005 style docking window auto-hide behavior
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	CMDIChildWndEx::m_bEnableFloatingBars = TRUE;

	// Create properties window
	CString strPropertiesWnd;
	BOOL bNameValid = strPropertiesWnd.LoadString(IDS_PROPERTIES_WND);
	ASSERT(bNameValid);
	if(! m_wndProperties.Create(strPropertiesWnd, this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_PROPERTIESWND, 
		WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI, 
		AFX_CBRS_REGULAR_TABS, AFX_NON_CLOSE_DOCKING_PANE_STYLE))
	{
		TRACE(_T("Failed to create Properties window\n"));
		return FALSE; // failed to create
	}
	if(! m_wndFilter.Create(_T("Filter"), this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_FILTER, 
		WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI, 
		AFX_CBRS_REGULAR_TABS, AFX_NON_CLOSE_DOCKING_PANE_STYLE))
	{
		TRACE(_T("Failed to create Filter window\n"));
		return FALSE; // failed to create
	}

	SetDockingWindowIcons(theApp.m_bHiColorIcons);

	AddPane(&m_wndFilter);
	AddPane(&m_wndProperties);
	m_wndFilter.EnableDocking(CBRS_ALIGN_ANY);
	m_wndProperties.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_wndFilter);
	CDockablePane* pTabbedBar = NULL;
	m_wndProperties.AttachToTabWnd(&m_wndFilter, DM_SHOW, TRUE, &pTabbedBar);
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	m_dockManager.LoadState(theApp.GetRegSectionPath(_T("ChildFrame")));
	m_dockManager.SetDockState();

	PostMessage(WMU_POSTINIT);

	return 0;
}
iar dupa creere (prin PostMessage) incerc sa scot afara butonul de close:

Code: Select all

LRESULT CChildFrame::OnPostInit(WPARAM wParam, LPARAM lParam)
{
	CDockablePane* pTabbedBar = (CDockablePane*)m_wndFilter.GetParentTabbedPane();
	if(NULL != pTabbedBar)	// remove AFX_CBRS_CLOSE flag
	{
		pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);
		pTabbedBar->RecalcLayout();
	}

	return 1;
}
de ce zic ca aproape merge ? Pentru ca ar mai trebui redesenat pTabbedBar si ar fi ok ... caci daca schimb tab-urile panelurilor, butonul de close dispare, semn ca e removed ... am incercat si cu

Code: Select all

pTabbedBar->Invalidate();
nu merge ... am atasat si proiectul de test ... imi puteti da o mana de ajutor ?
Attachments
Model.zip
(217.79 KiB) Downloaded 596 times
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Post by mesajflaviu »

Am rezolvat pana la urma (scriu aici solutia pentru cei care vor avea acelasi issue):

Code: Select all

	CDockablePane* pTabbedBar = NULL;
	m_wndProperties.AttachToTabWnd(&m_wndFilter, DM_SHOW, TRUE, &pTabbedBar);
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	m_dockManager.LoadState(theApp.GetRegSectionPath(_T("ChildFrame")));
	m_dockManager.SetDockState();

	pTabbedBar = (CDockablePane*)m_wndFilter.GetParentTabbedPane();
	if(NULL != pTabbedBar->GetSafeHwnd())
		pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);
pare ca merge ok.
Post Reply