Обзор
функции Initlnstance
Внесем некоторые
изменения и сокращения в файл реализации класса CTreeApp. Откройте файл Тгее.срр
в окне редактора и просмотрите коды функции Initlnstance. Если у вас присутствует
блок кодов
if
(lAfxOlelnit()) {
AfxMessageBox(IDP_OLE_INIT_FAILED)
;
return
FALSE;
}
AfxEnableControlContainer()
;
который представляет
собой инициализацию поддержки OLE (Object Linking and Embedding), то его можно
убрать, так как наше приложение не будет выполнять функции OLE-сервера или OLE-контейнера.
Следующая строка:
SetRegistryKey(_T("Local
AppWizard-Generated..."));
представляет
собой создание нового ключа в реестре Windows для хранения некоторой информации
о нашем приложении. Он действительно будет новым, если вы измените строку текста
на имя вашей компании, как это было задумано при разработке функции, или на
какое либо другое имя («My Soft»). После запуска приложения можно открыть реестр
(в командной строке Windows дайте команду: RegEdit) и отыскать в нем по адресу
HKEY_CURRENT_USER\Software вновь созданный ключ My Soft. Записывать информацию
по этому ключу вы можете с помощью методов класса cwinApp, от которого происходит
наш класс CTreeApp. Например, метод WriteProf ilelnt позволяет записать некое
целое значение (value), соответствующее произвольной секции текущего ключа.
Для эксперимента вставьте вместо строки SetRegistryKey такие три строки:
SetRegistryKey("My
Soft");
WriteProfileStringC'My
Data", "My Name","Alex");
WriteProfilelnt("My
Data","My Age",54);
Запустите приложение,
перейдите в окно реестра, обновите его (View > Refresh), найдите адрес HKEY_CURRENT_USER\Software\My
Soft\Tree\My Data, поставьте в него курсор мыши и убедитесь в наличии двух записей,
высвечиваемых в правом окне реестра. Удалите из реестра ключ My Soft, если вам
нужен реестр, а не свалка мусора (чем он обычно и является). Уберите также учебный
код из тела initinstance.
Для того чтобы
увидеть, как работает функция LoadStdProf ileSettings, вызов которой виден в
теле initinstance, запустите приложение и запишите хотя бы один документ (команда:
File > Save). После этого вы можете найти в реестре (не забывайте освежать
его) по тому же адресу новую секцию Recent File List, которая содержит запись
— полный путь к только что записанному файлу. Параметр функции LoadStdProf ileSettings
указывает, сколько записей может содержать список MRU (Most Recently Used) последних
документов. Если вы зададите его равным нулю, то список не будет поддерживаться
каркасом приложения.
Теперь можно
приступить к созданию двух шаблонов документов вместо одного, рассмотренного
выше. Для того чтобы задействовать второй шаблон, надо убрать из Initinstance
код по созданию шаблона pDocTemplate и вставить вместо него такие строки:
//======
Создаем первый шаблон
m_pTemplTree
= new CMultiDocTemplate(IDR_TreeTYPE,
RUNTIME_CLASS(CTreeDoc)
,
RUNTIME_CLASS(CTreeFrame)
,
RUNTIME_CLASS(CLeftView))
;
//======
Помещаем его в список
AddDocTemplate(m_pTemplTree);
//======
Создаем второй шаблон
m_pTemplDraw
= new CMultiDocTemplate(IDR_DrawTYPE,
RUNTIME_CLASS(CTreeDoc),
RUNTIME_CLASS(CDrawFrame),
RUNTIME_CLASS(CDrawView));
//======
Помещаем его в список
AddDocTemplate(m_pTemplDraw);
Второй шаблон
тоже помещается в список шаблонов приложения. Каркас приложения устроен так,
что теперь каждый раз, когда пользователь будет выбирать команду File > New,
будет появляться диалог со списком шаблонов и просить его выбрать шаблон, которому
должен соответствовать новый документ. Идентификатор ресурсов !DR_DrawTYPE определяется
заранее, то есть в файле resource.h должна быть макроподстановка #def ine, заменяющая
этот идентификатор целым положительным числом. Самым простым способом создания
нового идентификатора является вызов команды Edit > Resource Symbols.
Но этот способ будет некорректным в нашем случае, так как мы поместили второй
шаблон в список шаблонов, ассоциированных с документами приложения, и его идентификатор
должен быть связан с какими-то ресурсами.
Ресурсов, которые
связаны со вторым шаблоном, может быть несколько, и мы покажем, как связать
с ним значок, меню, панель инструментов и строковый ресурс, расположенный в
таблице String Table. Последний является текстовой строкой, которая разбита
символами ' \п' на отдельные части — подстроки. Каждая подстрока имеет определенное
значение и используется каркасом приложения в разные моменты его жизни. Например,
вторая подстрока является корнем для образования имен новых документов, и вы
обычно видите ее в заголовке дочернего окна документа. Откройте окно Resource
View, раскройте узел дерева под именем String Table и сделайте двойной щелчок
на вложенном в него элементе. В таблице строк справа найдите iDR_TreeTYPE. Он
идентифицирует комплексную строку:
\nTree\nTree\nTree
Files (*.mgn)\n.mgn\nTree.Document\nTree.Document
Примечание
Вы можете получить справку
по всем частям этой строки, если вызовете помощь (Help) по индексу GetDocString
— методу класса CDocTemplate, позволяющему выделить нужную подстроку комплексной
строки.
Если мы поместим
в String Table новую строку с идентификатором !DR_DrawTYPE, то при открытии
окон документов по шаблону m_pTemplDraw, они будут использовать этот ресурс.
При вставке новой строки надо быть внимательным, так как ее индекс должен быть
в определенном диапазоне.
- Сделайте два щелчка (не
двойной, а два щелчка с паузой между ними) в колонке Caption, той строки текста,
которая идентифицирована IDR_TreeTYPE. При втором щелчке на месте выделенной
строки появится окно редактирования.
- Выделите, скопируйте
в буфер всю текстовую строку и щелкните справа от окна редактирования.
- Если фокус выделения
ушел, то поставьте его вновь на строку IDR_TreeTYPE. Это обеспечит правильное
значение индекса для нового ресурса. Вызовите контекстное меню и выберите
команду New > String.
- Появится новая строка,
возможно, внизу экрана. Используя ту же технику повторного щелчка, создайте
окно редактирования в новой строке и вставьте из буфера старый текст. Замените
в нем две первые подстроки на Draw.
- Задайте для новой строки
идентификаторIDR_DrawTYPE и нажмите Enter.
- Щелкните мышью заголовок
столбца Value. Таблица будет отсортирована по возрастанию индексов.
Убедитесь в
том, что индекс новой строки (видимо, 130) следует за индексом, соответствующим
строке IDR_TreeTYPE, при этом строки двух шаблонов стоят рядом. Если индекс
новой строки не попал в нужный диапазон, то придется все повторить. Замените
поле Caption строкового ресурса IDR_MAINFRAME на Doc Viewer. Это необходимо
для того, чтобы пользователь легче воспринял закономерность образования заголовков
окон новых документов.
Завершая обзор
функции Initinstance, расскажем, что делают остальные функции, вызов которых
происходит при инициализации приложения. Вызов
m_pMainWnd->DragAcceptFiles();
с параметром
TRUE, заданным по умолчанию, сообщает системе, что главное окно приложения способно
обработать сообщение WM_DROPFILES. Благодаря этому пользователь может методом
Drag&Drop переместить в открытое окно приложения файл нашего документа
(mgn-файл), и он будет обработан командой File > Open. Вызов функции
EnableShellOpen делает возможным запуск нашего приложения при двойном щелчке
на mgn-файле или его значке (icon), а вызов RegisterShellFileTypes регистрирует
новый тип файлов (файлы документов нашего приложения) и действия при его открытии
двойным щелчком. Регистрация не производится, если данное расширение (mgn) уже
присутствует в базе данных Windows и с ним связано какое-то действие. Например,
если мы вместо mgn выберем расширение mag, то наши файлы будут рассматриваться
системой как файлы Microsoft Access Diagram Shortcut или как файлы документов
приложения ACDSee в зависимости от того, что установлено в системе. Это малоприятная
история, выходом из которой, как нам говорят разработчики системы, является
возможность задавать файлам документов более длинное расширение. Нет уверенности
в том, что это будет хорошим решением, так как вероятность совпадений остается
достаточно высокой.
В файле ТгееАрр.срр
присутствует также декларация и определение класса CAbout Dig, производного
от CDialog и обслуживающего окно простого диалога, ресурс которого (IDD_ABOUTBOX)
уже имеется в каркасе приложения. Так как мы не собираемся развивать диалог,
то можно убрать класс и все его методы, оставив лишь функцию вызова OnAppAbout,
тело которой упрощается до:
void
CTreeApp::OnAppAbout()
{
//
Класс CDialog справляется с задачей
CDialog(IDD_ABOUTBOX).DoModaK);
}