Статья
Версия для печати
Обсудить на форуме
Наборы вкладок (Tab Controls)


   Доброе время суток.
   Данная статья будет полезна (надеюсь :) ) людям , пишущим в среде VC++6 с использованием библиотеки MFC.

   Тема статьи - наборы вкладок. Создание, вставка в проект, приёмы работы с ними. Вкладки очень удобны в том случае, когда большое количество разнородных свойств чего либо надо разместить в виде смысловых групп (за примером ходить далеко не нужно -  в студии нажмите Alt+F7). Да и много ещё можно применений придумать наверное.
   Основная идея работы набора вкладок такова : имеется объект TAB, который выглядит как пустое диалоговое окно , у которого сверху есть набор вкладок-корешков с надписями. По сути , это просто набор переключателей. С каждым переключателем связан некий объект (назовём SHEET), производный в общем случае от CWnd , который должен быть показан на окне TAB, когда переключатель выбран. Таким образом, в каждый момент времени виден только один объект SHEET из набора.

   Здесь будет описана работа с классом MFC - CTabCtrl.
   В тестовом проекте будет использована следующая схема:

CTabsTestDlg  (главное окно проекта)
   
CTabCtrl
      
страница 1страница 2страница 3страница 4
CMyTabDialogCMyTabDialogCMyTabDialogCMyTabDialog
   

   CTabsTestDlg - это окно, на котором лежит контрол набора вкладок CTabCtrl. Этот контрол - это родительское окно для всех страниц. Все страницы произведены от класса CMyTabDialog:CDialog, о котором ниже.

   Готовый тестовый проект можно взять из по ссылке в конце статьи, но здесь всё равно описаны шаги создания набора вкладок.
   Итак, создаём тестовый проект ( MFC, Dialod-based ) с названием TabsTest. Открываем в редакторе ресурсов главное диалоговое окно IDD_TABSTEST_DIALOG. Берём из стандартной палитры элементов управления контрол с названием Tab Control и кладём на диалог.  Дадим контролу идентификатор IDC_TAB. Теперь добавим связанный с контролов член-переменную (можно было, конечно, и не делать этого, но так немного удобнее :) ; в общем - добавляем)  , нажимая Ctrl+двойной_щелчок_по_контролу. Имя переменной, недолго думая, делаем, приписывая "m_" к идентификатору  - m_IDC_TAB. В окошке Category уже указано control , ниже выбираем класс CTabCtrl.
   Теперь посмотрим, как вставить вкладки. Сначала немного теории.

   Каждая вкладка описана в ресурсе как обычный диалог. Только надо во вкладке Styles свойств ресурса диалога убрать галочку TitleBar , в списке Style выбрать Child, в списке Border выбрать Thin. Ещё нужно сделать диалог невидимым. Также можно удалить вставленные по умолчанию кнопки Ok и Cancel.

   Удобно произвести все страницы от своего класса (производного от CDialog), назовём его CMyTabDialog. Удобно это тем, что общий для всех страниц код (например реакция на нажатие Enter или Escape) расположен в одном классе. Кроме того, поскольку страницы будут создаваться динамически, в базовом классе  страницы будут сами возвращать в кучу память, отведённую под них:
Код:
void CMyTabDialog::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this;
}
   Однако этим мы себя обязали создавать объекты страниц ТОЛЬКО динамически, при помощи оператора new. Причём удалять объекты страниц нужно будет не delete, а вызовом виртуальной функции DestroyWindow().


   Пример добавления страницы в проект:
   Добавляем в проект страницу page1 :
   1) Вставляем новый диалог (дерево ресурсов, Insert Dialog) с идентификатором IDD_page1.
   2) Выставляем нужные свойства, убираем кнопки
   3) Создаём класс страницы - жмём Ctrl+W , даём название классу CPage1, выбираем базовый класс CDialog.
   4) Теперь надо заменить базовый класс на CMyTabDialog.
В дереве классов щёлкаем по классу нашей страницы CPage1, открывается заголовочный файл Page1.h. В строке
Код:
class CPage1 : public CDialog
меняем название базового класса с CDialog на CMyTabDialog.
   5)Открываем файл реализации Page1.cpp. Перед строкой
Код:
#include "Page1.h"
вставляем строку
Код:
#include "MyTabDialog.h"
, а также меняем в строке
Код:
CPage1::CPage1(CWnd* pParent /*=NULL*/)
: CDialog(CPage1::IDD, pParent)
название базового класса с CDialog на CMyTabDialog.

   Итак, пусть имеется 4 страницы с классами CPage1,CPage2,CPage3,CPage4. Они независимы друг от друга и могут содержать свои контролы и различный код.
   Далее в программе:
   1) добавляем страницы в TabControl
   2) удаляем их оттуда

   На главном диалоге размещён флажок IDC_chkCREATE для управления этим процессом. В обработчике щелчка CTabsTestDlg::OnchkCREATE() определяем состояние флажка и вызываем либо CTabsTestDlg::CreateProperties() , либо CTabsTestDlg::KillProperties(), в которых происходит создание и удаление объектов вкладок.

   Последовательность создания вкладки отражена в макросе def_create_page. Переменная nCurrIndx содержит текущий номер вкладки, после вставки каждой вкладки номер увеличивается.

Код:
//макрос - добавление страницы
#define def_create_page(clss,caption) \
{\
clss* pPage=new clss(&m_IDC_TAB);\
item.pszText =(char*)caption;\
item.lParam =(LPARAM)pPage;\
pPage->Create(pPage->IDD,&m_IDC_TAB);\
pPage->SetWindowPos(0, def_DlgTabLef0,def_DlgTabTop0,\
0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW);\
m_IDC_TAB.InsertItem(nCurrIndx, &item);\
nCurrIndx++;\
}

   Если требуется удалить вкладку, то при помощи структуры TC_ITEM аналогично вытаскивается указатель на окно вкладки (item.lParam) и для окна вызывается OnDestroy(). В процедуре KillProperties() делается удаление всех вкладок: удаляем первую вкладку до тех пор, пока вкладки ещё есть.

   Заметьте, что при закрытии программы можно не заботится об удалении объектов вкладок (если они созданы). Это сделает класс CMyTabDialog.



   Теперь нужно сделать переключение страниц, когда пользователь щёлкает по корешкам вкладок. Добавляем в главное окно обработчики событий TCN_SELCHANGE и TCN_SELCHANGING для Tab Control (при помощи визарда - выделите контрол, нажмите Ctrl+W, добавьте нужные обработчики).

Код:
//страница, на которую пришли. Показываем её
void CTabsTestDlg::OnSelchangeTab(NMHDR* pNMHDR, LRESULT* pResult)
{
TCITEM item={TCIF_PARAM};
int nTab = m_IDC_TAB.GetCurSel();
m_IDC_TAB.GetItem(nTab,&item);
if(item.lParam)
{
((CWnd*)item.lParam)->ShowWindow(SW_SHOW);
}
*pResult = 0;
}

//страница, с которой ушли. Скрываем её
void CTabsTestDlg::OnSelchangingTab(NMHDR* pNMHDR, LRESULT* pResult)
{
TCITEM item={TCIF_PARAM};
int nTab = m_IDC_TAB.GetCurSel();
m_IDC_TAB.GetItem(nTab,&item);
if(item.lParam)
{
((CWnd*)item.lParam)->ShowWindow(SW_HIDE);
}
*pResult = 0;
}

Вот , в принципе, и всё.
Проект, фрагменты кода из которого приведены в статье, можно найти здесь

Успехов :)

Автор: Алексей1153
Версия для печати
Обсудить на форуме