MDI-додаток на Visual C + +: погляд зсередини

У своїй статті я хочу розповісти про ключові моменти створення додатків з використанням мультідокументного інтерфейсу (Multiple Document Interface, MDI) Бібліотеки Microsoft Foundation Classes (MFC) компанії Microsoft.


У багатьох підручниках з MFC створення MDI-додатків приділяється недостатньо уваги. Зазвичай обмежуються описом кроків його створення з допомогою AppWizard і прикладом роботи з текстовим документом: відкриття документа в декількох дочірніх вікнах, правка і збереження.


Складніша справа, якщо створюється MDI проект, в якому дочірні вікна повинні відображати різні дані (наприклад, з таблиць баз даних) і в той же час, надавати можливість користувачеві вводити і коректувати дані. Ці вікна повинні мати набір GUI (Graphic User Interface) елементів: кнопки (Button), поля введення (EditBox), списки (ListBox) і пр.


Бібліотека MFC підтримує програми двох принципово різних типів – на базі однодокументних (Single Document Interface, SDI) і MDI інтерфейсу. У SDI-додатки всього одне вікно і завантажити одночасно можна тільки один документ. Додатково для роботи з документом можливе використання модальних та немодальний діалогів. Гарним прикладом SDI-додатки є програма Notepad. На відміну від SDI, MDI програма має кілька так званих дочірніх (child) вікон, кожне з яких працює з окремим документом. Прикладом MDI додатку може служити MS Word.


Головне вікно MDI програми нагадує робочий стіл, на якому розміщуються різні документи (дочірні вікна). Ці вікна з документами можна скрутити в значки всередині головного вікна, розмістити різним чином на «столі» (каскадом, мозаїкою тощо).


Базове MDI додаток легко створюється за допомогою AppWizard. При запуску останнього, в діалоговому вікні AppWizard – Step 1 тип програми MDI встановлений за умовчанням (мал. 1).

Рис. 1. Перше діалогове вікно AppWizard при створенні MFC додатку.


Процес запуску SDI і MDI додатків багато в чому однаковий. Об'єкт-додаток похідного від CWinApp класу має перевизначення функцію-член InitInstance. Ця функція відмінна від аналогічної функції SDI додатки. Починається ця функція з виклику AddDocTemplate. Це виглядає так:


CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CmultiDocTemplate(
IDR_XXTYPE,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CMyView));
AddDocTemplate(pDocTemplate);


У SDI додатках існує один клас вікна-рамки і один об'єкт цього класу і AppWizard генерує клас з ім'ям CMainFrame, похідний від CFrameWnd. У MDI додатку присутні два класи вікна-рамки і безліч об'єктів-рамок.


MDI програма має клас дочірнього вікна CСhildFrame і при кожному відкритті нового документа додаток створює новий екземпляр цього класу для відображення документа всередині головного вікна.

Рис. 2. Взаємозв'язок вікна-рамки і вікна відображення в MDI додатку.


У SDI-додатку об'єкт CMainFrame обрамляє програми та містить об'єкт «вигляд». У MDI-додатку ці дві речі розділені: у InitInstance створюється об'єкт CMainFrame, а всередині об'єкта CChildFrame міститься вікно відображення. Код, що генерується AppWizard, має вигляд:


CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();


Елемент даних m_pMainWnd належить класу CWinApp. Функція InitInstance присвоює цьому елементу даних покажчик на основне вікно-рамку. Тому, якщо такий покажчик знадобиться, ми можемо отримати доступ до m_pMainWnd через глобальну функцію AfxGetApp.


У MDI-додатку існують два види основного меню: один вид використовується при відкритті тільки основного вікна-рамки (без відкритих дочірніх вікон), другий – при відкритті хоча б одного дочірнього вікна. При цьому використовуються і два окремих ресурсу меню: IDR_MAINFRAME і IDR_TESTTYPE (де TEST є назвою додатка). Ось як виглядають ці два ресурси з розбивкою на підрядки:


IDR_MAINFRAME
"Test" / / заголовок основного вікна програми
IDR_TESTTYPE
/ / Заголовок вікна програми (з'являється в заголовку
/ / Дочірнього вікна)
Test/ / Це ім'я документа за замовчуванням
Test/ / Ім'я типу документа
Test Files (*.dat)/ / Опис і фільтр для типу документа
.dat/ / Розширення для файлів документів цього типу
Test.Document/ / Ідентифікатор типу файлу в реєстрі
Test Document / / опис типу файлу в реєстрі


Якщо подивитися на файл ресурсу Test.rc, то ми побачимо, що ці підрядка об'єднані в одну довгу рядок. Тема програми береться з ресурсу IDR_MAINFRAME і при відкритті документа до цього заголовку додається ім'я файлу цього документа.


При створенні порожнього документа MDI-додаток викликає OnFileNew. Основне вікно-рамка вже створена, тому OnFileNew викликає функцію OpenDocumentFile класу CwinApp. При цьому відбувається наступне:



  1. Функція OnFileNew створює об'єкт-документ, але поки не завантажує його дані
  2. Створюється об'єкт класу CChildFrame для дочірнього вікна-рамки і формується вікно. МенюDR_MAINFRAME замінюється на меню IDR_TESTTYPE.
  3. Створюється об'єкт "вигляд" і формується вікно відображення (але це вікно поки не відображається).
  4. Встановлюється зв'язок між переліченими вище об'єктами.
  5. Для об'єкта-документа викликається функція-член OnNewDocument.
  6. Для об'єкта "вид" викликається віртуальна функція-член OnInitialUpdate.
  7. Для об'єкта "дочірня рамка" викликається віртуальна функція-член ActivateFrame, щоб вивести на екран вікно-рамку і вікно відображення.

Розглянемо створення MDI-приложения на практиці. Запускаємо Microsoft Visual Studio і створюємо новий проект з ім'ям Test. Із списку запропонованих типів проектів виберемо тип MFC AppWizard (exe). AppWizard запропонує вам пройти шість кроків для створення проекту. На першому кроці залишаємо за замовчуванням тип створюваного додатка Multiple Documents, як зображено на рис. 1. На наступних чотирьох кроків залишаємо опції по замовчуванням. На шостому кроці базовий клас CView змінимо на CFormView, як зображено на рис. 3. Натиснувши клавішу Finish, AppWizard автоматично створить каркас MDI-додатки.

Рис. 3. Крок 6. Зміна базового класу додатки на CFormView.


Якщо ви не припускаєте відразу завантажувати який-небудь документ під час запуску програми, тоді можна ресурс меню IDR_MAINFRAME видалити, а IDR_TESTTYPE перейменувати на IDR_MAINFRAME. У цьому випадку необхідно також додати наступний код в розділ ініціалізації класу CTestApp:


/ / Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);


//Don”t show a new MDI child window during startup
if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew)
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
/ / Цим кодом ми повідомляємо каркасу додатки не відкривати нове вікно при
/ / Старті програми.
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;


Змінимо властивості (Properties) пункту меню New на Test, ID_FILE_NEW на ID_OPEN_TEST, а Open … на Data і ID_FILE_OPEN на ID_OPEN_DATA, як показано на рис. 4.

Рис. 4. Властивості нового пункту меню Test.


Додамо два діалогових ресурсу (два дочірніх вікна). Стиль цих вікон необхідно встановити, як показано на рис. 5.

Рис. 5. Властивості дочірніх діалогових вікон.


У списку ресурсів String Table необхідно створити два ідентифікатора: IDR_TESTOPEN і IDR_DATAOPEN. Зразкові властивості цих ресурсів показані на рис. 6.

Рис. 6. Властивості дочірніх діалогових вікон.


На створені два вікна можна помістити необхідні елементи управління (EditBox, ListBox, Radio Buttons, Check Buttons та ін.)


Тепер необхідно визначити класи для нових вікон. Для першого вікна це буде клас CTestClass, а для другого – CDataClass. Зверніть увагу, що за замовчуванням ClassWizard пропонує базовий клас для діалогу CDialog. Наші ж вікна успадковані від базового класу CFormView.


Далі, необхідно створити обробники командних повідомлень для кожного типу документа. На закладці ClassView вибираємо клас CTestApp, натискаємо праву кнопку миші, з контекстного меню вибираємо пункт Add Windows Message Handler …. У віконці Class or object to handle знаходимо ідентифікатор ID_OPEN_TEST і натискаємо кнопку Add and Edit. Повідомлення Command буде додано до класу CTestApp і редактор коду надасть нам можливість ввести код для цього командного повідомлення. Це буде єдина функція OpenNewDoc. Як параметр цієї функції передається підрядок типу документа з кожного шаблону:


void CTestApp::OnOpenTest()
{
OpenNewDoc(“TestOpen”);
}


void CTestApp::OnOpenData()
{
OpenNewDoc(“DataOpen”);
}


MDI-додаток підтримує множинні шаблони документів. Для цього виконується виклик AddDocTemplate для кожного з шаблонів:


CMultiDocTemplate* pDocTemplateTest;
pDocTemplateTest = new CMultiDocTemplate(
IDR_TESTOPEN,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS (CChildFrame), / / клас вікна-рамки
RUNTIME_CLASS (CTestOpen)); / / клас дочірнього вікна
AddDocTemplate(pDocTemplateTest);


CMultiDocTemplate* pDocTemplateData;
pDocTemplateData = new CMultiDocTemplate(
IDR_DATAOPEN,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS (CChildFrame), / / клас вікна-рамки
RUNTIME_CLASS (CDataOpen)); / / клас дочірнього вікна
AddDocTemplate(pDocTemplateData);


При виклику команди New з меню File каркас додатків виводить на екран список шаблонів, надаючи можливість вибрати необхідний шаблон по імені, заданому в строковому ресурсі (підрядок типу документа). При виконанні програми об'єкт-документ зберігає список об'єктів (активних шаблонів документів). Перебирати цей список дозволяють функції-члени GetFirstDocTemplatePosition і GetNextDocTemplate класу CWinApp:


BOOL CTestApp::OpenNewDoc(const CString& strTarget)
{
CString strDocName;
CDocTemplate* pSelectedTemplate;
POSITION pos = GetFirstDocTemplatePosition();
while (pos != NULL)
{
pSelectedTemplate = (CDocTemplate*) GetNextDocTemplate(pos);
pSelectedTemplate-> GetDocString (strDocName, CDocTemplate:: docName);
if (strDocName == strTarget) / / вибирається з строкового ресурсу шаблону
{
pSelectedTemplate->OpenDocumentFile(NULL);
return TRUE;
}
}
return FALSE;


}


Завантаження і збереження документів м MDI-додатку здійснюється за аналогією з SDI, але з двома відзнаками: коли документ завантажується, з диска створюється новий об'єкт-документ, а при закритті останнього дочірнього вікна відображення документа – руйнується.


Запустивши додаток, ми побачимо два активних підпункту меню з меню File. Вибравши їх, завантажаться два документи (дочірніх вікна) зі своїм набором елементів управління і відображення.


На закінчення варто відзначити, що створення MDI-додатків не складає особливих труднощів. А в результаті отримуємо зручне у використанні програма, що має сучасний зовнішній вигляд і відповідне встановленим світовим стандартам. Успіхів вам у програмуванні!


Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*