Вернуться наверх
aco.ifmo.ru photonic
вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


5.7. Реализация стандартного набора команд

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

Стандартные команды, реализованные в библиотеке MFC имеют идентификаторы иза диапазона от 0xE000 и до 0xEFFF. Неодходимо следить за тем, чтобы идентификаторы нестандартных команд находились за пределами этого диапазона.

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

5.7.1 Пункт меню File

New, Open, Close, Save, Save As, Page Setup, Print Setup, Print, Print Preview, MRU Files List, Exit

Создание нового/пустого документа

Команда: File => New(...)
Идентификатор: ID_FILE_NEW

Обработка события ON_COMMAND, сгенерированного после выполнения этой команды, реализована в классе CWinApp. Действия, которые выполняет функция CWinApp::OnFileNew зависят от количества шаблонов документов созданных в данном приложении. Если создан только один экземпляр класса CDocTemplate, то CWinApp::OnFileNew создаст новый документ типа определенного шаблоном и выполненит необходимые действия с классом рамки и классом представлением. Если создан более чем один шаблон, то CWinApp::OnFileNew создаст и отобразит на экране диалоговое окно, определенное в ресурсах с идентификатором AFX_IDD_NEWTYPEDLG, в котором пользователю будет предложено выбрать тип создаваемого документа. После этого будет создан документ на основе выбранного шаблона.

Перехват этого события или перегрузка этой функции необходима в том случае, если требуется организовать более сложный выбор типа документа или предусмотрено его создание с помощью мастера. В этом случае определяется собственная функция CMyApp::OnFileNew. Вызова функции базового класса не требуется.

Другой способ - это определение отдельных команд для создания отдельных типов документов. В этом случае определяются новые нестандартные идентификаторы, например ID_FILE_NEW_CHART and ID_FILE_NEW_SHEET.

Открыть существующий документ

Команда: File => Open...
Идентификатор: ID_FILE_OPEN

Обработка события ON_COMMAND, сгенерированного после выполнения этой команды, также реализована в классе CWinApp. Функция CWinApp::OnFileOpen имеет очень простую реализацию. В ней вызывается функция CWinApp::DoPromptFileName которая предоставляет пользователю возможность выбрать путь к открываемому документу, а затем вызывается CWinApp::OpenDocumentFile, которой в качестве аргумента передается путь к выбранному файлу. Функция DoPromptFileName, реализованная в классе CWinApp создает и отображает на экране стандартное диалоговое оуно для выбора пути к файлу, заполняя его параметрами указанными в ресурсах текущего шаблона документа.

Изменение действий выполняемых по команде ID_FILE_OPEN бывает неоходимо, если приложение работает с документами одного итого же типа, но сохраненными в разных форматах или с разными расширениями (например, изображения). В этом случае в реализации функции CMyApp::OnFileOpen необходимо сконструировать стандартное диалоговое окно для выбора файлов, заполнив его необходимыми параметрами, а затем вызвать функцию CWinApp::OpenDocumentFile указав выбранный путь к файлу. Вызов функции базового класса не требуется.

Конструктор класса CFileDialog имеет следующий прототип:

CFileDialog(
    BOOL bOpenFileDialog, // TRUE - если конструируется Open диалог и FALSE - если конструируется диалог SaveAs
    LPCTSTR lpszDefExt = NULL, // Расширение файла по умолчанию, которое будет добавляться к имени,
 если пользователь не указал явно, а если равно NULL, то вообще не добавляется
    LPCTSTR lpszFileName = NULL, // Имя файла для инициализации
    DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, // флаги, определяющие режимы
    LPCTSTR lpszFilter = NULL, // Строка фильтров
    CWnd* pParentWnd = NULL ); //Указатель на родительское окно 
Перегрузка функции OnFileOpen может быть осуществлена следующим образом:
void CTestApp::OnFileOpen()
{
    CFileDialog dialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
        "Bitmap Files (*.bmp)|*.bmp|GIF Files (*.gif)|*.gif|JPEG Files (*.jpg)|*.jpg|All Files (*.*)|*.*||",
		NULL); 
    if(dialog.DoModal() == IDOK)
    {
        OpenDocumentFile(dialog.GetPathName());
    }
}

Закрыть открытый документ

Команда: File => Close
Идентификатор: ID_FILE_CLOSE

Реакция на событие реализуется функцией CDocument::OnFileClose, которая вызывает CDocument::SaveModified для вывода диалогового окна с вопросом о том, должен ли быть сохранен документ. Обычно перегрузка этой функции не требуется.

Сохранить открытый документ

Команда: File => Save
Идентификатор: ID_FILE_SAVE

Выполнение этой команды осуществляется с помощью функции CDocument::DoSave, которая вызывается и из OnFileSave и из OnFileSaveAs. Если сохраняемый документ еще ни разу не сохранялся или файл, в котором он сохранен имеет атрибут read-only, то OnFileSave выполняется так же как и команда ID_FILE_SAVE_AS, то есть с предварительным вызовом окна для выбора имени файла. Процесс открытия файла и записи в него данных осуществляется в виртуальной функции OnSaveDocument.

Сохранить открытый документ c новым именем

Команда: File => Save As...
Идентификатор: ID_FILE_SAVE_AS

Реализация CDocument::OnFileSaveAs также использует функцию CDocument::DoSave. Она предоставляет возможность пользователю выбрать путь и имя сохраняемого документа с помощью стандартного диалогового окна и затем вызывает OnSaveDocument с этим именем в качестве аргумента.

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

void CTestDoc::OnFileSaveAs()
{
    CFileDialog dialog(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, 
        "Bitmap Files (*.bmp)|*.bmp|GIF Files (*.gif)|*.gif|JPEG Files (*.jpg)|*.jpg||",
        NULL);
    if(dialog.DoModal() == IDOK)
    {
        OnSaveDocument(dialog.GetPathName());
	}
}

Определить параметры страницы для печати

Команда: File => Page Setup...
Идентификатор: ID_FILE_PAGE_SETUP

Классы MFC не предусматривают какую-либо обработку этой команды. Но предусмотрено стандартное диалоговое окно для выполнения установок параметров страницы. Поэтому в обработчике этой команды необходимо сконструировать экземпляр класса CPageSetupDialog отобразить его на экране с помощью функции DoModal().

После того как пользователь сделал установки и нажал кнопку OK, необходимо обратиться к переменной-члену этого класса m_psd, который представляет собой экземпляр структуры PAGESETUPDLG и хранит все сделанные установки. В зависимости от них формируется графическое представление наконтексте устройства принтера.

Определить параметры страницы для печати

Команда: File => Print Setup...
Идентификатор: ID_FILE_PAGE_SETUP

По этой команде должно быть отображено стандартное диалоговое окно для задания параметров принтера. Функция CWinApp::OnFilePrintSetup реализуется очень просто. Она создает экземляр объекта CPrintDialog.

После того как пользователь нажмет кнопку OK необходимо обратиться к его переменной-члену m_pd, который представляет собой экземляр структуры PRINTDLG и содержит всевозможные параметры.

Печать открытого документа

Команда: File => Print...
Идентификатор: ID_FILE_PRINT

По этой команде происходит печать открытого документа. Функция CView::OnFilePrint реализует стандартное выполнение этой команды. Она вызывает виртуальную функцию CView::OnPreparePrinting, которая предоставляет пользователю возможность поработать с диалоговым окном настройки принтера. Затем создает контекст устройства для вывода на принтер, создает и запускает диалоговое окно AFX_IDD_PRINTDLG с индикатором печати, и посылает команды драйверу принтера. Функция CView::OnFilePrint также содержит ориентированную на страницы "петлю", в процессе которой для каждой страницы вызывается виртуальная функция CView::OnPrepareDC, за которой следует служебная escape-последовательность для инициализации принтера и вызов виртувльной функции CView::OnPrint в которой и происходит отображение информации. Когда информация для печати закончилась, вызывается виртуальная функция CView::OnEndPrinting. Процесс печати завершается.

Предварительный просмотр перед печатью

Команда: File => Print Preview...
Идентификатор: ID_FILE_PRINT_PREVIEW

Реализуется функцией CView::OnFilePrintPreview, которая переключает окно в режим предварительного просмотра, а затем вызывает вспомогательную функцию CView::DoPrintPreview.

Список последних открытых файлов

Команда: File => Имя файла
Идентификатор: ID_FILE_MRU_FILE1...FILE16

Классы библиотеки MFC, позволяют формировать и отображаить в пункте меню File список последних редактированных фалов. Вмешиваться в этот механизм не рекомендуется.

Завершение работы с программой

Команда: File => Exit
Идентификатор: ID_APP_EXIT

Завершение работы с приложением реализуется функцией CWinApp::OnAppExit, которая посылает сообщение WM_CLOSE главному окну приложения. Изменение реакции на эту команду не рекомендуется.

5.7.2 Пункт меню Edit

Undo, Redo, Repeat, Cut, Copy, Paste, Clear, Clear All, Select All, Find, Replace.

Отменить последнее действие

Команда: Edit => Undo
Идентификатор: ID_EDIT_UNDO

В настоящее время не существует стандартной реализации механизма Undo. Такой механизм реализован только в классе CEditView.

Выполнить отмененное действие

Команда: Edit => Redo
Идентификатор: ID_EDIT_REDO

В настоящее время не существует стандартной реализации механизма Redo. Нет его даже в классе CEditView.

Повтор последней операции

Команда: Edit => Repeat
Идентификатор: ID_EDIT_REPEAT

В настоящее время не существует стандартной реализации повтора последней выполненной команды. Ее необходимо реализовать специально. CEditView имеет реализацию повтора последней операции.

Вырезать выделенный участок в буфер обмена

Команда: Edit => Cut
Идентификатор: ID_EDIT_CUT

Не существует стандартной реализации вырезания в буфер обмена. Ее необходимо реализовать специально. Такая операция реализована в классе CEditView, который позволяет поместить в буфер обмена информацию в формате CF_TEXT. Эта команда должна быть недоступна в том случае, если в документе нет выделения.

Скопировать выделенный участок в буфер обмена

Команда: Edit => Copy
Идентификатор: ID_EDIT_COPY

Не существует стандартной реализации вырезания в буфер обмена. Ее необходимо реализовать специально. Такая операция реализована в классе CEditView, который позволяет поместить в буфер обмена информацию в формате CF_TEXT. Эта команда должна быть недоступна в том случае, если в документе нет выделения.

При создании наукоёмких приложений результат вычислений чаще всего отображается в виде всевозможных графиков. В окнах отображаются и моделируемые объекты. Результаты вычислений необходимо вставлять в отчеты, статьи, доклады. Пакеты исследовательсих программ редко реализуются возможность печати результатов или сохранения в одном из стандартных форматов. Многие используют стандартную возможность - сохранение в буфере обмена растрового изображения окна. Недостатком такого способа является то, что изображение с лишними областями (заголовок, меню, панель инструментов, полосы прокрутки, рамка) приходится вырезать требуемую информацию в каком-либо графическом редакторе. Эти недостатки можно преодолеть, если реализовать сохранение изображения в буфере обмена в своей программе. А сделать это совсем не сложно.

Последовательность действий такова:

  1. Создание в памяти контекста устройства, совместимого с контекстом окна, осуществляется с помощью функции-члена CreateCompatibleDC класса CDC.
  2. Создание битового массива, совместимого с контекстом устройства в памяти, осуществляется с помощью функции-члена CreateCompatibleBitmap класса CBitmap.
  3. Выбор битового массива в контексте устройства в памяти.
  4. Копирование битового массива из контекста окна в контекст памяти c помощью функции-члена BitBlt класса CDC.
  5. Размещение битового массива в буфере обмена.
  6. Отсоединить битовый массив от контекста памяти.

Функция выполняющая все эти действия выглядит следующим образом:

void Copy(CWnd* wnd)
{
// Конструируется контекст устройства
    CClientDC dc(wnd);
// Создаётся контекст устройства в памяти
    CDC memDC;
    memDC.CreateCompatibleDC(&dc);

// Определяется размер окна
    CRect rect;
    wnd->GetClientRect(rect);

// Создаётся битовый массив
    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());

// Битовый массив выбирается в контексте устройства
    CBitmap* oldBitmap=memDC.SelectObject(&bitmap);

// Битовый массив копируется из одного контекста устройства в другой
    memDC.BitBlt(0, 0, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);

// Открывается буфер обмена
    OpenClipboard(wnd->GetSafeHwnd());
// Очищается буфер обмена
    EmptyClipboard();
// Битовый массив помещается в буфер обмена
    SetClipboardData(CF_BITMAP, bitmap.GetSafeHandle()); 
// Закрывается буфер обмена
    CloseClipboard();

// Исходный битовый массив возвращается контексту устройства
    memDC.SelectObject(oldBitmap);
// Битовый массив отсоединяется от контекста устройства
    bitmap.Detach();
}

Вставить содержимое буфера обмена в документ

Команда: Edit => Paste
Идентификатор: ID_EDIT_PASTE

Не существует стандартной реализации вставки из буфера обмена. Ее необходимо реализовать специально. Такая операция реализована в классе CEditView, который позволяет извлечь информацию из буфера обмена в формате CF_TEXT.

Очистить выделенный участок

Команда: Edit => Clear
Идентификатор: ID_EDIT_CLEAR

Не существует стандартной реализации удаления участка. Ее необходимо реализовать специально. Такая операция реализована в классе CEditView. Эта команда должна быть недоступна в том случае, если в документе нет выделения.

Очистить весь документ

Команда: Edit => Clear All
Идентификатор: ID_EDIT_CLEAR_ALL

Не существует стандартной реализации очистки документа. Ее необходимо реализовать специально.

Выделить все

Команда: Edit => Select All
Идентификатор: ID_EDIT_SELECT_ALL

Не существует стандартной реализации выделения всего документа. Ее необходимо реализовать специально. CEditView предоставляет реализацию этой команды, которая выделяет весь текст документа.

Поиск

Команда: Edit => Find
Идентификатор: ID_EDIT_FIND

Не существует стандартной реализации функции поиска. Ее необходимо реализовать специально. Но эта функция реализована для класса CEditView, в котором создается и вызывается стандартное диалоговое окно поиска и замены CFindReplaceDialog. Оно работает в модальном режиме и позволяет пользователям осуществлять поиск по введенному ключевому словосочетанию.

Замена

Команда: Edit => Replace
Идентификатор: ID_EDIT_REPLACE

Не существует стандартной реализации функции поиска и замены. Ее необходимо реализовать специально. Но эта функция реализована для класса CEditView, в котором создается и вызывается стандартное диалоговое окно поиска и замены CFindReplaceDialog. Оно работает в модальном режиме и позволяет пользователям осуществлять поиск по введенному ключевому словосочетанию и замену на введенное словосочетание.

5.7.3 Пункт меню View

Включение/выключение панели инструментов

Команда: View => Toolbar
Идентификатор: ID_VIEW_TOOLBAR

Обработка этой команды осуществляется классом CFrameWnd. Изменять реакцию на это событие не требуется

Включение/выключение строки подсказки

Команда: View => Status bar
Идентификатор: ID_VIEW_STATUS_BAR

Обработка этой команды осуществляется классом CFrameWnd. Изменять реакцию на это событие не требуется.

Переход к следующему представлению

Команда: View => Next
Идентификатор: ID_NEXT_PANE

Реализуется в классе CView при наличии разделителя CSplitterWnd. Для перехода к следующему представлению используется функция CSplitterWnd::OnNextPaneCmd.

Переход к предыдущему представлению

Команда: View => Previous
Идентификатор: ID_PREV_PANE

Реализуется в классе CView при наличии разделителя CSplitterWnd. Для перехода к предыдущему представлению используется функция CSplitterWnd::OnNextPaneCmd.

5.7.4 Пункт меню Window

Создать новое окно

Команда: Window => New
Идентификатор: ID_WINDOW_NEW

Создание нового окна реализуется фунцией CMDIFrameWnd::OnWindowNew. По умолчанию аналогична команде File =>New.

Выравнить размещение окон

Команда: Window => Arrange
Идентификатор: ID_WINDOW_ARRANGE

Реализуется в классе CMDIFrameWnd с помощъю функции OnMDIWindowCmd.

Каскадное расположение окон

Команда: Window => Cascade
Идентификатор: ID_WINDOW_CASCADE

Реализуется в классе CMDIFrameWnd с помощъю функции OnMDIWindowCmd.

Горизональное расположение окон

Команда: Window => Tile Horizontally
Идентификатор: ID_WINDOW_TILE_HORZ

Реализуется в классе CMDIFrameWnd с помощъю функции OnMDIWindowCmd.

Вертикальное расположение окон

Команда: Window => Tile Vertically
Идентификатор: ID_WINDOW_TILE_VERT

Реализуется в классе CMDIFrameWnd с помощъю функции OnMDIWindowCmd.

Создание разделителя окон

Команда: Window => Split
Идентификатор: ID_WINDOW_SPLIT

Реализуется в классе CView применением функции DoKeyboardSplit класса CSplitterWnd.

5.7.5 Пункт меню Help

Список ключевых слов

Команда: Help => Index...
Идентификатор: ID_HELP_INDEX

Реализуется функцией CWinApp::OnHelpIndex, в которой вызывается CWinApp::WinHelp.

Контекстно-зависимая подсказка

Команда: Help => F1
Идентификатор: ID_HELP

Реализуется функцией CWinApp::OnHelp.

О программе

Команда: Help => About
Идентификатор: ID_APP_ABOUT

Не существует стандартной реализации диалогового окна о программе, хотя AppWizard генерирует подобный класс. Если не предполагается организовывать сложное интерактивное взаимодействие с этим диалоговым окно, то его реализацию можно упростить:

void CTestApp::OnAppAbout()
{
CDialog aboutDlg(IDD_ABOUTBOX);
aboutDlg.DoModal();
}