CDockablePane and CanBeClosed()

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.

CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 22 Mai 2017, 14:17

Am o aplicatie MDI, iar in CChildFrame am pus 2 CDockablePane, cu proprietatea sa nu pota fi inchise:
Cod: Selectaţi tot
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 ....
Fişiere ataşate
Model.zip
(225.74 KiB) Descărcat de 40 ori
mesajflaviu
Membru++
Membru++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 06 Iun 2017, 10:08

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 ?
mesajflaviu
Membru++
Membru++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Mesajde Ovidiu Cucu » 09 Iun 2017, 17:50

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:
Cod: Selectaţi tot
#define AFX_NON_CLOSE_DOCKING_PANE_STYLE   AFX_CBRS_FLOAT | AFX_CBRS_RESIZE | AFX_CBRS_AUTOHIDE

Cod: Selectaţi tot
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.
Cod: Selectaţi tot
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! :)
Avatar utilizator
Ovidiu Cucu
Fondator
Fondator
 
Mesaje: 3776
Membru din: 11 Iul 2007, 16:10
Localitate: Iasi
Judet: Iaşi

Re: CDockablePane and CanBeClosed()

Mesajde Ovidiu Cucu » 12 Iun 2017, 19:14

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.
Cod: Selectaţi tot
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);
}
Avatar utilizator
Ovidiu Cucu
Fondator
Fondator
 
Mesaje: 3776
Membru din: 11 Iul 2007, 16:10
Localitate: Iasi
Judet: Iaşi

Re: CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 15 Iun 2017, 11:12

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:

Cod: Selectaţi tot
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
Cod: Selectaţi tot
void CChildFrame::OnDestroy()
{
   m_dockManager.SaveState(theApp.GetRegSectionPath(_T("ChildFrame")));

   CMDIChildWndEx::OnDestroy();
}


Problema vine in momentul cand, dupa ce am restaurat panelu', apelez
Cod: Selectaţi tot
pTabbedBar->SetControlBarStyle(AFX_NON_CLOSE_DOCKING_PANE_STYLE);

adica:
Cod: Selectaţi tot
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;
}
Fişiere ataşate
Model.zip
(217.66 KiB) Descărcat de 17 ori
mesajflaviu
Membru++
Membru++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 15 Iun 2017, 13:50

Am cautat in sursele MFC, care "dockeaza" panel-urile:
Cod: Selectaţi tot
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:
Cod: Selectaţi tot
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++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 19 Iun 2017, 18:56

In sursele CBasePane::LoadState nu este nimic special, in stare sa crape acolo ...
Cod: Selectaţi tot
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++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj

Re: CDockablePane and CanBeClosed()

Mesajde mesajflaviu » 09 Iul 2017, 14:02

No fi bine cum salvez starea si pozitia panelurilor din childframe ?
Cod: Selectaţi tot
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++
 
Mesaje: 681
Membru din: 10 Sep 2008, 21:40
Judet: Cluj


Înapoi la MFC, ATL, WTL si GDI+.

Cine este conectat

Utilizatorii ce navighează pe acest forum: Niciun utilizator înregistrat şi 2 vizitatori

cron