m_pTabDialog = new CTabPage1;
// тип указателя соответствует нужному диалогу,
// иначе добавленный в диалог код не будет функционировать
break;
// вторая закладка
case 2:
id = IDD_TABPAGE2;
m_pTabDialog = new CTabPage2;
break;
// все остальные закладки, если они есть,
// будут здесь тоже представлены, каждая – отдельным case
// а если обработка такого номера не предусмотрена
default:
m_pTabDialog = new CDialog; // новый пустой диалог
return;
} // end switch
// создаем диалог
m_pTabDialog->Create(id, (CWnd*)&m_Tabs); //параметры: ресурс диалога и родитель
CRect rc;
m_Tab.GetWindowRect(&rc); // получаем "рабочую область"
m_Tab.ScreenToClient(&rc); // преобразуем в относительные координаты
// исключаем область, где отображаются названия закладок:
m_Tab.AdjustRect(FALSE, &rc);
// помещаем диалог на место...
m_pTabDialog->MoveWindow(&rc);
// и показываем:
m_pTabDialog->ShowWindow(SW_SHOWNORMAL);
m_pTabDialog->UpdateWindow();
*pResult = 0;
}
Теперь последний штрих: в OnInitDialog() нужно добавить следующий код:
…
m_Tab.InsertItem(1, &tci);
//-----------------
// добавить:
NMHDR hdr;
hdr.code = TCN_SELCHANGE;
hdr.hwndFrom = m_Tab.m_hWnd;
SendMessage(WM_NOTIFY, m_Tab.GetDlgCtrlID(), (LPARAM)&hdr);
//-----------------
return TRUE;
}
Это необходимо для того, чтобы отобразить самую первую закладку.
Как вариант можно просто вызвать OnSelchangeTab1(0,0); но тогда из OnSelchangeTab1 нужно удалить последнюю строку (*pResult=0).
Можете вволю поэксперементировать со свойствами и стилями CTabCtrl. Мне, например, очень нравятся закладки, надписи на которых подсвечиваются при наведении курсора мыши, кстати это имеет место в MS Access 97 (стиль TCS_HOTTRACK).
И еще: не забудьте, если диалог у вас немодальный, вы должны обеспечить корректный обмен данными между активным диалогом в Tab Control и вашим приложением. Это делается точно так же, как и обычный обмен данными с немодальным диалогом.
ОБРАТНАЯ СВЯЗЬ
Небезызвестный вам Борис Бердичевский(см. выпуск №3) делится своим решением часто возникающей проблемы с сериализацией.
Приветствую!
Случилось так, что мне пришлось потратить целый рабочий день на поиск одного решения в области сериализации; готов поделиться.
Как я уже отмечал недавно, при сериализации данных нужно решать проблему совместимости старых структур сохраняемых данных, т.е. если вы добавили новое поле в какой-то класс, подлежащий сериализации, будьте добры, обеспечьте чтение этого класса, ранее сохраненного без этого поля.
Способ, который я приводил (ReadClass/WriteClass), вполне хорош. Но закавыка-то в том, что в предыдущей версии я по какой-то причине (просто по неопытности) сохранял без WriteClass! А имплементация сериализации была описана как IMPLEMENT_SERIAL(MyClass, CObject, 0)
Итак, подобным образом сериализованный класс надо было успешно прочитать. Понятное дело, ReadClass на такую сериализацию вызывает CArchiveException с кодом CArchiveException::badIndex (=5, для справки)
Казалось бы, лови CArchiveException и обрабатывай себе, но не тут-то было! Вроде незначительная проблема: указатель архива продвигается, и невозможно уже прочитать данные из-за смещения. Никакого средства для возврата указателя на место для CArchive не существует! (Я бы мог попросить уважаемых читателей порыться, но заранее заявляю: бессмысленно! Говорите, можно манипулировать с функцией Seek принадлежащего
CArchive классу CFile? – пробовал, не работает.)
По счастью, нашлась недокументированная возможность. Выяснилось, и это очень важный нюанс, который не разъяснен в MSDN: У функции CReadClass есть 3 параметра, 2 последних имеют умолчание NULL:
CRuntimeClass* ReadClass(const CRuntimeClass* pClassRefRequested = NULL, UINT* pSchema = NULL, DWORD* obTag = NULL);
throw CArchiveException;
throw CNotSupportedException;
Первый параметр – RuntimeClass для проверки на соответствие загружаемого класса.
Второй параметр – поинтер на UINT – версию сериализуемого класса.
Третий параметр, и это самое интересное (кстати, в MSDN записано, что, он предназначен для внутреннего использования в функции ReadClass и обычно задается как NULL) – если не задан нулем, возвращает в младших 2-х байтах значение, сериализованное из архива, а CArchiveException при этом не вырабатывается! Версия при этом не заполняется.
Отсюда решение, которое проиллюстрировано во фрагменте кода:
#define BASE_DATA_VERSION 0x100
IMPLEMENT_SERIAL(MyClass, CObject, VERSIONABLE_SCHEMA | BASE_DATA_VERSION)
void MyClass::Serialize(CArchive& ar) {
Читать дальше