先在我們的選項卡可以説能用了,每個標籤頁都能點進去,但是這還遠遠沒到能用的地步,比如説你把窗口最大化後。

vitepress 選項卡_控件

立馬就露出馬腳了,所以這篇我們要先講講tabctrl的最基本的功能實現

 

改變選項卡大小

上圖的原因就是主窗口在改變的大小的時候沒有通知選項卡讓他跟着主窗口一起變,所以我們現在通知選項卡一下

添加ON_WM_SIZE消息,實現函數

void CtabView::OnSize(UINT nType, int cx, int cy)
{
    //CFormView::OnSize(nType, cx, cy);

    if (m_tab && m_tab.GetSafeHwnd())
    {                                                  
        m_tab.SetWindowPos (NULL, -1, -1, cx, cy,
            SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
    }
}

在主窗體改變大小的時候,會時時通知選項卡,主窗體我已經變成多大了(cx,cy),你趕快跟上。

 

改變控件位置

vitepress 選項卡_句柄_02

選項卡現在已經可以跟着主窗體一起變了,但是發現沒有,裏面的控件的位置卻沒有變,如果裏面的控件位置都沒變,那我們最大化還有什麼意義呢?所以我們還得通知子窗體裏面的控件“子窗體大小已經改變了,你控件也趕快挪位置跟上。”

既然是子窗體裏的控件,那肯定是要在子窗體去實現,但是一個子窗口會有很多控件,我們一個一個去取句柄然後再通知他們會不會很麻煩,當然麻煩,但是我們有簡單方法。

GetWindow函數會給我們返回子窗口裏所有控件ID,這樣我們就不用一個一個找了。所以我們只需要一個循環就能改變控件位置和大小

HWND GetWindow(HWND hWnd,UNIT nCmd)。

參數説明:
hWnd:窗口句柄。這個函數要返回的窗口句柄是依據nCmd參數值相對於hWnd參數的關係。
nCmd:説明指定窗口與要獲得句柄的窗口之間的關係。該參數值可以是下列之一:
GW_CHILD(&H5):如果指定窗口是父窗口,則獲得的是在Tab序頂端的子窗口的句柄,否則為NULL。函數僅檢查指定父窗口的子窗口,不檢查繼承窗口。

GW_ENABLEDPOPUP(&H6):(WindowsNT 5.0)返回的句柄標識了屬於指定窗口的處於使能狀態彈出式窗口(檢索使用第一個由GW_HWNDNEXT 查找到的滿足前述條件的窗口);
如果無使能窗口,則獲得的句柄與指定窗口相同。

GW_HWNDFIRST(&H0):返回的句柄標識了在Z序最高端的相同類型的窗口。如果指定窗口是最高端窗口,則該句柄標識了在Z序最高端的最高端窗口;
如果指定窗口是頂層窗口,則該句柄標識了在z序最高端的頂層窗口:如果指定窗口是子窗口,則句柄標識了在Z序最高端的同屬窗口。

GW_HWNDLAST(&H1):返回的句柄標識了在z序最低端的相同類型的窗口。如果指定窗口是最高端窗口,則該柄標識了在z序最低端的最高端窗口:
如果指定窗口是頂層窗口,則該句柄標識了在z序最低端的頂層窗口;如果指定窗口是子窗口,則句柄標識了在Z序最低端的同屬窗口。

GW_HWNDNEXT(&H2):返回的句柄標識了在Z序中指定窗口下的相同類型的窗口。如果指定窗口是最高端窗口,則該句柄標識了在指定窗口下的最高端窗口:
如果指定窗口是頂層窗口,則該句柄標識了在指定窗口下的頂層窗口;如果指定窗口是子窗口,則句柄標識了在指定窗口下的同屬窗口。

GW HWNDPREV(&H3):返回的句柄標識了在Z序中指定窗口上的相同類型的窗口。如果指定窗口是最高端窗口,則該句柄標識了在指定窗口上的最高端窗口;
如果指定窗口是頂層窗口,則該句柄標識了在指定窗口上的頂層窗口;如果指定窗口是子窗口,則句柄標識了在指定窗口上的同屬窗口。

GW_OWNER(&H4):返回的句柄標識了指定窗口的所有者窗口(如果存在)。GW_OWNER與GW_CHILD不是相對的參數,沒有父窗口的含義,如果想得到父窗口請使用GetParent()。
例如:例如有時對話框的控件的GW_OWNER,是不存在的。

返回值:如果函數成功,返回值為窗口句柄;如果與指定窗口有特定關係的窗口不存在,則返回值為NULL。

若想獲得更多錯誤信息,請調用GetLastError函數。

 

 

添加一個Point 成員變量記錄控件以前的小

POINT m_old;

創建一個chang函數並在OnSize中調用 

void CDialog1::ChangeSize(/*CWnd * pWnd, int cx, int cy*/)
{
    float fsp[2];  
    POINT Newp; //獲取現在對話框的大小  
    CRect recta;      
    GetClientRect(&recta);     //取客户區大小    
    Newp.x=recta.right-recta.left;  
    Newp.y=recta.bottom-recta.top;  
    fsp[0]=(float)Newp.x/m_old.x;  
    fsp[1]=(float)Newp.y/m_old.y;  
    CRect Rect;  
    int woc;  
    CPoint OldTLPoint,TLPoint; //左上角  
    CPoint OldBRPoint,BRPoint; //右下角  
    HWND  hwndChild=::GetWindow(m_hWnd,GW_CHILD);  //列出所有控件    

    while(hwndChild)      
    {      
        woc=::GetDlgCtrlID(hwndChild);//取得ID 
        GetDlgItem(woc)->GetWindowRect(Rect);    
        ScreenToClient(Rect);    
        OldTLPoint = Rect.TopLeft();    
        TLPoint.x = long(OldTLPoint.x*fsp[0]);    
        TLPoint.y = long(OldTLPoint.y*fsp[1]);    
        OldBRPoint = Rect.BottomRight();    
        BRPoint.x = long(OldBRPoint.x *fsp[0]);    
        BRPoint.y = long(OldBRPoint.y *fsp[1]);    
        Rect.SetRect(TLPoint,BRPoint);    
        GetDlgItem(woc)->MoveWindow(Rect,TRUE);  
        hwndChild=::GetWindow(hwndChild, GW_HWNDNEXT);      
    }  
    m_old=Newp;  
}

現在感覺好多了,中間的文本框字體需要另外的代碼去改變它

vitepress 選項卡_句柄_03

 

實現關閉標籤頁功能

最後有沒有發現,標籤頁上的按鈕是沒有反應的,這是我們沒有實現這個按鈕

添加一個MFC類CMFCTabCtrlEx,繼承與CMFCTabCtrl

給CMFCTabCtrlEx類添加ON_WM_LBUTTONDOWN()事件,因為是動態創建選項卡,所以這個事件只能自己手寫。

//.h


class CMFCTabCtrlEx : public CMFCTabCtrl
{
    DECLARE_DYNAMIC(CMFCTabCtrlEx)

public:
    CMFCTabCtrlEx();
    virtual ~CMFCTabCtrlEx();

protected:
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

 

//.cpp// MFCTabCtrlEx.cpp : 實現文件
//#include "stdafx.h"
#include "MFCTabCtrlEx.h"// CMFCTabCtrlEx
IMPLEMENT_DYNAMIC(CMFCTabCtrlEx, CMFCTabCtrl)
CMFCTabCtrlEx::CMFCTabCtrlEx()
{}
CMFCTabCtrlEx::~CMFCTabCtrlEx()
{
}BEGIN_MESSAGE_MAP(CMFCTabCtrlEx, CMFCTabCtrl)
    ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void CMFCTabCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息處理程序代碼和/或調用默認值  
    CMFCTabCtrl::OnLButtonDown(nFlags, point);

    // 獲取Tab的數量
    int n = GetTabsNum();

    if (n == 0) return;

    CRect rc;

    // 獲取整個Tab區域
    CalcRectEdit(rc);
    GetTabsRect(rc);
    // 獲取當前選項卡窗口的指針
    CWnd *pTabWnd = GetActiveWnd();
    pTabWnd->GetClientRect(rc);

    int sel;

    // 獲取當前選擇的Tab
    sel = GetActiveTab();

    // 獲取單個Tab區域
    GetTabRect(sel, rc);

    // 獲取單個Tab的最大寬度
    int nWidth = GetTabMaxWidth();

    // 獲取關閉按鈕的區域
    CRect rcCloseBtn;
    rcCloseBtn = GetTabCloseButton();

    if (rcCloseBtn.PtInRect(point))
    {
        if(MessageBox(_T("確認關閉該標籤頁?"), _T("關閉提示"), MB_YESNO) == IDYES)
        {
            // 關閉Tab頁關聯窗口        
            pTabWnd->SendMessage(WM_CLOSE);  
            // 刪除Tab  
            RemoveTab(sel);
        }
    }
           
    

}

將主窗口選項卡基類替換為CMFCTabCtrlEx就完成了。

 

vitepress 選項卡_控件_04

vitepress 選項卡_控件_05