Міністерство освіти і науки, молоді та спорту України
Національний університет “Львівська політехніка”
Кафедра ЕОМ
РОЗРАХУНКОВО-ГРАФІЧНА РОБОТА
з дисципліни: “О’бєктно-орієнтоване програмування”
Львів-2013
Мета: Оволодіти навиками розробки програм з графічними діалоговими інтерфейсами з використанням бібліотеки класів MFC.
ТЕОРЕТИЧНІ ВІДОМОСТІ
Концепція роботи windows програм
Програма, що написана мовою С/С++, з консольним інтерфейсом працює від початку функції main до її кінця. Для здійснення операцій вводу/виводу програма викликає функції вводу/виводу операційної системи (ОС). ОС не викликає прикладну програму. Windows програма з віконним інтерфейсом працює навпаки. Програма тільки запускається операційною системою і все. Далі програма нічого не робить, а чекає поки не отримає повідомлення від ОС, яке потім буде опрацьовуватися. Повідомлення – це сигнал про те, що відбулася деяка подія. Будь-яка подія супроводжується повідомленнями, що надсилаються або конкретному вікну, кільком вікнам, чи всім вікнам одразу. Деякі події можуть породити ще кілька подій, наприклад створення вікна супроводжується його перемальовуванням. Прикладами події є переміщення курсору мишки, натискання кнопки, тощо.
На відміну від звичайних С/С++ програм, де операції в програмі виконувалися лінійно, в Windows програмах виконується опрацювання повідомлень, які з’являються в довільному порядку, тобто неочікувано (асинхронно). Цим Windows програма схожа на обробник переривань.
Повідомлення мають унікальний номер і часто мають символьні мнемонічні позначення, наприклад WM_PAINT замість 0x000F, і два числові параметри. Список повідомлень є у файлі winuser.h. Сюди можна додати свої повідомлення з унікальними номерами. М
Повідомлення передається програмі від ОС через спеціальну функцію Windows. Після чого вони надходять в чергу повідомлень програми, яка об’являється наступним макросом в класі діалогу: DECLARE_MESSAGE_MAP(). При надходжені повідомлення з черги на обробку відбувається пошук його номера в карті повідомлень. При наявності номера повідомлення в карті повідомлень відбувається виклик зв’язаного з повідомленням обробника. Повідомлення, що відслідковуються і мають свої нестандартні обробники заносяться між макросами BEGIN_MESSAGE_MAP і END_MESSAGE_MAP.
Макрос обробника повідомлення має наступну структуру: тип повідомлення, наприклад ON_BN_CLICKED, ON_COMMAND,....; номер повідомлення; назва обробника події.
Наприклад:
BEGIN_MESSAGE_MAP(C***App, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CLogin, CDialog)
ON_BN_CLICKED(IDOK, OnBnClickedOk)
END_MESSAGE_MAP()
Події можна генерувати вручну методами SendMessage – синхронний метод (чекає поки повідомлення не буде опрацьоване), PostMessage – асинхронний метод (не чекає на опрацювання повідомлення, а продовжує виконання програми).
Структура віконної діалогової MFC програми
Бібліотека Microsoft Foundation Classes (MFC) дає можливість розробляти GUI-застосунки для Microsoft Windows на мові C++ з використанням багатого набору бібліотечних класів. Велика частина MFC є відносно тонким об'єктно-орієнтованим шаром над Windows API. Це рішення, з одного боку, підвищує продуктивність, але, з другого боку, успадковує всі недоліки дизайну Windows API і перешкоджає перенесенню програм на інші платформи.
Типова діалогова програма, що написана з використанням бібліотеки класів MFC складається з двох основних класів: класу C***App і класу C***Dlg, де замість зірочок по замовчуванню мітиться назва проекту.
Клас C***App містить метод InitInstance(), з якого починається виконання програми і створення об’єкту програми theApp. В функції InitInstance() ініціюється черга повідомлень, створюється об’єкт діалогу, до нього приєднується вікно діалогу і діалог відображається на екрані. Початково діалог містить 2 кнопки “OK” і “Cancel”. Кожна з них має свій обробник, який можна редагувати.
Клас C***Dlg містить функції OnInitDialog() та OnPaint(). В функції OnInitDialog() налаштовується зовнішній вигляд вікна діалогу та можна робити деякі початкові налаштування і присвоєння до моменту відображення діалогу. На момент виклику більшість об’єктів вже створено. Функція OnPaint() здійснює вивід вікна діалогу на екран. Кожен з класів містить карту повідомлень.
При створенні проекту діалогової програми у MS Visual Studio ці класи генеруються автоматично. Особливістю класу діалогу є те, що у нього інкапсулюються об’єкти, що прив’язані до контролів (кнопок, радіо кнопок, полів введеня тощо), меню, та інші об’єкти, що реалізують бізнес логіку програми.
Структура бібліотеки класів MFC
Структура бібліотеки класів MFC відображена на діаграмі класів (див. рис.1). В основі більшості класів бібліотеки MFC лежить базовий клас CObject. Як видно з рис. 1 клас, що забезпечує роботу з діалогами (CDialog), знаходиться в ієрархії класів CObject → CCmdTarget → CWnd. Отже, завдяки спадкуванню, клас діалогу CDialog володіє всією функціональність цих класів. Найціннішим у цій ієрархії є клас CWnd, який має множину методів, що забезпечують керування відображенням діалогу, обмін даними між контролами і змінними до яких вони прив’язані за допомогою механізму DDX/DDV; методи взаємодії з чергою повідомлень, меню, стандартними обробниками деяких подій, таймером.
Характеристика деяких класів бібліотеки MFC
CEdit – клас реалізує Edit Box, найпростіший текстовий редактор.
CRichEditCtrl - клас реалізує Rich Edit Control, текстовий редактор, що за своєю функціональністю схожий на wordpad.
CStatic – реалізує можливість розміщувати на діалозі статичний текст.
CButton – різні кнопочки: радіо-кнопочки, кнопочки, чекбокси.
CListBox – вікно з списком (текстовий список, де кожен елемент є з нового рядка, може містити в кожному з полів невидиму додаткову інформацію)
CComboBox – реалізує випадаючий список.
CDC – клас, що реалізує контексти пристроїв. Графічне розміщення інформації можливе переважно за допомогою маніпулювання класами пензлика, ручки, палітри в контексті пристрою. Контексти пристроїв є апаратно-залежними, тобто можуть відображати однакові значення кольорів в залежності від можливостей конкретного пристрою.
CString - клас реалізує текстові рядки. Клас описаний у MSDN в розділі CStringT Class. Методи класу CString описані в розділі MSDN присвяченому CStringT Members.
Меню
Меню в MFC реалізовується за допомогою класу CMenu. Цей клас інкапсулює в собі об’єкт Windows, що визначається дескриптором HMENU. Конструктор об’єкт класу CMenu створює екземпляр класу, але не саме меню! Після створення об’єкту треба використати спеціальні функції для його безпосереднього створення, або завантажити його з ресурсів.
Щоб отримати вказівник на меню треба викликати метод класу CWnd:: GetMenu(). Приклад:
CWnd* pMain = AfxGetMainWnd();
// The main window _can_ be NULL, so this code
// doesn't ASSERT and actually tests.
if (pMain != NULL)
{
// Get the main window's menu
CMenu* pMenu = pMain->GetMenu();
....
}
далі меню можна редагувати програмно.
Завдання:
4
Базовий клас:
class Shape2D
{
public:
Shape2D();
virtual ~ Shape2D();
virtual float Area( )= 0;
virtual float Perimeter() = 0;
virtual void PrintMessage();
};
Shape2D() – конструктор базового класу.
~ Shape2D() – деструктор базового класу.
Area() – повертає значення площі фігури.
Perimeter() – повертає значення периметру фігури.
PrintMessage() – виводить повідомлення про тип фігури.
Похідні класи Triangle та Rectangle
Визначити необхідні для похідних класів параметри та перевизначити необхідні функції.
…
…
…
…
22
4
deque
3
…
…
…
…
Код програми:
Myint.h
#include <deque>//мій метод
#include<functional>
#include <iterator>
#pragma once
using namespace std;
class CMyInt : public CStdioFile
{
deque<CString> lst;
char* figureName;
public:
/*мій код вставляти
* При використанні динамічного виділення пам'яті у членів-даних класу
* слід обов'язково перевизначити конструктор за замовчуванням,
* конструктор копіювання, деструктор та оператор присвоювання, які
* мають забезпечити коректне опрацювання
* динамічно створених членів класу
*/
void WriteString(CString d,CString f,CString p);
CMyInt(void);
CMyInt(const CMyInt&);
~CMyInt(void);
CMyInt& operator=(const CMyInt&);
void SetChar(char);//
char GetChar(void) const;//
private:
};
RGRDlg.h
// RGRDlg.h : файл заголовка
#pragma once
#include "MyInt.h"
#include <deque>//мій метод
#include<functional>
#include <iterator>
#include "afxcmn.h"
using namespace std;
// диалоговое окно CRGRDlg
class CRGRDlg : public CDialogEx
{
private:
CMyInt m_intObj;
// Создание
public:
CRGRDlg(CWnd* pParent = NULL); // стандартный конструктор
// Данные диалогового окна
enum { IDD = IDD_RGR_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // поддержка DDX/DDV
// Реализация
protected:
HICON m_hIcon;
// Созданные функции схемы сообщений
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnEnChangeEdit1();
afx_msg void OnEnChangeEdit3();
afx_msg void OnBnClickedWrite();
afx_msg void OnLvnItemchangedList3(NMHDR *pNMHDR, LRESULT *pResult);
CString m_figureName;
CString m_frenglyName;
CString m_Price;
CListCtrl m_List;
CMyInt obj;
afx_msg void OnBnClickedRead();
afx_msg void OnEnChangeEdit4();
afx_msg void On32771();
afx_msg void On32772();
afx_msg void On32773();
};
Myint.cpp
#include "stdafx.h"
#include "MyInt.h"
CMyInt::CMyInt(void)
{
figureName= new char;//
*figureName=0;//
}
CMyInt::CMyInt(const CMyInt& obj)
{
figureName= new char;//
*figureName=*obj.figureName;//
}
CMyInt::~CMyInt(void)
{
delete figureName;//
}
CMyInt& CMyInt::operator=(const CMyInt& obj)
{
if (this != &obj)
{
*figureName=*obj.figureName;//
return *this;
}
return *this;
}
void CMyInt::SetChar(char d)//
{
*figureName= d;
}
char CMyInt::GetChar(void) const//
{
return *figureName;
}
void CMyInt::WriteString(CString d,CString f,CString p)
{
if (lst.size()<4)
{
lst.insert(lst.end(), d);
lst.insert(lst.end(), f);
lst.insert(lst.end(), p);
}
else
{
CFileDialog OpenDialog(false, "txt", NULL,
OFN_HIDEREADONLY, "Text Files (*.txt)|*.txt||");
if(OpenDialog.DoModal() == IDOK) // Якщо натиснули кнопку ОК в діалозі
{
char buf[255];
// Відкрити файл за заданим шляхом в режимі запису
try
{
CStdioFile file(OpenDialog.GetPathName(), CFile::modeCreate |
CFile::modeNoTruncate | CFile::modeWrite);
// переміщуємося в кінець файлу
file.SeekToEnd();
// записуємо дані з буфера (списку) в текстовий файл
deque<CString>::iterator i;//мій метод
for(i=lst.begin(); i != lst.end(); i++)
{
sprintf_s(buf,"%s\n",(*i).GetBuffer(255));//char=c s = string
file.WriteString(buf);
}
// записуємо у файл останню порцію даних, яка не потрапила в буфер
sprintf_s(buf,"%s\n",d);
file.WriteString(buf);
sprintf_s(buf,"%s\n",f);
file.WriteString(buf);
sprintf_s(buf,"%s\n",p);
file.WriteString(buf);
// Закриваємо файл
file.Close();
// очистити буфер
lst.clear();
}
catch (CInvalidArgException* ex)
{
//MessageBoxA(NULL,"Cannot open file");
return;
}
catch (CFileException* ex)
{
//MessageBoxA("Error writing file");
return;
}
catch (...)
{
//MessageBoxA("Unknown error");
return;
}
}
}
}
RGRDlg.cpp
#include "stdafx.h"
#include "RGR.h"
#include "RGRDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// Диалоговое окно CAboutDlg используется для описания сведений о приложении
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
…
…
…
…
BOOL CRGRDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Добавление пункта "О программе..." в системное меню.
// IDM_ABOUTBOX должен быть в пределах системной команды.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Задает значок для этого диалогового окна. Среда делает это автоматически,
// если главное окно приложения не является диалоговым
SetIcon(m_hIcon, TRUE); // Крупный значок
SetIcon(m_hIcon, FALSE); // Мелкий значок
// TODO: добавьте дополнительную инициализацию
// Встановлюємо стиль списку "Рапорт"
m_List.ModifyStyle(LVS_LIST, LVS_REPORT);
// Визначаємо розмір клієнтської області списку
CRect rect;
m_List.GetClientRect(&rect);
// Встановлюємо в список одну колонку, яка називається Data,
// розмір якої рівний ширині клієнтської області списку (ширині списку).
// Елементи вирівнюватимуться по лівій границі
m_List.InsertColumn(0,_T("Фігура"),LVCFMT_LEFT,rect.Width()/3);
m_List.InsertColumn(1,_T("Кількість сторін"),LVCFMT_LEFT,rect.Width()/3);
m_List.InsertColumn(1,_T("Периметр"),LVCFMT_LEFT,rect.Width()/3);
return TRUE; // возврат значения TRUE, если фокус не передан элементу управления
}
…
…
…
void CRGRDlg::OnBnClickedWrite()
{
// Записуємо значення з контролів в змінні m_DeviceName
UpdateData(true);
obj.WriteString(m_figureName.GetString(),m_frenglyName.GetString(),m_Price.GetString());
// TODO: добавьте свой код обработчика уведомлений
}
void CRGRDlg::OnLvnItemchangedList3(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: добавьте свой код обработчика уведомлений
*pResult = 0;
}
void CRGRDlg::OnBnClickedRead()
{
// TODO: добавьте свой код обработчика уведомлений
CString data;
CString buff[255];
int j = 0;
int i = 0;
CFileDialog dlg(true,"txt",NULL,OFN_FILEMUSTEXIST|OFN_HIDEREADONLY,"Text files (*.txt)|*.txt||");
if(dlg.DoModal() == IDOK){
m_List.DeleteAllItems();
CStdioFile file(dlg.GetPathName(),CFile::modeRead);
file.SeekToBegin();
while(file.ReadString(data)){
buff[j] = data;
j++;
}
LVITEM lv;
for(int i = 0; i < j; i+=3){
m_List.InsertItem(i,buff[i]);
}
int p = 0;
for(int i = 1; i < j; i+=3){
lv.mask = LVIF_TEXT;
lv.iSubItem = 1;
lv.iItem = p;
lv.pszText = (LPTSTR)(LPCTSTR)buff[i];
m_List.SetItem(&lv);
p++;
}
p = 0;
for(int i = 2; i < j; i+=3){
lv.mask = LVIF_TEXT;
lv.iSubItem = 2;
lv.iItem = p;
lv.pszText = (LPTSTR)(LPCTSTR)buff[i];
m_List.SetItem(&lv);
p++;
}
}
UpdateData(false);
}
void CRGRDlg::OnEnChangeEdit4()
{
// TODO: Если это элемент управления RICHEDIT, то элемент управления не будет
// send this notification unless you override the CDialogEx::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Добавьте код элемента управления
}
void CRGRDlg::On32771()
{
OnBnClickedRead();
}
void CRGRDlg::On32772()
{
// TODO: добавьте свой код обработчика команд
OnBnClickedWrite();
}
void CRGRDlg::On32773()
{
// TODO: добавьте свой код обработчика команд
}
Результат виконання програми:
/
Рис.1. Ескіз вікна програми і ввід даних у графи “Ввід даних”
/
Рис.2. Ескіз вікна запису у файл після натискання клавіші “Запис даних”
/
Рис.3. Читання даних з файлу