Использование Visual C++. Часть 1
1. Создание проекта с помощью MFC AppWizard
- Выберите File – New – Projects, в дереве типов проектов раскройте Visual C++, выберите MFC, а в поле Templates – MFC Application, в поле Name набеерите имя проекта FirstProgram. Можно также снять флажок Create directory for solution. Нажмите ОК. Нажмите кнопку Next.
- В окне Application Type нам необходимо выбрать Multiple Documents – этот тип используется по умолчанию. Нажмите кнопку Next два раза.
- В окне Document Template StringsSpecify можно задать ряд свойств приложения, а именно: заголовок приложения, расширение для открываемых
файлов и т.п. Нажмите кнопку Next два раза.
- В окне User Interface Features можно задать размер окна приложения, наличие системного меню, кнопок изменения размера окна и т.д. Нажмите кнопку Next два раза.
- В окне Generated Classes в качестве Base Class задайте CFormView. В этом случае вид документа будет основан на диалоге. Нажмите кнопку Finish и ответьте Yes на предложение No printing support.
2. Создание формы для документа
- В появившемся окне нарисуйте следующий диалог (пункт меню Layout позволяет выравнивать
элементы диалога, делать их одинаковыми по размеру, центрировать и т.д.):

Используемые элементы: Static Text, Edit Box, Spin, Combo Box.
Идентификаторы элементов диалога: IDC_STATIC, IDC_EDIT_DAY, IDC_EDIT_MONTH,
IDC_EDIT_YEAR, IDC_SPIN_DAY, IDC_SPIN_MONTH, IDC_SPIN_YEAR, IDC_COMBO_EVENTS,
IDC_EDIT_COMMENTS. Для элементов Spin нужно установить параметры Auto buddy,
Set buddy integer (эти параметры позволяют автоматически связать элемент Spin и
соседним с ним Edit Box) и Alignment = Right (вкладка Styles). Для элемента
Combo Box нужно добавить данные: Встреча, Звонок, День рождения, Другое (для разделения
элементов используются клавиши Ctrl-Enter). Для элемента IDC_EDIT_COMMENTS установите
свойство Disabled.
- Необходимо сменить язык диалога на русский. Для этого в окне ResourseView найдите диалог с
идентификатором IDD_FIRSTPROGRAM_FORM, нажатием правой кнопки мыши вызовите
контекстное меню, выберите Properties и измените язык.
- Теперь можно откомпилировать и запустить программу (для этого можно использовать кнопку с
красным восклицательным знаком). У вас готова оболочка и внешнее представление приложения,
однако, она ещё не делает ничего существенного.
3. Работа с элементами диалога и данными, отображаемыми в диалоге
Для работы с элементами диалога и отображаемыми в них данными элементам диалога надо
сопоставлять переменные, которые могут быть двух категорий: Value и Control. Переменные катего-
рии Value используются для работы с данными, а переменные категории Control – для работы с
самими элементами. Иногда одному элементу могут быть сопоставлены переменные категории Value
разных типов, например, для Edit Box такая переменная может быть строковой или
целочисленной.
- Выберите View – ClassWizard. Не создавайте новый класс! За отображение данных документа
отвечает класс CFirstProgramView (который уже существует), поэтому все переменные для
работы с элементами диалога должны объявляться в этом классе.
- Перейдите на вкладку Member Variables и создайте следующие переменные:
| Идентификатор элемента |
Тип переменной |
Переменная |
| IDC_COMBO_EVENTS |
int |
m_Event |
| IDC_EDIT_COMMENTS |
CString |
m_Comments |
| IDC_EDIT_COMMENTS |
CEdit |
m_EditComments |
| IDC_EDIT_DAY |
int |
m_Day |
| IDC_EDIT_MONTH |
int |
m_Month |
| IDC_EDIT_YEAR |
int |
m_Year |
| IDC_SPIN_DAY |
CSpinButtonCtrl |
m_SpinDay |
| IDC_SPIN_MONTH |
CSpinButtonCtrl |
m_SpinMonth |
| IDC_SPIN_YEAR |
CSpinButtonCtrl |
m_SpinYear |
Теперь нам надо инициализировать отображаемые данные. Как было указано ранее, за отображение вашего документа отвечает класс CFirstProgramView, в котором, в частности, существует
функция OnInitialUpdate, которой и надо воспользоваться в данном случае.
- Найдите эту функцию через окно ClassView. Добавьте описания необходимых переменных и
после существующих в ней операторов необходимые действия (жирным шрифтом выделено то,
что добавляется):
void CFirstProgramView::OnInitialUpdate()
{ CTime time = CTime::GetCurrentTime();
int year = time.GetYear();
CFormView::OnInitialUpdate();
ResizeParentToFit();
m_SpinDay.SetRange(1, 31);
// Установка верхней и нижней границы
m_SpinMonth.SetRange(1, 12);
// изменения данных для элемента Spin
m_SpinYear.SetRange(year, year + 5);
m_Day = time.GetDay();
m_Month = time.GetMonth();
m_Year = year;
UpdateData(0);
 
// Модификация диалога
}
Теперь сделаем так, чтобы при выборе одного из вариантов с помощью элемента
IDC_COMBO_EVENTS, элемент IDC_EDIT_COMMENTS становился бы доступен, и можно было
бы ввести туда текст. Для этого надо написать функцию, которая будет вызываться, когда
произойдет событие «изменение выбранной строки» для элемента IDC_COMBO_EVENTS.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CFirstProgramView, в поле Object IDs выберите IDC_COMBO_EVENTS, в поле
Messages – CBN_SELCHANGE. Нажмите кнопки Add Function для добавления функции и
Edit Code для перехода к новой функции. Добавьте в неё следующие операторы:
UpdateData(1);
// Не забывайте модифицировать данные!
if (m_Event != -1)
// Номер строки, выбранной в Combo Box - начинается
// с 0, -1 означает, что ничего не выбрано
m_EditComments.EnableWindow(1);
// Делаем элемент доступным
else
m_EditComments.EnableWindow(0);
4. Открытие и сохранение
За открытие и сохранение, а также за создание нового документа, отвечает класс
CFirstProgramDoc. В этот класс необходимо добавить переменные, в которых будут храниться
данные. В нашем случае они продублируют переменные, которые мы создали в классе
CFirstProgramView, однако в более сложных случаях связь между данными и их отображением
может быть не такая однозначная.
В классе CFirstProgramDoc существуют функции OnNewDocument, OnOpenDocument,
OnSaveDocument, которые вызываются при соответствующих событиях, однако, обратите внимание,
что в эти функции нужно добавлять только вспомогательные действия, производимые при вводе и
выводе, например, установку начальных значений при создании нового документа, проверку
правильности данных при выводе, проверку правильности ввода/вывода. Сам ввод/вывод
осуществляется в функции Serialize того же класса CFirstProgramDoc! При этом вызов диалогов
для выбора файла, а также открытие и закрытие файлов берёт на себя та часть приложения, которая
генерируется автоматически, и вам не нужно заботиться об этом.
- Добавьте в класс CFirstProgramDoc переменные day, month, year, event и comments соответствующих типов. Для этого нужно нажатием правой кнопкой мыши вызвать контекстное меню для этого класса и выбрать Add Member Variable… Переменные должны быть
public.
- Функция OnNewDocument должна инициализировать эти переменные при создании нового
документа:
BOOL CFirstProgramDoc::OnNewDocument()
{ CTime time = CTime::GetCurrentTime();
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
day = time.GetDay();
month = time.GetMonth();
year = time.GetYear();
event = -1;
comments = "";
return TRUE;
}
- Однако нам теперь надо изменить функцию OnInitialUpdate так, чтобы она ссылалась на эти
переменные:
void CFirstProgramView::OnInitialUpdate()
{ int year = GetDocument()->year;
// Функция GetDocument позволяет сослаться
// на документ, к которому присоединён
// данный объект класса CFirstProgramView
CFormView::OnInitialUpdate();
ResizeParentToFit();
m_SpinDay.SetRange(1, 31);
m_SpinMonth.SetRange(1, 12);
m_SpinYear.SetRange(year, year + 5);
m_Day = GetDocument()->day;
m_Month = GetDocument()->month;
m_Year = year;
m_Event = GetDocument()->event;
m_Comments = GetDocument()->comments;
UpdateData(0);
// Модификация диалога
if (m_Event != -1)
m_EditComments.EnableWindow(1);
}
- Напишем ввод и вывод. Как было сказано ранее, это делается в функции Serialize:
void CFirstProgramDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
// Вывод
{ ar.Write(&day, sizeof(int));
ar.Write(&month, sizeof(int));
ar.Write(&year, sizeof(int));
ar.Write(&event, sizeof(int));
ar.Write(comments, 256);
}
else
// Ввод
{ ok = 1;
// В класс CFirstProgramDoc надо также добавить
// переменную ok типа int, которая будет
// использоваться для хранения признака ошибки
if (ar.Read(&day, sizeof(int)) == 0) ok = 0;
if (ar.Read(&month, sizeof(int)) == 0) ok = 0;
if (ar.Read(&year, sizeof(int)) == 0) ok = 0;
if (ar.Read(&event, sizeof(int)) == 0) ok = 0;
ar.Read(comments.GetBuffer(255), 256);
}
}
Однако если теперь попробовать сохранить файл, а потом открыть его, мы не увидим тех же
данных. Это связано с тем, что после изменения данных пользователем надо модифицировать соответствующие переменные класса CFirstProgramDoc. Мы сделаем это в функции
OnSaveDocument.
- Сначала надо добавить эту функцию. Откройте ClassWizard. Перейдите на вкладку
Message Maps. В поле Class Name выберите CFirstProgramDoc, в поле
Object IDs – тоже CFirstProgramDoc, в поле Messages – OnSaveDocument. Добавьте
функцию и отредактируйте её:
BOOL CFirstProgramDoc::OnSaveDocument(LPCTSTR lpszPathName)
{ POSITION pos;
CFirstProgramView *view;
pos = GetFirstViewPosition();
// Эти операторы используются для
view = (CFirstProgramView *)GetNextView(pos);
// доступа к объекту класса
// CFirstProgramView данного документа
view->UpdateData(1);
day = view->m_Day;
month = view->m_Month;
year = view->m_Year;
event = view->m_Event;
comments = view->m_Comments;
if (event == -1)
{ MessageBox(theApp.m_pMainWnd->m_hWnd, "Выберите событие",
"Внимание", MB_OK | MB_ICONWARNING);
return FALSE;
}
if (comments == "")
if (MessageBox(theApp.m_pMainWnd->m_hWnd,
"Вы уверены, что не хотите добавить комментарии?",
"Внимание", MB_YESNO | MB_ICONQUESTION) == IDNO)
return FALSE;
return CDocument::OnSaveDocument(lpszPathName);
}
- Однако прежде чем компилировать программу надо в начало файла добавить следующие
строки:
#include "FirstProgramView.h"
// Необходимо включить данный заголовочный файл для того,
// чтобы сослаться на класс CFirstProgramView
extern CFirstProgramApp theApp; // Объявление переменной theApp, которая нужна для того,
// чтобы передать функции MessageBox первый
// параметр – указатель на окно приложения
- Аналогично добавьте функцию OnOpenDocument и добавьте в неё код, который выводит
сообщение в случае ошибок при вводе:
BOOL CFirstProgramDoc::OnOpenDocument(LPCTSTR lpszPathName)
{ CString str;
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
// TODO: Add your specialized creation code here
if (!ok)
{ str = "Произошли ошибки при чтении файла '";
str += lpszPathName; str += "'";
MessageBox(theApp.m_pMainWnd->m_hWnd, str, "Ошибка",
MB_OK | MB_ICONSTOP);
return FALSE;
}
return TRUE;
}
5. Работа с меню
Если вы откроете окно ресурсов, то увидите, что ресурсы вашей программы содержат два
меню: IDR_MAINFRAME и IDR_FIRSTPTYPE. Первое меню отображается только тогда, когда нет
ни одного открытого документа. В остальных случаях отображается второе меню.
- Необходимо сменить язык меню на русский.
- Добавьте в меню IDR_FIRSTPTYPE пункт «Параметры». По умолчанию он отмечен как Pop-up
(т.е. меню, содержащее подменю) и не имеет идентификатора. Если вы хотите, то можете
перетащить этот пункт в любое место меню.
- Добавьте в этот пункт подпункт «Дополнительные параметры». Задайте ему идентификатор
ID_PARAM. В окне Prompt задается строка подсказки, которая может состоять из двух частей,
разделенных символом перевода строки \n. Первая часть содержит текст, который отображается в
строке состояния (строка внизу вашего приложения), а вторая часть (обычно не очень длинная)
отображается в виде всплывающей подсказки.
Если вы теперь запустите вашу программу, то увидите, что пункт меню «Дополнительные
параметры» отображается, но он не доступен. Это происходит потому, что мы не написали функцию,
которая будет выполняться при выборе данного пункта меню.
22. Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CChildFrame, в поле Object IDs – ID_PARAM, в поле Messages – COMMAND.
Добавьте соответствующую функцию. Даже если она будет пустая, пункт меню
«Дополнительные параметры» станет доступным.
6. Создание диалога
- Откройте окно ресурсов. Для добавления диалога выберите Insert Dialog из меню элемента
Dialog. Нарисуйте следующий диалог:
Используемые элементы: Group Box, Radio Button, Check Box.
Идентификаторы элементов диалога: IDC_STATIC, IDC_RADIO_THE_DAY,
IDC_RADIO_DAY_BEFORE, IDC_RADIO_ 2DAYS_BEFORE, IDC_RADIO_WEEK_BEFORE,
IDC_CHECK_SHOW, IDC_CHECK_DEL. Для кнопок используются идентификаторы, задаваемые по
умолчанию. Для первой радио-кнопки необходимо установить параметр Group. Для того чтобы
редактировать параметры всего диалога надо вызвать контекстное меню нажатием правой кнопки
мыши на диалоге, но не на каком-либо элементе. Для диалога будем использовать идентификатор
IDD_DIALOG_PARAM. Также необходимо сменить язык диалога на русский.
- Войдите в ClassWizard. Создайте новый класс для вашего диалога: CDialogParam.
- Перейдите на вкладку Member Variables и создайте следующие переменные:
| Идентификатор элемента |
Тип переменной |
Переменная |
| IDC_CHECK_DEL |
BOOL |
m_Del |
| IDC_CHECK_SHOW |
BOOL |
m_Show |
| IDC_RADIO_THE_DAY |
int |
m_Before |
Для каждой группы радио-кнопок создаётся только одна переменная, которая имеет значение
-1, если ни одна радио-кнопка не выбрана, значение 0 – если выбрана первая радио-кнопка, значение
1 – если выбрана вторая радио-кнопка и т.д.
- Создайте в классе CFirstProgramDoc соответствующие переменные before, show и del. Добавьте в функцию OnNewDocument операторы для инициализации этих переменных (например, значениями 0, 1 и 1), а в функцию Serialize – операторы для ввода/вывода этих переменных.
Теперь можно попробовать вызвать новый диалог. Для вызова диалога необходимо, в первую
очередь, объявить переменную соответствующего класса. Затем инициализируются внутренние
переменные класса. Для вызова диалога используется функция DoModal. После этого, если
пользователь нажал ОК, необходимо переменным before, show и del задать новые значения.
- В функцию, которая вызывается для пункта меню «Дополнительные параметры» добавьте
действия для работы с диалогом:
void CChildFrame::OnParam()
{ CDialogParam dp;
// Объявление переменной для диалога
CFirstProgramDoc *doc;
CFirstProgramView *view;
// TODO: Add your command handler code here
view = (CFirstProgramView *)GetActiveView();
// Получаем указатель на документ
doc = view->GetDocument();
dp.m_Before = doc->before;
// Инициализируем переменные диалога
dp.m_Show = doc->show;
dp.m_Del = doc->del;
if (dp.DoModal() == IDOK)
// Вызов диалога и проверка ответа
{ doc->before = dp.m_Before;
// Меняем переменные документа
doc->show = dp.m_Show;
doc->del = dp.m_Del;
}
}
- Для корректной работы программы необходимо в текущий файл добавить следующие директивы
препроцессора:
#include "FirstProgramView.h"
#include "FirstProgramDoc.h"
#include "DialogParam.h"
Однако в данный момент программа работать ещё не будет, т.к. мы не установили связь
между переменными m_Before, m_Show и m_Del и их отображением в диалоге. Для этого нужно добавить функции, которые будут производить начальную инициализацию диалога и сохранение
переменных при нажатии пользователем кнопки ОК.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CDialogParam, в поле Object IDs – тоже CDialogParam, в поле Messages –
WM_INITDIALOG. Добавьте функцию для инициализации диалога. В нее необходимо добавить
один оператор: UpdateData(0);
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CDialogParam, в поле Object IDs – IDOK, в поле Messages – BN_CLICKED.
Добавьте функцию, вызываемую при нажатии ОК. В нее необходимо добавить один оператор:
UpdateData(1);
Можно сделать так, чтобы элемент IDC_CHECK_SHOW («Показывать каждый день») был
доступен, только когда выбран показ сообщения раньше назначенного дня.
- Откройте ClassWizard, перейдите на вкладку Member Variables и для элемента
IDC_CHECK_SHOW создайте вторую переменную с именем m_CheckShow категории Control
типа CButton. Эта переменная будет использоваться для управления состоянием элемента.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CDialogParam, в поле Object IDs – IDC_RADIO_THE_DAY, в поле Messages –
BN_CLICKED. Добавьте функцию, вызываемую при нажатии первой из радио-кнопок. В нее
необходимо добавить один оператор m_CheckShow.EnableWindow(0);, который сделает
соответствующий элемент недоступным.
- Аналогично создайте функции, вызываемые при нажатии остальных радио-кнопок. В них надо
добавить тот же оператор, только параметр функции должен быть 1 для того, чтобы сделать
элемент доступным.
7. Работа с панелью инструментов
- Для добавления панель в строку инструментов откройте через окно ресурсов панель
инструментов, имеющую идентификатор IDR_MAINFRAME. На пустой кнопке нарисуйте
подходящую иконку.
- Дважды щелкните на кнопке и в параметрах в качестве идентификатора выберите идентификатор
того пункта меню, с которым вы хотите связать кнопку, – ID_PARAM.
8. Запрос на сохранение
Запрос на сохранение закрываемого файла выдаётся приложением автоматически в том
случае, если известно, что документ был изменен.
- Добавьте в функцию CFirstProgramView::OnSelchangeComboEvents() оператор
GetDocument()->SetModifiedFlag(1); и проверьте результат.
Для того, что бы запрос выдавался при любом изменении данных, Вам придется добавлять
соответствующие функции, которые вызываются при изменении данных в том или ином элементе, и
добавлять в функции этот оператор. Элемент Edit Box при изменении посылает сообщение
EN_CHANGE, следовательно, необходимо написать функции, которые вызывались бы при
получении такого сообщения от каждого элемента Edit Box.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CFirstProgramView, в поле Object IDs – IDC_EDIT_COMMENTS, в поле Messages
– EN_CHANGE. Добавьте соответствующую функцию и отредактируйте её, добавив оператор
GetDocument()->SetModifiedFlag(1);.
- Аналогично создайте и отредактируйте функции для других элементов Edit Box.
- Добавьте этот оператор в функцию CChildFrame::OnParam(), чтобы установить, что документ
был изменён, если пользователь изменил дополнительные параметры (если, конечно, он нажал
кнопку ОК!).
9. Создание всплывающего меню
Всплывающее меню, которое вызывается нажатием правой кнопки мыши на нужном
элементе, – очень удобная для пользователя возможность, поэтому в хорошей программе
обязательно должны быть такие меню. Создать его не сложно – объявляется соответствующая
переменная, добавляются нужные пункты меню и пишутся действия для каждого пункта.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CFirstProgramView, в поле Object Ds – тоже CFirstProgramView, в поле Messages
– WM_RBUTTONUP. Добавьте соответствующую функцию.
- В эту функцию добавьте действия по созданию меню и работе с ним:
void CFirstProgramView::OnRButtonUp(UINT nFlags, CPoint point)
{ CMenu menu;
// Переменная для меню
// TODO: Add your message handler code here and/or call default
ClientToScreen(&point);
// Преобразуем координаты для отображения меню
// в нужном месте
menu.CreatePopupMenu();
// Создание меню и добавление пунктов меню
menu.AppendMenu(MF_STRING, ID_PARAM, "Дополнительные параметры");
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_CLEAR, "Очистить");
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON,
// Функция отображения меню
point.x, point.y, this, NULL);
menu.DestroyMenu();
// Уничтожение меню
CFormView::OnRButtonUp(nFlags, point);
}
- В файл Resource.h необходимо добавить строку #define ID_CLEAR 32772 для определения идентификатора ID_CLEAR (предыдущие идентификаторы создавались автоматически).
Теперь меню отображается при нажатии правой кнопки мыши, но ещё ничего не делает.
Обработка команд, получаемых от меню, происходит в функции OnCommand.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CFirstProgramView, в поле Object IDs – тоже CFirstProgramView, в поле Messages
– OnCommand. Добавьте соответствующую функцию и отредактируйте её код:
BOOL CFirstProgramView::OnCommand(WPARAM wParam, LPARAM lParam)
{ CDialogParam dp;
CFirstProgramDoc *doc;
CTime time = CTime::GetCurrentTime();
// TODO: Add your specialized code here and/or call the base class
switch (LOWORD(wParam))
// Проверка, какой пункт меню был выбран
{ case ID_PARAM:
doc = GetDocument();
dp.m_Before = doc->before;
dp.m_Show = doc->show;
dp.m_Del = doc->del;
if (dp.DoModal() == IDOK)
{ doc->before = dp.m_Before;
doc->show = dp.m_Show;
doc->del = dp.m_Del;
doc->SetModifiedFlag(1);
}
break;
case ID_CLEAR:
m_Day = time.GetDay();
m_Month = time.GetMonth();
m_Year = time.GetYear();
m_Event = -1;
m_Comments = "";
UpdateData(0);
m_EditComments.EnableWindow(0);
GetDocument()->SetModifiedFlag(1);
break;
}
return CFormView::OnCommand(wParam, lParam);
}
10. Включение и отключение пунктов меню
Если закрыть все окна, кнопка панели инструментов «Дополнительные параметры» всё равно
остаётся доступной, и при нажатии на неё возникают ошибки. Чтобы включать и отключать пункты
меню при определённых условиях (а кнопки панели инструментов, по сути, являются более удобной
формой для вызова пунктов меню и работают синхронно с соответствующими пунктами меню),
необходимо обрабатывать сообщение UPDATE_COMMAND_UI.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CChildFrame, в поле Object IDs – ID_PARAM, в поле Messages –
UPDATE_COMMAND_UI. Добавьте соответствующую функцию и отредактируйте её код:
void CChildFrame::OnUpdateParam(CCmdUI* pCmdUI)
{ CWnd *wnd;
// TODO: Add your command update UI handler code here
wnd = GetFocus();
if (wnd)
// Если есть хоть какое-то окно
pCmdUI->Enable(1);
// включаем пункт меню (и кнопку)
else
// Если нет окон
pCmdUI->Enable(0);
// отключаем пункт меню (и кнопку)
}
11. Операции «Отменить», «Вырезать», «Копировать», «Вставить»
Данные пункты меню автоматически вставляются в генерируемое по умолчанию меню.
Однако для того чтобы они работали, надо написать соответствующие функции. Функции Undo(),
Cut(), Copy() и Paste() являются членами класса CEdit, который отвечает за работы элемента Edit Box, поэтому написание функций для соответствующих пунктов меню не представляет особого
труда.
- Откройте ClassWizard. Перейдите на вкладку Message Maps. В поле Class Name
выберите CMainFrame, в поле Object IDs – ID_EDIT_UNDO, в поле Messages –
COMMAND. Добавьте соответствующую функцию и отредактируйте её код:
void CMainFrame::OnEditUndo()
{ CWnd *wnd;
char className[6];
// TODO: Add your command handler code here
wnd = GetFocus();
// Получаем указатель на текущее окно
if (wnd && wnd->m_hWnd &&
::GetClassName(wnd->m_hWnd, className, 6) &&
// Проверяем, что это
stricmp(className, "Edit") == 0)
// элемент Edit Box
((CEdit *)wnd)->Undo();
// Отменяем последнее действие
}
- Аналогично напишите функции для остальных действий.
Теперь надо написать функции, которые отключают и соответствующие пункты меню.
- Для каждого пункта меню «Отменить», «Вырезать», «Копировать», «Вставить» добавьте
функцию для обработки сообщения UPDATE_COMMAND_UI (см. пункт 44).
- Отредактируйте функцию для пункта меню «Отменить»:
void CMainFrame::OnUpdateEditUndo(CCmdUI* pCmdUI)
{ CWnd *wnd;
char className[6];
// TODO: Add your command update UI handler code here
wnd = GetFocus();
if (!wnd || !wnd->m_hWnd)
{ pCmdUI->Enable(0);
return;
}
if (::GetClassName(wnd->m_hWnd, className, 6) &&
stricmp(className, "Edit") == 0
&&
((CEdit *)wnd)->CanUndo())
// Функция CanUndo проверяет, можно ли
pCmdUI->Enable(1);
// отменить последнюю операцию
else
pCmdUI->Enable(0);
}
- Отредактируйте функцию для пунктов меню «Вырезать» и «Копировать» (они одинаковы):
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{ CWnd *wnd;
char className[6];
int start, end;
// TODO: Add your command update UI handler code here
wnd = GetFocus();
if (!wnd || !wnd->m_hWnd)
{ pCmdUI->Enable(0);
return;
}
if (::GetClassName(wnd->m_hWnd, className, 6) &&
stricmp(className, "Edit") == 0)
{ ((CEdit *)wnd)->GetSel(start,
end);
// Проверяем, что есть
pCmdUI->Enable(start != end);
// выделенный текст
}
else
pCmdUI->Enable(0);
}
- Отредактируйте функцию для пункта меню «Вставить»:
void CMainFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)
{ CWnd *wnd;
char className[6];
// TODO: Add your command update UI handler code here
wnd = GetFocus();
if (!wnd || !wnd->m_hWnd)
{ pCmdUI->Enable(0);
return;
}
if (::GetClassName(wnd->m_hWnd, className, 6) &&
stricmp(className, "Edit") == 0 &&
!(wnd->GetStyle() & ES_READONLY))
// Функция IsClipboardFormatAvailable проверяет,
// что в буфере находится текст,
// который можно вставить в элемент Edit Box
pCmdUI->Enable(::IsClipboardFormatAvailable(CF_TEXT));
else
pCmdUI->Enable(0);
}
Файл в формате Word
См. также «Использование Visual C++. Часть 2»
Содержание