如何使用CTabCtrl来实现多页面属性对话框?

关于在VC中如何使用这个控件,大体思路可以这样:
1)在资源管理器中创建几个Dialog, 分别生成相应的类(基于CDialog),注意,要设置Dialog的Style为"Child", Border为"None";
2)在CDialog::OnInitDialog中,调用CTabCtrl::InsertItem(...)方法添加几个页面
3)调用CDialog的Create方法创建几个对话框变量;
3)在主窗口中,捕获Tab Control的页选择事件,在其中对索引值进行分类,然后就显示你的对话框,注意显示一个的同时,要隐藏其他的对话框。

下面是个测试程序:
 如何使用CTabCtrl来实现多页面属性对话框?_第1张图片

主对话框:

// TmpTstDlg.h : header file
//

#if !defined(AFX_TMPTSTDLG_H__881692D3_4A86_42CD_89F6_1FCB692A13F9__INCLUDED_)
#define AFX_TMPTSTDLG_H__881692D3_4A86_42CD_89F6_1FCB692A13F9__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "TreeDlg.h"
#include "TstDlg.h"
#include "OtherDlg.h"

/////////////////////////////////////////////////////////////////////////////
// CTmpTstDlg dialog

class CTmpTstDlg : public CDialog
{
// Construction
public:
 CTmpTstDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
 //{{AFX_DATA(CTmpTstDlg)
 enum { IDD = IDD_TMPTST_DIALOG };
 CTabCtrl m_ctrlTab;
 //}}AFX_DATA

 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CTmpTstDlg)
 public:
 virtual BOOL PreTranslateMessage(MSG* pMsg);
 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
 //}}AFX_VIRTUAL

// Implementation
protected:
 HICON m_hIcon;
 CMenu m_menuMain;

 void AdjustCtrl();
 // Generated message map functions
 //{{AFX_MSG(CTmpTstDlg)
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon(); 
 afx_msg void OnDestroy();
 afx_msg void OnSize(UINT nType, int cx, int cy);
 afx_msg void OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult);
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 afx_msg void OnAbout();
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
private:
 CTreeDlg m_dlgTree;
 CTstDlg m_dlgTst;
 COtherDlg m_dlgOther;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TMPTSTDLG_H__881692D3_4A86_42CD_89F6_1FCB692A13F9__INCLUDED_)

// TmpTstDlg.cpp : implementation file
//

#include "stdafx.h"
#include "TmpTst.h"
#include "TmpTstDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#pragma comment(lib, "vfw32.lib")
#pragma comment(lib, "User32.lib")
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
 CAboutDlg();

// Dialog Data
 //{{AFX_DATA(CAboutDlg)
 enum { IDD = IDD_ABOUTBOX };
 //}}AFX_DATA

 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CAboutDlg)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
 //}}AFX_VIRTUAL

// Implementation
protected:
 //{{AFX_MSG(CAboutDlg)
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
 //{{AFX_DATA_INIT(CAboutDlg)
 //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CAboutDlg)
 //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 //{{AFX_MSG_MAP(CAboutDlg)
  // No message handlers
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTmpTstDlg dialog

CTmpTstDlg::CTmpTstDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CTmpTstDlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CTmpTstDlg)
  // NOTE: the ClassWizard will add member initialization here
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CTmpTstDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CTmpTstDlg)
 DDX_Control(pDX, IDC_TAB1, m_ctrlTab);
 //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTmpTstDlg, CDialog)
 //{{AFX_MSG_MAP(CTmpTstDlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON() 
 ON_WM_DESTROY()
 ON_WM_SIZE()
 ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnSelchangeTab1)
 ON_WM_CREATE()
 ON_COMMAND(ID_ABOUT, OnAbout)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTmpTstDlg message handlers

BOOL CTmpTstDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // Add "About..." menu item to system menu.

 // IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // Set the icon for this dialog.  The framework does this automatically
 //  when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon
 
 // TODO: Add extra initialization here
 WORD wVersion = 0;
 WSAData sttWsa;
 CString m_strErrMsg;

 memset(&sttWsa, 0, sizeof(WSAData));
 wVersion = MAKEWORD(2, 0); 
 if (0 != WSAStartup(wVersion, &sttWsa))
 {
  m_strErrMsg.Format("Fail to initialize socket of version [%u], and error no is [%ld]",
   wVersion, WSAGetLastError());
  MessageBox(m_strErrMsg,  "Initialization Error", MB_OK + MB_ICONSTOP);
  WSACleanup();
  return -1;
 }

 /*m_hMenu = ::LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MENU1));
 ASSERT(m_hMenu);
 CMenu* pMenu = CMenu::FromHandle(hMenu);
 ASSERT(pMenu);
 SetMenu(pMenu);
 */

 ASSERT(m_menuMain.LoadMenu(IDR_MENU1));
 ASSERT(SetMenu(&m_menuMain));

 this->AdjustCtrl();

 //add pages for tab
 BOOL bRet = FALSE;
 bRet = m_ctrlTab.InsertItem(0, _T("树操作测试"));
 bRet = m_ctrlTab.InsertItem(1, _T("临时测试"));
 bRet = m_ctrlTab.InsertItem(2, "其他测试");
 ASSERT(bRet);

 /*CRect rc;
 m_ctrlTab.GetClientRect(rc); 
 
 rc.top+=20;
 rc.bottom-=4;
 rc.left+=4;
 rc.right-=4;
 
 //adjust size
 m_dlgTree.MoveWindow(rc);
 m_dlgTst.MoveWindow(rc);
 m_dlgOther.MoveWindow(rc);
 
 m_dlgTree.ShowWindow(SW_SHOW);
 m_ctrlTab.SetCurSel(0);//set focus on first page
 */

 return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTmpTstDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTmpTstDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting

  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;

  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
  //this->AdjustCtrl();
 }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTmpTstDlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}

int CTmpTstDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CDialog::OnCreate(lpCreateStruct) == -1)
  return -1;
 
 // TODO: Add your specialized creation code here
 ASSERT(m_dlgTree.Create(IDD_DIALOG1, GetDlgItem(IDC_TAB1)));
 ASSERT(m_dlgTst.Create(IDD_DIALOG2, GetDlgItem(IDC_TAB1)));
 ASSERT(m_dlgOther.Create(IDD_DIALOG3, GetDlgItem(IDC_TAB1)));
 return 0;
}

void CTmpTstDlg::OnDestroy()
{
 CDialog::OnDestroy();
 
 // TODO: Add your message handler code here
 m_menuMain.DestroyMenu();
 m_dlgTree.DestroyWindow();
 m_dlgTst.DestroyWindow();
 m_dlgOther.DestroyWindow();
}

void CTmpTstDlg::AdjustCtrl()
{
 /*if (GetMenu())
 {
  SetMenu(NULL);
 }*/
 
 int iCapHgh = ::GetSystemMetrics(SM_CYCAPTION);//caption height:19(default)
 TRACE("Caption height is %ld in pixels/n", iCapHgh);
 int iBordWid = ::GetSystemMetrics(SM_CXBORDER);//border width:1(default)
 int iBordHgh = ::GetSystemMetrics(SM_CYBORDER);//border height:1(default)
 TRACE("Horizonal border width is %ld in pixels/n", iBordWid);
 TRACE("Vertical border height is %ld in pixels/n", iBordHgh);

 CRect rcClient(0, 0, 0, 0);
 GetClientRect(rcClient);
 
 CWnd* pWndOk = GetDlgItem(IDOK);
 CWnd* pWndCancel = GetDlgItem(IDCANCEL);
 ASSERT_VALID(pWndOk);
 ASSERT_VALID(pWndCancel);
 CRect rcBtnOk(0, 0, 0, 0);
 CRect rcBtnCancel(0, 0, 0, 0);

 pWndOk->GetWindowRect(rcBtnOk);
 ScreenToClient(rcBtnOk);
 pWndOk->GetWindowRect(rcBtnCancel);
 ScreenToClient(rcBtnCancel);
 
 //calculate coordinates of 'IDOK' button
 int iBtnWid = rcBtnOk.Width();
 int iBtnHgh = rcBtnOk.Height();
 rcBtnOk.left = rcClient.right - 2*iBtnWid /*- iBordWid*/ - 1;
 rcBtnOk.top = rcClient.bottom - iBtnHgh/* - iBordHgh*/;
 rcBtnOk.right = rcBtnOk.left + iBtnWid;
 rcBtnOk.bottom = rcBtnOk.top + iBtnHgh;
 //calculate coordinates of 'IDCANCEL' button
 rcBtnCancel.left = rcBtnOk.right + 1;
 rcBtnCancel.top = rcBtnOk.top;
 rcBtnCancel.right = rcBtnCancel.left + iBtnWid;
 rcBtnCancel.bottom = rcBtnOk.bottom;
 BOOL bRet = pWndOk->SetWindowPos(NULL, rcBtnOk.left, rcBtnOk.top,
  rcBtnOk.Width(), rcBtnOk.Height(), SWP_NOSIZE | SWP_NOZORDER); 
 ASSERT(bRet);
 bRet = pWndCancel->SetWindowPos(NULL, rcBtnCancel.left, rcBtnCancel.top,
  rcBtnCancel.Width(), rcBtnCancel.Height(), SWP_NOSIZE | SWP_NOZORDER);
 ASSERT(bRet);
 
 CRect rcTab(rcClient);
 rcTab.bottom = rcBtnOk.top - 1;
 m_ctrlTab.MoveWindow(rcTab);

 //ajust coordinates
 CRect rc;
 m_ctrlTab.GetClientRect(rc); 
 rc.top+=20;
 rc.bottom-=4;
 rc.left+=4;
 rc.right-=4;
 
 //adjust per page size
 m_dlgTree.MoveWindow(rc);
 m_dlgTst.MoveWindow(rc);
 m_dlgOther.MoveWindow(rc);
 
 //show first page at first
 m_dlgTree.ShowWindow(SW_SHOW);
 m_ctrlTab.SetCurSel(0);//set focus on first page
}

void CTmpTstDlg::OnSize(UINT nType, int cx, int cy)
{  
 // TODO: Add your message handler code here
 CDialog::OnSize(nType, cx, cy);
 
 if (SIZE_MAXIMIZED == nType)
 {
  this->AdjustCtrl();
 }

 if (NULL != GetDlgItem(IDOK))//very mild, modified
 {
  if (SIZE_RESTORED == nType)
  {
   this->AdjustCtrl();
  } 
 }
}

void CTmpTstDlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
 // TODO: Add your control notification handler code here
 int iIndex = m_ctrlTab.GetCurSel();
 switch (iIndex)
 {
  case -1:
  {
   break;
  }
  case 0:
  {          
   m_dlgTree.ShowWindow(SW_SHOW);
   m_dlgTst.ShowWindow(SW_HIDE);
   m_dlgOther.ShowWindow(SW_HIDE);
   break;
  }
  case 1:
  {  
   m_dlgTree.ShowWindow(SW_HIDE);
   m_dlgTst.ShowWindow(SW_SHOW);
   m_dlgOther.ShowWindow(SW_HIDE);
   break;
  }
  case 2:
  { 
   m_dlgTree.ShowWindow(SW_HIDE);
   m_dlgTst.ShowWindow(SW_HIDE);
   m_dlgOther.ShowWindow(SW_SHOW);
   break;
  }
  default:
  {
   break;
  }
 }
 *pResult = 0;
}

void CTmpTstDlg::OnAbout()
{
 // TODO: Add your command handler code here
 CAboutDlg dlgAbout;
 dlgAbout.DoModal();
}

BOOL CTmpTstDlg::PreTranslateMessage(MSG* pMsg)
{
 // TODO: Add your specialized code here and/or call the base class
 if (pMsg->message == WM_KEYDOWN)
 {
  if (VK_ESCAPE == pMsg->wParam)
  {
   return FALSE;
  }
 }
 return CDialog::PreTranslateMessage(pMsg);
}

你可能感兴趣的:(如何使用CTabCtrl来实现多页面属性对话框?)