ООП на MFС примерах (OOP in MFC examples)

Автор: | 05.02.2018

Создание MFC приложения
Простейшие оконные приложения:

1. Пустое окно
2. Обработка события в окне
3. Рисунок в окне
4. Вставка элементов управления в окно
5. Создание производного класса
6. Вставка панели состояния от события создания окна 

Файловая структуризация приложений
Обмен данными между объектами
Контрольные задания
Пример выполнения контрольной работы
Полезные ссылки

Библиотека (MicrosoftFoundationClasses – MFC) предоставляет программистам удобные классы объектов, в которых размещены все наиболее важные структуры и API-функции.

Создание MFC приложения

Запустите Visual Studio. Выполните команду File > New>Project, на экране появляется окно New Project . В окне выберите тип проекта Win32 и прототип Win32 Project. Введите в текстовом поле Name имя проекта first, а в поле Location укажите каталог для проекта.

Описание приводится для Visual Studio версий 15 и ниже. Инсталляция Visual Studio версий 17 и выше имеет свои особенности. Необходимо указывать вручную необходимые компоненты.  Без них при создании проектов могут появляться разного рода ошибки.  Полезная ссылка: Установка Visual Studio 2017 Сommunity

Нажмите кнопку ОК. На экране появляется новое диалоговое окно с именем Win32 Application Wizard. Нажмите в нем кнопку Application Settings, в разделе Additional options установите флажок для опции Empty project (Пустой проект)  и нажмите кнопку Finish.

Подключите к проекту MFC библиотеку. Выберите пункт меню Project => Properties  (Проект => Свойства), появится окно first Property Pages (Страницы свойств проекта first). В этом окне активизируйте пункт General, откроется страница, в пункте Use of MFC (использовать ли MFC) установите Use MFC in a Static Library (использования MFC как статической библиотеки)

Добавим в проект файл, в котором будет исходный код. Мы же с вами выбрали пустой проект и нам нужен cpp файл в этом проекте. Щелкните правой кнопкой на папке Source Files (Исходные файлы) в окне Solution Explorer (Поиск решений). Выберите пункт меню Add => New Item (Добавить => Новый элемент).

Откроется окно Add New Item. Выберите в списке Templates (Шаблоны) пункт C++ File (.cpp). Укажите Ishodnik в качестве названия файла. Не изменяйте значение расположения (Location), принятое по умолчанию. Щелкните на кнопке Add (Добавить) для того, чтобы закрыть диалог Add New Item dialog (Добавить новый элемент) и открыть Source Editor (Редактор текстов программ).

Введите в окно исходник файла (Ishodnik.cpp)

#include <afxwin.h>

class CFirstApp:public CWinApp
{
public:
virtual BOOL InitInstance(){
AfxMessageBox(L"First MFC-application");
return FALSE;}
};

CFirstApp theApp;

Скомпилируйте и запустите проект: Build => Build (Создать => Создать)

Если возникают ошибки из-за преобразования символов, тогда в окне свойств проекта установите «Использовать набор символов Юникода»:

Простейшие MFC приложения

MFC приложение _1. Приложение выводит окно:

#include <afxwin.h>   // Подключение MFC библиотеки

class CMyMainWnd : public CFrameWnd
{
public:
CMyMainWnd(){ Create(NULL, L"My title"); } // 3. вызов конструктора класса окна
};

class CMyApp : public CWinApp
{
public:
CMyApp(){}; // конструктор
virtual BOOL InitInstance(){
m_pMainWnd=new CMyMainWnd();      // 2. Создание объекта класса окна
m_pMainWnd->ShowWindow(SW_SHOW);  // 4. Вызов функции объекта класса окна
return TRUE;}
};

CMyApp theApp;      // 1. Создание объекта класса приложения

Порядок выполнения программы обозначен пунктами 1-4.

Возникает вопрос, где функция Main? Ведь в приложении на С/С++  выполнение программы начинается с вызова функции Main. Она есть, правда у нее другое имя – WinMain. Функция WinMain скрыта от программиста в описании класса CWinApp. Вызов этой функции осуществляется неявно при объявлении переменной класса приложения (CMyApp theApp;). Функция WinMain выполняет ряд действий, в том числе обеспечивает вызов функции InitInstance.

В этом приложении использованы два MFC класса – CWinApp (класс приложения) и CFrameWnd (класс окна).

CWinApp – это класс, который обеспечивает запуск приложения. На основе этого класса создан производный класс (CMyApp), в котором переопределена виртуальная функция InitInstance. Эта функция запускается в MFC приложениях сразу же после создания объекта окна.  В ней пользователь устанавливает дальнейший порядок выполнения программы.

При запуске функции InitInstance() динамически создается объект класса CMyMainWnd. Для этого используется переменная-указатель m_pMainWnd. Эта переменная объявлена в базовом классе CWinApp, но она доступна и в производном классе по  правилу наследования «не нашел у себя, можешь позаимствовать у отца». При создании объекта класса окна вызывается его конструктор. Метод ShowWindow(SW_SHOW) также определен в базовом классе окна и объект производного класса может им воспользоваться.

На основе рассмотренного примера сделаем обобщающие выводы. Приложение, построенное на основе библиотеки MFC, – «айсберг», большая часть которого невидима, но является основой всего приложения. Часть приложения, лежащая в библиотеке MFC, называется каркасом приложения. Процесс взаимодействия между каркасом и частью приложения, разработанной программистом строится на основе следующих особенностей объектно-ориентированного программирования:

  • в методах, определенных программистом, могут встречаться вызовы методов базового класса (сын может воспользоваться методом отца);
  • виртуальные функции производного класса могут быть вызваны из функции родительского класса.

MFC приложение _2. Модифицируем MFC приложение _1, обеспечим  возможность обработки события – на щелчок мышкой в окне появляется сообщение MessageBox:

Ниже приводятся фрагменты изменения предыдущей программы. В класс окна добавлены карта сообщений и функция обработки сообщений.

class CMyMainWnd : public CFrameWnd{
public:
CMyMainWnd(){ Create(NULL,L"My title");}
afx_msg void OnLButtonDown(UINT, CPoint); // объявляется функция обработки сообщений
DECLARE_MESSAGE_MAP() // объявляется карта сообщений
};
// определяется карта сообщений
BEGIN_MESSAGE_MAP(CMyMainWnd, CFrameWnd) //Начало карты сообщений
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()                        //Конец карты сообщений
// определяется функция обработки сообщений
void CMyMainWnd::OnLButtonDown(UINT, CPoint){
AfxMessageBox(L"Левая кнопка мыши");  // L – макрос явного переопределения типа 
}

Обработка событий (сообщений) в программах на различных языках и типах приложений выполняется по разному. В Visual Basic для каждого события используются процедуры обработки событий.  В Java приложениях для определения события используются три действующих лица – объект-источник события, объект-слушатель события и объект-событие.  В WinApi С++ приложениях события идентифицируются именем константы (WM_PAINT, WM_DESTROY  и др.) в конструкции switch.. case… break.

switch (messg)
 {
 case WM_PAINT:
...
break;

В MFC приложениях события также идентифицируются именем константы ON_WM_LBUTTONDOWN(), ON_WM_PAINT() и др. . Константы помещаются в так называемую карту сообщений. В карте сообщений указываются все события, относящиеся к описываемому классу.  Первый параметр в карте сообщений показывает, для какого класса мы пишем нашу карту сообщений, а второй – кто должен обрабатывать то или иное сообщение, если наш класс не может (обычно – базовый класс).

Необходимо также объявить в классе и определить функцию обработки сообщения. Обратите внимание на соответствие имен функции OnLButtonDown и константы ON_WM_LBUTTONDOWN(). Эти имена описаны в каркасе библиотеки MFC.

Префикс Afx в AfxMessageBox(«…») есть признаком, по которому можно определить принадлежность того или иного объекта к библиотеке MFC.

В объявлении функции OnLButtonDown декларация afx_msg тождественна virtual, т.е., по сути, здесь используется механизм переопределения виртуальной функции.

MFC приложение _3. Добавим в приложение возможность рисовать в окне (прямоугольник в левом верхнем окне):

class CMyMainWnd : public CFrameWnd{
...

afx_msg void OnLButtonDown(UINT, CPoint);
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
};

Затем добавляем макрос в карту сообщений:

BEGIN_MESSAGE_MAP(CMyMainWnd, CFrameWnd)
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
END_MESSAGE_MAP()

И, наконец, пишем реализацию нашей функции:

void CMyMainWnd::OnPaint(){ CPaintDC* pDC=new CPaintDC(this);
pDC->Rectangle(1,1,10,10); }

В функции объявили  указатель pDC класса CPaintDC и динамически создали объект – так называемый контекст устройства (объект для рисования в окне). Ключевое слово this указывает на объект класса CMyMainWnd, в котором создается контекст устройства.

У контекста устройства много разных методов рисования (окружности, эллипсы и т. д.). Один из них – это рисование прямоугольника, который мы и используем. Его параметры – это координаты верхнего левого и правого нижнего углов.

MFC приложение _4. – Вставка элемента управления Controls в окно.

Используем для этого следующую иерархическую модель вхождения (агрегации) объектов одного класса в другой:

Для решения задачи необходимо выполнить следующие изменения в программе:

  1. В описании класса окна надо объявить указатели на элементы управления:
CButton* MyButton;
...
  1. В  конструкторе   окна указатели превратить в объекты и вызвать функцию Create():
MyButton = new CButton();
MyButton->Create(L"MyButton",WS_CHILD|WS_VISIBLE|SS_CENTER,
CRect(120,10,220,50),this,IDC_MYBUTTON);
...
  1. В деструкторе окна обеспечить удаление динамических объектов при завершении программы:
if (MyButton!=NULL) delete MyButton;
...

Фрагмент программы:

#include "afxwin.h"
#define IDC_MYBUTTON 100 // Идентификатор кнопки
#define IDC_MYEDIT 102 // Идентификатор поля редактирования

class CMyMainWnd: public CFrameWnd
{
public:
CMyMainWnd();
~CMyMainWnd();
private:
CStatic* MyStatic;
CButton* MyButton;
CEdit* MyEdit;
};

CMyMainWnd::CMyMainWnd()
{
Create(NULL,"Step2",WS_OVERLAPPEDWINDOW,rectDefault,
NULL,NULL);// Создать окно программы
// оператор new по умолчанию в случае ошибки вернет NULL
// проверка указателя на NULL дает возможность избавиться от дальнейших ошибок
MyStatic = new CStatic();
if (MyStatic!=NULL) MyStatic->Create("MyStatic",WS_CHILD|WS_VISIBLE|SS_CENTER,
CRect(10,10,100,50),this); // создали
MyButton = new CButton();
if (MyButton!=NULL) MyButton->Create(L"MyButton",WS_CHILD|WS_VISIBLE|SS_CENTER,
CRect(120,10,220,50),this,IDC_MYBUTTON);
MyEdit = new CEdit();
if (MyEdit!=NULL) MyEdit->Create(WS_CHILD|WS_VISIBLE|WS_BORDER,
CRect(240,10,340,50),this,IDC_MYEDIT);
}

CMyMainWnd::~CMyMainWnd()
{
if (MyStatic!=NULL) delete MyStatic; // удалить динамический объект
if (MyButton!=NULL) delete MyButton; // удалить динамический объект
if (MyEdit!=NULL) delete MyEdit; // удалить динамический объект
}

MFC приложение _5. – необходимо обеспечить, чтобы кнопка (объект класса CButton) реагировала на щелчки (DblClk),  перемещаясь с одного места окна в другое. В стандартном классе CButton это не предусмотрено. Следовательно, необходимо создать производный класс CMyButton.

class CMyButton: public CButton
{
public:
afx_msg void OnLButtonDblClk(UINT, CPoint);
afx_msg void OnRButtonDblClk(UINT, CPoint);
DECLARE_MESSAGE_MAP();
};

void CMyButton::OnLButtonDblClk(UINT, CPoint)
{
MoveWindow(CRect(120,100,220,150),TRUE);
}

void CMyButton::OnRButtonDblClk(UINT, CPoint)
{
MoveWindow(CRect(120,10,220,50),TRUE);
}

BEGIN_MESSAGE_MAP(CMyButton, CButton)
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDBLCLK()
END_MESSAGE_MAP()

Не забудьте правило, что описание класса в программе должно предшествовать его использованию. Также не забудьте в тексте программы заменить CButton на CMyButton.

MFC приложение _6. – Вставка в окно панели состояния.

Создадим в нашем приложении еще и панель состояния не динамически (не через указатель), а статически (через объявление переменной объекта), причем – от события создания окна.

  1. Включаем библиотеку расширений MFC:
#include "afxext.h"
  1. В класс окна добавляем процедуры создания и описание класса панели состояния.
int OnCreate(LPCREATESTRUCT lpCreateStruct);
CStatusBar m_wndStatusBar;
  1. Добавляем в карту сообщений класса окна реакцию на создание окна:
BEGIN_MESSAGE_MAP(CMyMainWnd, CFrameWnd)
…
ON_WM_CREATE() // событие создания окна
END_MESSAGE_MAP()
  1. Описываем процедуру создания окна, а в ней создаем панель состояния:

int CMyMainWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
m_wndStatusBar.Create(this);
return 0;
}

Обратите внимание на обращение к функциям – через имя класса и имя объекта. Функции, к которым можно обращаться через имя класса, называются статическими. Подробно их рассмотрим позднее на примерах Java приложений.

Файловая структуризация приложений

До сих пор мы использовали только один файл для записи программы. Но, обычно MFC приложение записывается в нескольких файлах, что позволяет хорошо структурировать программу.

В файлы  с расширением .h (хедер или заголовочные файлы) обычно переносится описание классов и деклараций. В файлах .cpp (исходные файлы) помещается определение функций и выполняемый код.

Установить разметку графического интерфейса можно следующими способами:

  • создать элементы управления программно в коде файлов .h  и  .cpp;
  • описать элементы интерфейса в файлах ресурсов;
  • сочетание предыдущих способов – базовые элементы разметки определить в файлах ресурсов, а остальные добавлять во время выполнения.

Ниже приводится пример структурированного приложения mfc_menu, которое содержит все указанные файлы.

Добавьте в проект файлы с различными расширениями (описание файлов приводится ниже) через контекстное меню, наведя курсор на соответствующую файлам папку проекта.

Для создании файла  с расширением .rc в открывшемся окне выбирается подпункт Resource File…

После создания файла  с расширением .rc его код для редактирования раскрывается в контекстном меню кнопкой View Code.

Описание графических элементов интерфейса

Файл resource.h (заголовочный файл)

#define IDR_MENU 101
#define ID_TEST_BEEP 40001
#define ID_TEST_EXIT 40002

Файл resource.rc

#include "resource.h"
IDR_MENU MENU DISCARDABLE
BEGIN
POPUP "Test"
BEGIN
MENUITEM "Beep", ID_TEST_BEEP
MENUITEM SEPARATOR
MENUITEM "Exit", ID_TEST_EXIT
END
END

Описание классов и определений входящих функций

Файл menu.h (заголовочный файл)

// #include <afxwin.h>
class CMenuApp: public CWinApp
{
public:
virtual BOOL InitInstance();
};

Файл menu.cpp

#include <afxwin.h>
#include "menu.h"
#include "menum.h"

BOOL CMenuApp::InitInstance()
{
m_pMainWnd= new CMainWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}

CMenuApp theApp;

Файл menum.h

// #include <afxwin.h>
class CMainWindow : public CFrameWnd
{
public:
CMainWindow();
afx_msg void TestBeep();
afx_msg void TestExit();
DECLARE_MESSAGE_MAP()
};

Файл menum.cpp

#include <afxwin.h>
#include "menum.h"
#include "resource.h"
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_COMMAND(ID_TEST_BEEP,TestBeep)
ON_COMMAND(ID_TEST_EXIT,TestExit)
END_MESSAGE_MAP()

CMainWindow::CMainWindow()
{
Create(NULL,"Hello",WS_OVERLAPPEDWINDOW,rectDefault,
NULL,MAKEINTRESOURCE(IDR_MENU));
}

void CMainWindow::TestBeep()
{
MessageBeep(0);
}

void CMainWindow::TestExit()
{
DestroyWindow();
}

Обратите внимание, как осуществляется взаимосвязь между описанием дизайна меню в файлах ресурсов и определением их действия в сpp-файлах. Для этого служат идентификаторы (например, ID_TEST_BEEP). На языках DCL и VLISP такая взаимосвязь осуществляется через свойство key. На VB взаимосвязь между описанием элементов и процедурами обработки событий осуществляется через  свойство name элементов формы.

Контрольные задания:

  1. Обеспечить появление MessageBox с сообщением «Привет» при щелчке в окне.
  2. Обеспечить появление MessageBox с сообщением «Привет» при щелчке на кнопке меню «Beep».

Обмен данными между объектами

Практически во всех объектах C++ хранятся определенные данные, а для их обработки используются функции. Для обработки данных используются функции, которые работают с внутренними данными класса, причем подробности этой работы скрываются от внешних частей программы, т.е. данные должны быть закрытыми. Это чрезвычайно полезно, поскольку большую программу удается разбить на блоки приемлемого размера. Однако обмен данными между объектами приложения пусть в минимальном количестве, но все же должен осуществляться. Доступ к закрытым данным объекта может осуществляться с помощью открытых функций. Обращение к закрытым переменным через PublicMethod входит в число стандартных приемов программирования, тем самым удается ограничить доступ к закрытым данным класса.

Рассмотрим, на примерах, как осуществляется обмен данными в MFC приложениях.

Создайте проект типа Win32 Application c подключением MFC. Создайте ресурс – диалоговую панель с  идентификатором  IDD_DIALOG1 (обычно это имя идентификатора присваивается по умолчанию). Диалоговое окно добавляется через выбор кнопки Resource… в контекстном меню и, затем — кнопки Dialog в открывшемся окне.

 

Добавьте в проект исходный файл со следующим кодом:

#include "afxwin.h"
#include "resource.h"

class CMyDlg : public CDialog{
CEdit* pEdit;
virtual BOOL OnInitDialog();
virtual void OnOK();
CString GetResultData();
public:
CMyDlg(CWnd* pParent=NULL);
~CMyDlg() {if (pEdit!=NULL) delete pEdit;}
};

class CMyMainWnd : public CFrameWnd
{
afx_msg void OnLButtonDown(UINT, CPoint){CMyDlg dlg; dlg.DoModal();}
public:
CMyMainWnd(){Create(NULL,L"My title");}
DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMyMainWnd, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

class CMyApp : public CWinApp
{
CString m_PrivateString;
virtual BOOL InitInstance()
{
m_pMainWnd=new CMyMainWnd();
m_pMainWnd->ShowWindow(SW_SHOW);
return TRUE;
}
public:
CString m_PublicString;
CMyApp (){
m_PublicString = "Public";
m_PrivateString = "Private";}
CWnd* Getm_pMainWndData(){return m_pMainWnd;}
};

CMyApp theApp;

CMyDlg::CMyDlg(CWnd* pParent):CDialog(IDD_DIALOG1, pParent){}

BOOL CMyDlg::OnInitDialog()
{
pEdit = new CEdit; //динамически создается окно редактирования
pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
CRect(10, 10, 100, 30), this, 1);
return TRUE;
}

void CMyDlg::OnOK()
{
CWnd* pMainWnd = theApp.Getm_pMainWndData();
pMainWnd->SetWindowText(GetResultData());
SetWindowText(GetResultData());
}

CString CMyDlg::GetResultData()
{
CString pResult;
if (pEdit != NULL)
pEdit->GetWindowText(pResult); // параметр – текстовый буфер
return pResult;
}

Почему значение переменной pResult обновляется при использовании ее в качестве параметра в функции  GetWindowText(pResult)? Подсказку можно найти здесь.

Запустите программу. Открывается главное окно. При щелчке левой кнопкой мыши в главном окне появляется диалоговая панель.

 

После ввода текста и нажатия кнопки ОК текст устанавливается в заголовке диалоговой панели и главного окна.

 

Ознакомьтесь с программой – определите последовательность выполнения программы, обратите внимание на специфические особенности программы, связанные с обменом данных.

Для закрепления материала выполните следующие  задания:

  1. Модифицируйте программу, обеспечив при открытии диалогового окна инициализацию текстового окна значением переменной m_PublicString, описанной в классе приложения CMyApp (см.  код программы)

  1. Модифицируйте программу, обеспечив инициализацию текстового окна значением переменной m_PrivateString, описанной в классе приложения (см. код программы)

 

Контрольные задания

Модифицировать одно из 2-х исходных приложений, в соответствии с вариантом задания. Варианты задания приведены в таблице:

Вариант Задачи
1 1 3
2 2 4
3 1 5
4 2 6
5 1 7
6 2 8
7 1 9
8 2 10
9 11 13
10 12 14

В таблице для каждого варианта указаны номера 2-х задач, описание которых приводится ниже. Каждый студент должен выполнить свой вариант задания, который выбирается в соответствии с последней цифрой номера зачетной книжки. Если последняя цифра 0, выбирается 10 вариант.

Задачи к приложению mfc_simple (при запуске программы появляется основное окно):

  1. Добавить кнопку (класс CButton), на которой инициализируется сообщение «Привет». Перед удалением окна кнопка должна быть уничтожена из памяти. Программу структурировать по файлам (.cpp .h).
  2. Добавить поле редактирования (класс CEdit),в котором инициализируется сообщение «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти. Программу структурировать по файлам (.cpp .h).
  3. Добавить кнопку (созданную от базового класса CButton), при нажатии на которую (событие ON_WM_LBUTTONDBLCLK ) появляется MessageBox с сообщением «Привет». Перед удалением окна кнопка должна быть уничтожена из памяти. Программу структурировать по файлам (.cpp .h).
  4. Добавить поле редактирования (созданное от базового класса CEdit), при активизации которого (событие ON_WM_LBUTTONDOWN) появляется MessageBox с сообщением «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти. Программу структурировать по файлам (.cpp .h).
  5. Добавить кнопку (созданную от базового класса CButton), при нажатии на которую (событие ON_WM_RBUTTONDBLCLK ) надпись на кнопке меняется на «Привет». Перед удалением окна кнопка должна быть уничтожена из памяти. Программу структурировать по файлам (.cpp .h).
  6. Добавить поле редактирования (созданное от базового класса CEdit), при активизации которого (событие ON_WM_LBUTTONDOWN) в нем появляется сообщение «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти. Программу структурировать по файлам (.cpp .h).
  7. Добавить поле редактирования (класс CEdit). При щелчке в окне (событие ON_WM_LBUTTONDBLCLK) в поле редактирования появляется сообщение «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти. Программу структурировать по файлам (.cpp .h).
  8. Добавить кнопку (созданную от базового класса CButton). При щелчке в окне (событие ON_WM_LBUTTONDBLCLK) надпись на кнопке меняется на «Привет». Перед удалением окна кнопка должна быть уничтожена из памяти. Программу структурировать по файлам (.cpp .h).
  9. Добавить кнопку (созданную от базового класса CButton). При нажатии на кнопку (событие ON_WM_LBUTTONDBLCLK) заголовке окна появляется сообщение «Привет». Перед удалением окна кнопка должна быть уничтожена из памяти. Программу структурировать по файлам (.cpp  .h).
  10. Добавить поле редактирования (созданное от базового класса CEdit), при активизации которого (событие ON_WM_LBUTTONDOWN) в заголовке окна появляется сообщение «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти. Программу структурировать по файлам (.cpp .h).

Задачи к приложению mfc_menu  (при запуске программы появляется основное окно c кнопками меню):

    1. Добавить поле редактирования (класс CEdit). При нажатии на кнопку меню (кнопка Test->Beep) в поле редактирования появляется сообщение «Привет». Перед удалением окна поле редактирования должно быть уничтожено из памяти.
    2. Добавить кнопку (класса CButton). При нажатии на кнопку меню (кнопка Test->Beep) на кнопке (класса CButton) появляется сообщение «Привет». Перед удалением окна кнопка (класса CButton) должна быть уничтожена из памяти.
    3. Добавить кнопку (созданную от базового класса CButton) и поле редактирования (класс CEdit). При нажатии на кнопку (событие ON_WM_LBUTTONDBLCLK) в поле редактирования появляется сообщение «Привет». Перед удалением окна кнопка и поле редактирования должны быть уничтожены из памяти. Программу структурировать по файлам (.cpp  .h).
    4. Добавить поле редактирования (созданное от базового класса CEdit) и кнопку (класс CButton). При активизации поля редактирования (событие ON_WM_LBUTTONDOWN) на кнопке появляется сообщение «Привет». Перед удалением окна кнопка и  поле редактирования должны быть уничтожены из памяти. Программу структурировать по файлам (.cpp  .h).

Пример выполнения контрольной работы

Этап 1. Запуск исходного приложения mfc_simple (при запуске программы появляется основное окно):

Код исходного файла:

#include <afxwin.h>
class CMyMainWnd : public CFrameWnd
{
public:
CMyMainWnd(){ Create(NULL, L"My title"); }   // 3
};

class CMyApp : public CWinApp
{
public:
CMyApp(){};
virtual BOOL InitInstance(){
m_pMainWnd=new CMyMainWnd();   // 2
m_pMainWnd->ShowWindow(SW_SHOW);  // 4
return TRUE;}
};

CMyApp theApp;        // 1– точка входа в программу

Этап 2. Структуризация программы по файлам.

Файл 1.h

#include <afxwin.h>
class CMyMainWnd : public CFrameWnd
{
public:
CMyMainWnd(){ Create(NULL, L"My title"); }
};

class CMyApp : public CWinApp
{
public:
CMyApp(){};
virtual BOOL InitInstance(){
m_pMainWnd=new CmyMainWnd();
m_pMainWnd->ShowWindow(SW_SHOW);
return TRUE;}
};

Файл 1.cpp

// #include <afxwin.h>
#include “1.h”
CMyApp theApp;

Поставим задачу: поместить в окне объект  класса CEdit, при активизации которого левой кнопкой мышки возникает соответствующее сообщение:

Этап 3. Поместим в окно объект  класса CEdit.

Файл 1.h

// #include <afxwin.h>
#define IDC_MYEDIT 102

class CMyMainWnd : public CFrameWnd
{
CEdit* MyEdit;
public:
CMyMainWnd();
~CMyMainWnd();
};

class CMyApp : public CWinApp
{
public:
CMyApp(){};
virtual BOOL InitInstance(){
m_pMainWnd=new CMyMainWnd();
m_pMainWnd->ShowWindow(SW_SHOW);
return TRUE;}
};

Файл 1.cpp

#include <afxwin.h>
#include "1.h"
CMyApp theApp;
CMyMainWnd::CMyMainWnd()
{
Create(NULL,L"Step2",WS_OVERLAPPEDWINDOW,rectDefault,NULL,NULL);
MyEdit = new CEdit();
if (MyEdit!=NULL) MyEdit->Create(WS_CHILD|WS_VISIBLE|WS_BORDER,
CRect(240,10,340,50),this,IDC_MYEDIT);
}

CMyMainWnd::~CMyMainWnd()
{
if (MyEdit!=NULL) delete MyEdit;
}

Этап 4. Создадим производный класс от класса CEdit.

Файл 1.h

// #include <afxwin.h>
#define IDC_MYEDIT 102

class CMyEdit: public CEdit
{
public:
afx_msg void OnLButtonDblClk(UINT, CPoint);
DECLARE_MESSAGE_MAP();
};

class CMyMainWnd : public CFrameWnd
{
CMyEdit* MyEdit;
public:
 …
};

...

Файл 1.cpp

#include <afxwin.h>
#include "1.h"

void CMyEdit::OnLButtonDblClk(UINT, CPoint)
{
AfxMessageBox(L"ПРИВЕТ");
}

BEGIN_MESSAGE_MAP(CMyEdit, CEdit)
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()

CMyMainWnd::CMyMainWnd()
{
...
MyEdit = new CMyEdit();
...
}
…

Этап 5. При активизации объекта класса CMyEdit в нем появляется сообщение «Привет». Такой же текст появляется в заголовке окна.

Оформление события активизации объекта класса CMyEdit остается таким же, как и на предыдущем этапе, изменяется только определение функции обработки события:


void CMyEdit::OnLButtonDblClk(UINT, CPoint)
{
//AfxMessageBox(L"ПРИВЕТ");
SetWindowText(L"ПРИВЕТ" );
}

Сложнее обеспечить сообщение «ПРИВЕТ» в заголовке окна, поскольку к функции SetWindowText необходимо обращаться через указатель на объект окна  m_pMainWnd.  Однако объект MyEdit не видит  переменную m_pMainWnd, поскольку она в классе приложения CMyApp закрыта. Чтобы ее видеть, в классе приложения  необходимо создать открытый метод:

CWnd* Getm_pMainWndData(){return m_pMainWnd;}

В событии CMyEdit::OnLButtonDblClk можно обратиться к этому методу через объектную переменную theApp, которая  объявлена глобальной и, поэтому видима в приложении везде.

void CMyEdit::OnLButtonDblClk(UINT, CPoint)
{
//AfxMessageBox(L"ПРИВЕТ"); 
SetWindowText(L"ПРИВЕТ" );
CWnd* pMainWnd = theApp.Getm_pMainWndData();
pMainWnd->SetWindowText(L"ПРИВЕТ");
}

Этап 6. При активизации командной кнопки  в текстовом окне появляется сообщение.

Командная кнопка создается по аналогии с  текстовым окном

void CMyButton::OnLButtonDblClk(UINT, CPoint)
{
CMyMainWnd* pMainWnd = (CMyMainWnd *)theApp.Getm_pMainWndData();
pMainWnd->SetWindowText(L"Привет");
CMyEdit* ed = pMainWnd->Get_MyEdit();
ed->SetWindowText(L"Привет");
}

Обратите внимание на явное преобразование типа созданного указателя pMainWnd. Это необходимо сделать, поскольку указатель  m_pMainWnd, возвращаемый функцией Getm_pMainWndData, унаследован от базового класса CWinApp и ему неизвестны объекты и функции, добавленные в производный класс CMyMainWnd.

 Функция Get_MyEdit() должна быть определена в классе окна CMyMainWnd. Она достает  из класса окна закрытую переменную-указатель MyEdit.

CMyEdit* Get_MyEdit(){return MyEdit;}

Полезные ссылки:

Установка Visual Studio 2017 Сommunity

 

 

Автор: Николай Свирневский