- •О.С. Зеленський
- •Розділ 1. Загальні відомості створення додатку windows
- •1.1. Створення додатку Windows за допомогою майстра AppWizard
- •1.2. Варіанти майстрів для різних проектів
- •1.3. Короткий опис sdi програми
- •1.4. Короткий опис mdi програми
- •1.5. Короткий опис простого діалогового додатку
- •Контрольні питання
- •Розділ 2. Повідомлення і команди
- •2.1. Обробка повідомлень
- •2.2. Цикли обробки повідомлень
- •2.3. Карти повідомлень
- •Контрольні питання
- •Розділ 3. Документи та види
- •3.1. Клас додатку
- •3.2. Клас головного вікна
- •3.3. Клас документа
- •3.4. Класи виду
- •Контрольні питання
- •Розділ 4. Робота з клавіатурою, мишею і меню
- •4.1. Робота з клавіатурою
- •4.2. Робота з мишею
- •4.3. Робота з меню
- •Контрольні питання
- •Розділ 5. Виведення на екран
- •5.1. Класи графічних об'єктів
- •5.2. Робота зі шрифтами
- •5.3. Робота з пензликами та малювання графічних фігур
- •5.4. Робота з пензликом
- •5.5. Робота зі скролінгом
- •5.6. Приклад роботи з таблицями
- •5.7. Малювання на екрані маніпулятором "миша"
- •5.8. Завантаження та виведення на екран бітових зображень
- •5.9. Копіювання бітових образів
- •5.10. Малювання графічних об'єктів з використанням резинових контурів та метафайлів
- •5.11. Виділення графічних об'єктів у прямокутній області
- •5.12. Універсальний приклад роботи з двовимірною графікою з використанням резинового контуру
- •5.13. Запис на диск та зчитування з диску графічних об'єктів
- •5.14. Побудова кругових діаграм і гістограм
- •5.15. Користувацький режим роботи з графікою на прикладі малювання годинника Clock
- •Контрольні питання
- •Завдання
- •Розділ 6. Друк і попередній перегляд документів
- •6.1. Вибір і налаштування параметрів друку
- •6.2. Створення контекста пристрою
- •6.3. Друк документів і бібліотека mfc
- •6.4. Масштабування
- •6.5. Друк багатосторінкового документа
- •Контрольні питання
- •Розділ 7. Робота з файлами
- •7.1. Приклад роботи з файлами на основі класів cFile, cStdioFile та потоку fstream
- •7.1.1. Робота з класом cFile
- •7.1.2. Робота з потоком fstream
- •Можливі режими доступу
- •7.1.3. Робота з класом cStdioFile
- •7.2. Серіалізація даних, клас cArchive
- •7.3. Використання реєстру в додатках
- •Контрольні питання
- •Завдання
- •Розділ 8. Діалогові вікна
- •8.1. Створення діалогового вікна та простіші елементи керування
- •8.2. Робота зі списками і комбінованими полями
- •8.3. Ускладнений приклад зі списками
- •8.4. Робота з повзунками
- •8.5. Виведення бітових матриць в діалозі та у вікні виду
- •8.6. Лінійний регулятор, лінійний індикатор, інкриментний регулятор
- •8.7. Стандартні діалоги вибору файлів, шрифтів та кольору
- •8.8. Взаємоз'вязок діалога, документа та виду при розробці додатку
- •8.8.1. Клас cDialDoc
- •8.8.2. Клас cDialView
- •8.8.3. Клас Cdlg
- •8.9. Формування вхідного документа на основі діалогу
- •Контрольні питання
- •Завдання Робота з типовими елементами керування
- •Робота зі списками і комбінованими полями
- •Список літератури
1.2. Варіанти майстрів для різних проектів
На рисунку 1.1 було показано вікно для створення нового проекту, у якому можливо обрати тип створюваної програми. Основні типи програм наведено у табл. 1.1:
Таблиця 1.1
Основні типи програм при створенні проекту
Тип програми |
Опис |
Custom AppWizard |
Дозволяє створити власний "майстер" Custom AppWizard, який можна буде використовувати для розробки шаблонів додатків з заданими властивостями |
Makefile |
Надає додаткові можливості для використання MAKE-файлу |
MFC AppWizard (exe) |
Додаток, що створюється з використанням бібліотеки класів MFC. За допомогою AppWizard можна автоматично створити основні класи необхідні для програми |
MFC AppWizard (dll) |
Бібліотека динамічного компонування – DLL, що створюється за допомогою бібліотеки класів MFC. AppWizard дозволяє автоматично створити всі основні файли, необхідні для DLL |
Win32 Application |
Додаток, створений на основі бібліотеки класів MFC або з використанням тільки викликів функцій програмного інтерфейсу Windows |
Win32 Console Application |
Додаток, розроблено з використанням функцій консольного введення / виведення. Цей тип додатків можна використовувати для створення невеликих програм, що працюють в пакетному режимі |
Продовження таблиці 1.1
Тип програми |
Опис |
Win32 Dynamic-Link Library |
Бібліотека динамічного компонування, створена з використанням тільки викликів функцій програмного інтерфейсу Windows |
Win32 Static Library |
Бібліотека функцій |
1.3. Короткий опис sdi програми
Термін SDI (Single Document Interface) дослівно означає одно-документний інтерфейс і описує програми, здатні завантажити і використовувати одночасно лише один документ. Програма Notepad (Блокнот), є яскравим представником такого класу програм.
SDI-додаток має меню, яке користувач може застосувати для того, щоб відкрити будь-який документ (але тільки один) і потім з ним працювати. У цьому розділі ми розглянемо тексти програм, підготовлені для такого додатку AppWizard при наступному налаштуванні: відсутня підтримка операцій з базами даних і складеними документами, але є панель інструментів, рядок стану, оперативна довідка та коментарі у тексті програми, функції з бібліотеки MFC підключені в режимі поділюваних DLL-модулів. Іншими словами, це налаштування, яке пропонує AppWizard за замовчуванням на всіх етапах після першого.
Буде створено п'ять класів. Імена цих класів для програми FirstSDI перераховані нижче.
•CAboutDlg – клас діалогу для вікна About.
•CFirstSDIApp – клас для програми в цілому, породжений CWinApp.
•CFirstSDIDoc – клас документа.
•CFirstSDIView – клас виду.
•CMainFrame – клас фрейма вікна.
Файл заголовка (header file) для класу CFirstSDIApp приведений далі. При роботі з Visual Studio найпростіше познайомитися з вмістом цього файлу, клацнувши двічі на імені класу CFirstSDIApp у вікні ClassView. Після цього текст файлу заголовка, відповідного зазначеного класу, з'явиться у вікні редактора коду.
// FirstSDI.h : Головний файл заголовка для програми FIRSTSDI
//
#if !defined(AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)
#define AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#ifndef __AFXWIN_H__
#error Включение stdafx.h передує включенню цього файлу для РСН
#endif
#include "resource.h" // Головні символи.
/////////////////////////////////////////////////////////////////////////////
// CFirstSDIApp:
// Використання цих класів наводиться в FirstSDI.срр.
//
class CFirstSDIApp : public CWinApp
{
public:
CFirStSDIApp();
// Перевантаження.
// Перевантажені віртуальні функції,
// сформовані ClassWizard-ом.
//{{AFX_VIRTUAL(CFirstSDIApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Реалізація.
//{{AFX_MSG(CFirstSDIApp)
afx_msg void OnAppAbout();
// УВАГА!! Тут ClassWizard буде додавати і
// видаляти функції-члени.
// не редагуйте текст у цих блоках!
//}}AFX_MSG
DECIARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ буде вставляти додаткові
// оголошення перед попереднім рядком.
#endif // !defined(AFX_FIRSTSDI_H__CDF38D8A_8718_11D0_B02C_0080C81A3AA2__INCLUDED_)
Розглянемо текст програми більш детально. Директива #if! defined, за якою йде дуже довге вираження, є в явному вигляді захист заголовка (include guarding). В текстах програм дуже часто зустрічаються фрагменти такого типу:
#ifndef test_h
#include "test.h"
#define test_h
#endif
Подібна конструкція гарантує, що файл заголовка test.h не буде включений в даний модуль двічі. Подвійне включення файлу заголовка небажано в програмах на C++. Припустимо, визначено клас Employee (персонал) і він використовує клас Manager (менеджер). Якщо обидва файли заголовків і для класу Manager, і для класу Employee, в свою чергу, включають файл BigCorp.h, то компілятор видасть повідомлення про помилку, яке говорить, що деякі символи перевизначені. А сталося це через неявне повторне включення в модуль файлу ВigCorp.h.
Використання наведеної вище конструкції не вирішує проблеми. Якщо хтось включить test.h, але забуде визначити константу test_h, з'явиться можливість повторно включити файл test.h. Кардинальне вирішення питання полягає в тому, щоб включити і перевірку, та визначення константи в сам файл заголовка. Тоді фрагмент файлу test.h буде виглядати наступним чином:
#ifndeftest_h
... текст файла ...
#define test_h
#endif
AppWizard у створеному ним файлі сформував довший вираз, а не просто test_h. Цей довгий вираз запобігає виникненню проблем з однаковими іменами файлів заголовків, які, однак, розміщені, в різних папках. Крім того, використовується дещо відмінний синтаксис перевірки виразу. Директива #pragma once також є директивою для захисту від повторного включення. Але заради сумісності зі старою першою версією компілятора майстром AppWizard генеруються довгі директиви #define. Подивіться на рядок:
#if _MSC_VER>= 1000
Тут _MSC_VER означає версію поточного компілятора, а 1000 означає першу версію.
Клас CFirstSDIApp успадкований від CWinApp, класу MFC, який включає в себе більшість функціональних можливостей, необхідних додаткам. AppWizard згенерував кілька функцій для класу-спадкоємця, які перевантажують відповідні функції базового класу. Фрагмент тексту, який починається з коментаря // Перевантаження, якраз і є перевантаження віртуальної функції. AppWizard генерує дивний, на перший погляд, коментар навколо оголошення перевантаження віртуальної функції InitInstance(). Ці коментарі потім будуть використані СlassWizard і при необхідності полегшать йому включення нових перевантажень. Наступна секція тексту програми – карта повідомлень; в ній оголошується функція OnAppAbout(). Про карту повідомлень буде розказано більш детально в розділі 2, Повідомлення і команди.
AppWizard у файлі FirstSDI.cpp генерує текст функцій-членів класу CFirstSDIApp: конструктора, InitInstance() і OnAppAbout(). Нижче наведено код конструктора, який ініціалізує об'єкт класу.
CFirstSDIApp::CFirstSDIApp()
{
// ЗРОБИТИ: додати сюди текст конструктора.
// Всі процедури ініціалізації повинні розташовуватися
// у функції InitInstance.
}
Це конструктор, типовий для Microsoft. Оскільки конструктор нічого не повертає, повідомити програмі про якісь проблеми, що виникли при ініціалізації об'єкту, зовсім не просто. Є два абсолютно різні способи виходу із ситуації. Підхід, використаний у Microsoft, – так звана двоетапна ініціалізація. При цьому створюється окрема функція ініціалізації, так що сам конструктор, по суті, нічого не ініціалізує. Ця функція має стандартну назву InitInstance (Ініціалізувати екземпляр):
BOOL CFirstSDIApp::InitInstance()
{
AfxEnableControlContainer();
// Стандартна ініціалізація
// Якщо ви не використовуєте ці властивості і хочете
// зменшити розмір виконавчих файлів, видаліть
// непотрібні вам процедури ініціалізації.
#ifdef _AFXDLL
Enable3dControls(); // Цю функцію слід викликати у тому
// випадку, якщо використовуються DLL-модулі MFC.
#else
Enable3dControlsStatic(); // Цю функцію слід викликати
// у тому випадку, якщо функції з MFC
// прикомпоновуються статично.
#endif
// Змініть ключ системного реєстру, у якому будуть
// зберігатися параметри програми.
// Цей рядок потрібно змінити відповідно найменуванню
// вашої фірми або організації.
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings();// Завантаження стандартного
// файлу INI (включаючи MRU)
// Реєстрація шаблону документів додатка. Шаблони
// документів забезпечують взаємодію документів, рамок
// вікна і видів
CSingleDocTemplate* pDocTemplate;
pDoctemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CFirstSDIDoc),
RUNTIME_CLASS(CMainFrame), // Рамка головного SDI-вікна.
RUNTIME_CLASS(CFirstSDIView));
AddDocTemplate(pDocTemplate);
// Аналіз аргументів командного рядка, динамічний
// обмін даними (DDE) і відкриття файлу
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Розпреділити функції, задані у командному рядку
if(!ProcessShellCommand(cmdInfo))
return FALSE;
// Створено перше і едине вікно програми;
// тут воно показується та оновлюється.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
Функція InitInstance() готує додаток до роботи. Все починається з дозволу додатку містити в своєму складі елементи керування ActiveX, для чого викликається AfxEnableControlContainer(). Потім настає черга об'ємного дизайну елементів управління і ключа реєстрації програми.
Наступна дія InitInstance() – реєстрація єдиного шаблону документа, того самого, який буде створений SDI-додатком.
Для аналізу командного рядка InitInstance() організовує порожній об'єкт класу CCommandLineInfо. Потім функція ParseCommandLine() поміщує в нього параметри, задані в, командному рядку при запуску програми. І нарешті, викликається ProcessShellCommand(), яка повинна організувати виконання операцій, заданих цими параметрами. Це означає, що додаток зможе підтримувати параметри командного рядка і надає, таким чином, можливість користувачеві заощадити час, необхідний для налаштування його роботи. Наприклад, якщо користувач набере в командному рядку FirstSDI fooble, додаток відразу ж після виклику відкриє файл fooblе. Функція ProcessShellCommand() підтримує наступні параметри в командному рядку.
Таблиця 1.2
Параметри для командного рядку
Параметр |
Призначення |
Без параметрів |
Запускає програму та відкриває новий файл |
Ім'я_файлу |
Запускає програму та відкриває вказаний файл |
/p ім'я_файлу |
Запускає програму та роздруковує вказаний файл на принтері заданому за замовчуванням |
/pt ім'я_файлу Принтер Драйвер Порт |
Запускає програму та роздруковує вказаний файл на зазначеному принтері |
/dde |
Запускає додаток і чекає команди DDE (динамічного обміну даними) |
/Automation |
Запускає додаток як автоматичного сервера OLE |
/Embedding |
Запускає додаток в режимі редагування впровадженого об'єкта OLE |
Якщо необхідно реалізувати будь-які інші функції, необхідно створити клас, який успадкує від CCommandLineInfо здатність накопичувати компоненти, що утворюються при аналізі командного рядка, а потім у власному Арр-класі необхідно перезавантажити функції CWinApp::ParseCommandLine() і CWinApp::ProcessSheNCommand().
Відомо, що багато програм Windows викликаються з командного рядку. Наприклад, якщо набрати на клавіатурі Notepad blah.txt, то буде відкритий файл blah.txt в програмі Notepad. Також можна звертатися і до інших параметрів командного рядка. Набравши Notepad /p blah.txt, ви відкриєте файл blah.txt в програмі Notepad і роздрукуєте його.
Останній оператор в InitInstance() повертає TRUE, сповіщаючи таким чином викликаючу програму про те, що ініціалізація завершена і можна приступати до роботи.
Присутність карти повідомлень у файлі заголовка є індикатором того, що функція OnAppAbout організовує виведення повідомлення. Приведемо карту повідомлень у файлі тексту програми.
BEGIN_MESSAGE_MAP(CFirstSDIApp, CWinApp)
//{{AFX_MSG_MAP(CFirstSDIApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// УВАГА!! Тут ClassWizard буде додавати і
// видаляти функції-члени.
// не редагуйте текст у цих блоках!
//}}AFX_MSG_MAP
// Стандартні команди роботи з файлами документів.
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Стандартні команди налаштування принтера.
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFileprintSetup)
END_MESSAGE_MAP()
Ця карта повідомлень перехоплює команди з меню, про що буде детально розповідатись у розділі 2. Коли користувач обирає опцію Help → About, буде викликана функція-член CFirstSDIApp::OnAppAbout() (функція відгуку). Якщо ж користувач обирає опцію File → New, File → Open або File → PrintSetup, то будуть визвані функції-члени класу CWinApp. Дані функції можна перевантажити власними функціями, якщо треба, щоб вони виконували певні дії на перераховані команди меню. Текст функції OnAppAbout() виглядає наступним чином:
void CFirstSDIApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}
Тут оголошується об'єкт, який є екземпляром класу CAboutDlg, і викликається функція DoModal(), яка виводить на екран вікно діалогу.