- •О.С. Зеленський
- •Розділ 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. Формування вхідного документа на основі діалогу
- •Контрольні питання
- •Завдання Робота з типовими елементами керування
- •Робота зі списками і комбінованими полями
- •Список літератури
8.8.3. Клас Cdlg
Структура для зберігання інформації про автомобіль має вигляд:
struct dialog
{
int color;
int kol_mach;
int marka;
int god;
int sost;
int probeg;
BOOL kond;
BOOL signal;
int val;
double grn;
double euro;
DWORD color_viv;
LOGFONT logFont;
};
У класі Cdlg зробимо змінну dial типу dialog. Майже у всіх функціях класу будемо звертатись до цієї змінної. Цей клас створено для редагування інформації (див. рис. 8.17). В ньому створені елементи керування та функції, які реагують на зміни в них.
Конструктор класу має такий вид:
Cdlg::Cdlg(dialog di, CDialDoc* pParent)
: CDialog(Cdlg::IDD,0)
{
dial = di;
par = pParent;
}
У ньому ми встановлюємо значення змінної dial відповідно до параметру di, який було передано із зовнішнього класу.
Одна із найважливіших функцій це DoDataExchange, вона викликається для зв’язування змінних діалогу із елементами керування:
void Cdlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(Cdlg)
DDX_Control(pDX, IDC_CHECK2, m_signal);
DDX_Control(pDX, IDC_CHECK1, m_kond);
DDX_Control(pDX, IDC_SLIDER1, m_slid1);
DDX_Control(pDX, IDC_LIST1, m_mark);
DDX_Control(pDX, IDC_COMBO1, m_tp);
DDX_Control(pDX, IDC_SLIDER2, m_slid2);
DDX_Text(pDX, IDC_EDIT1, m_mach);
DDX_Text(pDX, IDC_EDIT2, m_sost);
DDX_Text(pDX, IDC_EDIT3, m_probeg);
//}}AFX_DATA_MAP
}
Рис. 8.17. Вікно діалога з ідентифікаторами елементів (клас Cdlg)
Ця функція викликається класом автоматично, заповнюється майстром. В ній можна бачити, які елементи керування зв’язані з якими змінними. Вище було зазначено, що можна викликати функцію UpdateData, яка викличе DoDataExchange. Функція UpdateData приймає параметр BOOL bSaveAndValidate, якщо він має значення TRUE, то значення від елементів керування будуть перенесені до змінних, а коли значення FALSE, то навпаки в залежності від змінних будуть змінені елементи керування.
Також цікаво подивитись карту повідомлень:
BEGIN_MESSAGE_MAP(Cdlg, CDialog)
//{{AFX_MSG_MAP(Cdlg)
ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1)
ON_EN_CHANGE(IDC_EDIT3, OnChangeEdit3)
ON_LBN_SELCHANGE(IDC_LIST1, OnSelchangeList1)
ON_CBN_SELCHANGE(IDC_COMBO1, OnSelchangeCombo1)
ON_BN_CLICKED(IDC_CHECK1, OnCheck1)
ON_BN_CLICKED(IDC_CHECK2, OnCheck2)
ON_EN_CHANGE(IDC_EDIT5, OnChangeEdit5)
ON_EN_CHANGE(IDC_EDIT6, OnChangeEdit6)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
В карті повідомлень вказано які події операційної системи Windows пов’язуються з якими функціями. Так наприклад: ON_EN_CHANGE означає подію зміни тексту в текстовому полі (E → Edit), ON_LBN_SELCHANGE означає подію зміни фокуса у елементі списку (LB → ListBox).
При створенні вікна діалогу, після функцій DoModal та DoDataExchange викликається функція OnInitDialog:
BOOL Cdlg::OnInitDialog()
{
CString str;
CDialog::OnInitDialog();
for(int i = 0; i < 11; i++)
{
m_mark.AddString(par->mas_mach[i]);
m_mark.SetItemData(i,par->mas_st[i]);
}
m_mark.SetCurSel(dial.marka);
m_marka = dial.marka;
m_tp.AddString("2008");
m_tp.AddString("2007");
m_tp.AddString("2006");
m_tp.AddString("2005");
m_tp.AddString("2004");
m_tp.AddString("2003");
m_tp.AddString("2002");
m_tp.AddString("2001");
m_tp.SetCurSel(dial.god);
str.Format("%.2f",dial.grn);
GetDlgItem(IDC_EDIT5)->SetWindowText(str);
str.Format("%.2f",dial.euro);
GetDlgItem(IDC_EDIT6)->SetWindowText(str);
if(dial.color == 0)
((CButton*)GetDlgItem(1008))->SetCheck(1);
if(dial.color == 1)
((CButton*)GetDlgItem(1009))->SetCheck(1);
if(dial.color == 2)
((CButton*)GetDlgItem(1010))->SetCheck(1);
if(dial.val == 0)
GetDlgItem(IDC_STATIC1)->SetWindowText("$");
if(dial.val == 1)
GetDlgItem(IDC_STATIC1)->SetWindowText("грн");
if(dial.val == 2)
GetDlgItem(IDC_STATIC1)->SetWindowText("€");
m_kond.SetCheck(dial.kond);
m_signal.SetCheck(dial.signal);
m_slid1.SetRange(0,3);
m_slid1.SetPos(dial.sost);
m_slid1.SetTicFreq( 1);
m_slid1.SetLineSize(1);
m_slid1.SetPageSize(3);
m_slid2.SetRange(0,500);
m_slid2.SetPos(dial.probeg);
m_slid2.SetTicFreq(50);
m_slid2.SetLineSize(1);
m_slid2.SetPageSize(50);
m_mach.Format("%d",dial.kol_mach);
m_probeg.Format("%d",dial.probeg);
return TRUE;
}
У даній функції ми заповнюємо такі елементи керування як списки і бігунки, тому що стандартна функція DodataExchange з цим завданням впоратись не зможе. Елемент керування список має декілька рядків тексту, один із яких є активним. Активність рядка списка (CListBox) задається функцією SetCurSel. Функція SetPos бігунка (CSliderCtrl) встановлює позицію повзунка. Функція SetRange задає мінімальне та максимальне значення для повзунка. Функція SetTicFreq бігунка встановлює відстань між діленнями. Функція SetLineSize задає значення на яке буде змінюватись позиція бігунка при натисненні курсорів на клавіатурі. Функція SetPageSize встановлює значення на яке буде змінюватись позиція бігунка при натисненні на клавіатурі кнопок [Page Up] та [Page Down].
Функція, яка викликається при зміні елемента IDC_EDIT1 має вигляд:
void Cdlg::OnChangeEdit1()
{
char buf[20];
CEdit* kol_mach_ed = (CEdit*)GetDlgItem(IDC_EDIT1);
kol_mach_ed->LimitText(3);
kol_mach_ed->GetWindowText(buf,20);
if ((!IsNumeric(buf,0) && buf[0]!=0)
|| buf[0]== '+' || buf[0]== '-')
{
kol_mach_ed->Undo();
kol_mach_ed->SetSel(65534, 65534);
kol_mach_ed->SetFocus();
return;
}
kol_mach_ed->EmptyUndoBuffer();
UpdateData(1);
dial.kol_mach = atoi(buf);
cena();
}
В текстовому полі IDC_EDIT1 вказується кількість автомобілів, а функція OnChangeEdit1 слідкує за правильним введенням даних. В функції ми робимо такі дії: виділяємо буфер buf[20] та створюємо вказівку на об’єкт класу CEdit. Функція GetDlgItem повертає дискриптор вікна HWND, який потрібно приводити до потрібного класу елементу керування. Далі зчитуємо текст із текстового поля в буфер (зчитування тексту робить функція GetWindowText) та перевіряємо чи не містить цей текст нечислові символи (цю перевірку робить функція IsNumeric, яку створили у класі Cdlg). Якщо інформація введена правильно викликаємо функцію EmptyUndoBuffer, якою фіксуємо зміни значення текстового поля, тобто функція Undo уже не буде діяти. Інакше, якщо введено не коректні дані (одразу після умови if) викликаємо функцію Undo, для повернення попереднього значення.
Функція, яка викликається при зміні елемента IDC_EDIT3 має вигляд:
void Cdlg::OnChangeEdit3()
{
char buf[20];
CEdit* rasgon_ed = (CEdit*)GetDlgItem(IDC_EDIT3);
rasgon_ed->GetWindowText(buf,20);
if ((!IsNumeric(buf,0) && buf[0]!=0)
|| buf[0]== '+' || buf[0]== '-')
{
rasgon_ed->Undo();
rasgon_ed->SetSel(65534, 65534);
rasgon_ed->SetFocus();
return;
}
rasgon_ed->EmptyUndoBuffer();
CSliderCtrl* m_slid2 = (CSliderCtrl*)
GetDlgItem(IDC_SLIDER2);
m_slid2->SetPos(atoi(buf));
UpdateData(1);
}
В цьому полі вказується пробіг автомобіля. Функція має аналогічний вигляд, як і розглянута вище, але відмінність полягає у тому, що до поля приєднується елемент керування CSliderCtrl. Функція SetPos встановлює позицію бігунка.
Елементи IDC_EDIT5 та IDC_EDIT6 слугують для введення курсів гривні та евро відносно долару. Логіка роботи така як і у IDC_EDIT1:
void Cdlg::OnChangeEdit5()
{
char buf[20];
CEdit* grn_ed = (CEdit*)GetDlgItem(IDC_EDIT5);
grn_ed->GetWindowText(buf,20);
grn_ed->LimitText(4);
if ((!IsNumeric(buf,2) && buf[0]!=0)
|| buf[0]== '+' || buf[0]== '-')
{
grn_ed->Undo();
grn_ed->SetSel(65534, 65534);
grn_ed->SetFocus();
return;
}
grn_ed->EmptyUndoBuffer();
UpdateData(1);
dial.grn = atof(buf);
cena();
}
void Cdlg::OnChangeEdit6()
{
char buf[20];
CEdit* euro_ed = (CEdit*)GetDlgItem(IDC_EDIT6);
euro_ed->GetWindowText(buf,20);
euro_ed->LimitText(4);
if ((!IsNumeric(buf,2) && buf[0]!=0)
|| buf[0]== '+' || buf[0]== '-')
{
euro_ed->Undo();
euro_ed->SetSel(65534, 65534);
euro_ed->SetFocus();
return;
}
euro_ed->EmptyUndoBuffer();
UpdateData(1);
dial.euro = atof(buf);
cena();
}
Функція Cdlg::cena слугує для розрахунку ціни автомобіля залежно від заданих параметрів. Розрахована ціна виводиться у поле IDC_EDIT4:
void Cdlg::cena()
{
double m_cena;
CString per;
m_cena = (m_mark.GetItemData(dial.marka)
+500*(dial.kond + dial.signal)
+dial.color*100 - dial.probeg*10)
*(1-0.2*dial.sost)*(1-0.03*dial.god)
*dial.kol_mach;
if (dial.val==1) m_cena*= dial.grn;
if (dial.val==2) m_cena*= dial.euro;
per.Format("%.2f",m_cena);
GetDlgItem(IDC_EDIT4)->SetWindowText(per);
}
Функція Format з класу CString дозволяє перевести значення змінної m_cena у рядковий формат. Функція SetWindowText з класу CEdit встановлює текст у поле елемента IDC_EDIT4.
Функція на зміну марки автомобіля має такий вид:
void Cdlg::OnSelchangeList1()
{
dial.marka = m_mark.GetCurSel();
cena();
}
Змінна m_mark має тип CListBox, пов’язана з елементом керування IDC_LIST1. Функція GetCurSel повертає номер вибраного рядка списку.
Рік випуску автомобіля задається функцією OnSelchangeCombo1:
void Cdlg::OnSelchangeCombo1()
{
dial.god = m_tp.GetCurSel();
cena();
}
Змінна m_tp має тип CComboBox, пов’язана з елементом керування IDC_COMBO1.
Розглянемо функцію OnNotify:
BOOL Cdlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if (wParam == IDC_SLIDER1)
{
dial.sost=m_slid1.GetPos();
if (dial.sost== 0)m_sost = "отличное";
if (dial.sost== 1)m_sost = "хорошее";
if (dial.sost== 2)m_sost = "среднее";
if (dial.sost== 3)m_sost = "ниже среднего";
}
if (wParam == IDC_SLIDER2)
{
m_probeg.Format("%d", m_slid2.GetPos());
dial.probeg = m_slid2.GetPos();
}
UpdateData(0);
cena();
return CDialog::OnNotify(wParam, lParam, pResult);
}
Ця функція викликається при отриманні повідомлення WM_NOTIFY, яке використовується для оповіщення батьківського вікна елемента керування про те, що у ньому відбулась подія. При зміні елементу керування IDC_SLIDER1 див. рис. 8.18 відбуваються такі події:
2) викликається функція OnNotify, у котрій відбуваються усі інші дії;
3) змінна dial.sost отримує нове значення у залежності від позиції елементу IDC_SLIDER1. Функція GetPos класу CSlider повертає позицію бігунка;
4) робляться зміни у текстовому полі IDC_EDIT2 через функцію UpdateData(0), яка викликає описану вище функцію DoDataExchange;
5) викликається функція cena для розрахунку нової ціни у текстовому полі IDC_EDIT4.
Рис. 8.18. Використання функції OnNotify у класі Cdlg
1 – зміни у елементі керування IDC_SLIDER1;
2 – виклик функції OnNotify.
При зміні позиції бігунка у елементі IDC_SLIDER2 змінюються інші параметри, але відбуваються дії аналогічні до зображених на рис. 8.18.