Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
289-320.doc
Скачиваний:
2
Добавлен:
11.11.2019
Размер:
301.57 Кб
Скачать

321

са меню, выбрав в окне Resource View меню IDR_MAINFRAME. Удалите меню File и все команды в меню Edit, но само пустое меню сохраните. Теперь добавьте в меню Edit команду с надписью &Clear и идентификатором ID_EDIT_CLEAR , разделитель и команду с надписью E&xit с идентификатором ID_APP_EXIT. Завершенное меню Edit показано на рис. 9.3.

Рис. 9.3. Редактирование меню Edit программы Echo

Рис. 9.4. Значок в программе Echo, отображенный в графическом редакторе

  1. Измените значок IDR_MAINFRAME программы Echo в графическом редакторе (рис. 9.4). Программа содержит стандартное изображение размером 3232 пиксела. Сначала удалите все типы изображений по команде ImageDelete Image Type. Затем создайте новый тип изображения с помощью команды ImageNew Image Type…. Выберите тип изображения 3232, 256 colors. Создайте такое изображение, как на рис. 9.4. Сохраните ресурсы по команде FileSave All.

  2. Определите функции для обработки сообщений WM_CHAR, посылаемых при каждом вводе символа. Для определения обработчиков сообщения в окне мастера Class View выберите класс представления CEchoView, объект которого будет получать сообщения. Затем в окне Properties щелкните на кнопке Messages панели инструментов. В списке идентификаторов сообщений выберите WM_CHAR и выполните команду <Add> OnChar. В окне редактора кода появится текст файла EchoView.cpp, и курсор выделит заголовок добавленной функции.

  3. Сгенерируйте обработчик командного сообщения для выполнения команды меню EditClear. Для этого откройте редактор модифицированного ресурса меню. Выделив команду Clear в меню Edit, вызовите контекстное меню (рис. 9.5) и выполните команду Add Event Handler….

Рис. 9.5. Контекстное меню для вызова мастера создания обработчика командного сообщения

  1. В окне Event Handler Wizard выберите только имя класса CEchoView, членом которого будет создаваемая функция-обработчик. Все остальные значения полей мастер определил самостоятельно. Нажмите кнопку Add and Edit. В файл EchoView.h будут добавлены объявления функций:

public: afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnEditClear();

  1. В файл EchoView.cpp добавится макрос ON_COMMAND() и шаблоны функций:

BEGIN_MESSAGE_MAP(CEchoView, CView) ON_WM_CHAR() ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) END_MESSAGE_MAP()

  1. Добавьте в класс CEchoDoc определение переменной, в которой будет сохраняться строки текста. Для этого вызовите мастер Add Member Variable Wizard, в поле Variable type введите тип CString, в поле Variable name — m_TextLine.. В класс CEchoDoc добавится объявление переменной:

public: CString m_TextLine;

  1. Добавьте код в функцию CEchoView::OnChar() обработки сообщения WM_CHAR. Эта функция получает управление при каждом нажатии символьной клавиши, а параметр nChar содержит код ANSI символа. В функции должны быть запрограммированы следующие действия: получение указателя на объект документа; добавление нового символа в конец строки; создание объекта контекста устройства; установка цвета текста и фонового режима TRANSPARENT; вывод текста. Для установки цвета текста используется стандартный цвет, получаемый передачей значения COLOR_WINDOWTEXT в функцию ::GetSysColor(). Так как функция CEchoView::OnChar() не устанавливает шрифт текста, символы отображаются с использованием стандартного системного шрифта. Для добавления кода откройте файл EchoView.cpp и введите следующие операторы вызовов функций:

void CEchoView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default CEchoDoc* PDoc = GetDocument(); PDoc->m_TextLine.AppendChar(nChar); CClientDC ClientDC(this); ClientDC.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); ClientDC.SetBkMode(TRANSPARENT); ClientDC.TextOut(0, 0, PDoc->m_TextLine); CView::OnChar(nChar, nRepCnt, nFlags); }

  1. Если вы запустите программу, то при вводе символов увидите, что нажатие управляющих клавиш (Enter, Tab, Esc) приводит к отображению в окне представления некоторых символов (рис. 9.6). Появление этих символов необходимо исключить. Для этого следует проверить генерируемый код символа, и, если его значение меньше 32, функция CEchoView::OnChar() подаст звуковой сигнал и завершит свою работу. Следующий ниже код, выделенный полужирным шрифтом, содержит оператор, который необходимо добавить. Кроме того, в этом фрагменте добавление символа в конец строки реализовано через перегруженный оператор «+=» с преобразованием типов (тип UINT преобразован в тип CString).

Рис. 9.6. Окно программы Echo в процессе ввода текста

void CEchoView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default if (nChar < 32) { ::MessageBeep(MB_OK); //звонок return; //конец работы функции } //демонстрация ввода символов при нажатии управляющих клавиш CEchoDoc* PDoc = GetDocument(); //PDoc->m_TextLine.AppendChar(nChar); CString str; //альтернативные AppendChar() операторы str.Format("%c",nChar); PDoc->m_TextLine +=str; CClientDC ClientDC(this); ClientDC.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); ClientDC.SetBkMode(TRANSPARENT); ClientDC.TextOut(0, 0, PDoc->m_TextLine); CView::OnChar(nChar, nRepCnt, nFlags); }

Предупреждение Если окно представления поддерживает средства прокрутки, т.е. порождается от класса CScrollView, то перед отображением текста или графики необходимо передать созданный объект контекста устройства CClientDC в функцию CView::OnPrepareDC(). Эта функция согласует объект контекста устройства с текущей позицией прокрутки документа таким образом, чтобы выводимые символы появились в правильных позициях. Для объекта контекста устройства, переданного функции CEchoView::OnDraw(), функцию OnPrepareDC() вызывать не нужно.

  1. Для поддержки взаимодействия объектов документа и представления при обмене данными реализуйте отображение строки символов с помощью функции CEchoView::OnDraw(). С этой целью добавьте представленный ниже полужирным шрифтом код в функцию CEchoView::OnDraw() в файле EchoView.cpp.

void CEchoView::OnDraw(CDC* pDC) { CEchoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); pDC->SetBkMode(TRANSPARENT); pDC->TextOut(0, 0, pDoc->m_TextLine); }

  1. Если вы реализуете отображение строки символов в окне представления с помощью функции CEchoView::OnDraw(), то перед вызовом метода CView::OnChar() базового класса добавьте в функцию CEchoView::OnChar() оператор вызова функции UpdateAllViews() класса документа. Функция CEchoView::OnDraw() не будет вызываться, если область окна представления не будет объявлена недействительной, что выполняется в результате вызова функции UpdateAllViews() класса документа. Вызов функции CDC::TextOut() для отображения текста из обработчика CEchoView::OnChar() становится не нужным. Закомментируйте этот оператор. Таким образом, в программе Echo будут реализованы оба варианта отображения символов. Вам остается выбрать один из них. Изменения в коде представлены ниже полужирным шрифтом.

void CEchoView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default if (nChar < 32) { ::MessageBeep(MB_OK); //звонок return; //конец работы функции } //демонстрация ввода символов при нажатии управляющих клавиш CEchoDoc* PDoc = GetDocument(); //PDoc->m_TextLine.AppendChar(nChar); CString str; //альтернативные AppendChar() операторы str.Format("%c",nChar); PDoc->m_TextLine +=str; CClientDC ClientDC(this); ClientDC.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); ClientDC.SetBkMode(TRANSPARENT); // ClientDC.TextOut(0, 0, PDoc->m_TextLine); pDoc->UpdateAllViews(NULL); CView::OnChar(nChar, nRepCnt, nFlags); }

  1. Реализуйте обработчик команды EditClear. Для этого в функцию CEchoView::OnEditClear() добавьте код вызова функции CString::Empty(), которая удаляет символы, хранимые в переменной m_TextLine и устанавливает длину строки в 0. Затем вызывается функция CDocument::UpdateAllViews(), очищающая окно представления.

void CEchoView::OnEditClear() { // TODO: Add your command handler code here CEchoDoc* pDoc = GetDocument(); pDoc->m_TextLine.Empty(); pDoc->UpdateAllViews(NULL); }

  1. Теперь можно построить и выполнить программу Echo. В процессе работы программы попробуйте ввести строки текста.

Оставьте проект Echo открытым в Developer Studio, потому что работа с этой программой еще не окончена. При выполнении программы Echo вы, вероятно, заметили, что не хватает мигающего курсора, отмечающего место вставки символов в текст. Технология добавление программного кода, управляющего работой курсора, рассматривается в упражнении 8. Но предварительно познакомимся с назначением функций, которые будут использоваться для управления курсором.

Управление курсором при редактировании текста

Первое сообщение, которое получает оконная процедура созданного класса окна, это WM_CREATE. Это сообщение посылается функцией ::CreateWindow() перед тем, как окно станет видимым, до завершения работы этой функции. Параметр этого сообщения определяет указатель на структуру CREATESTRUCT, которая содержит информацию для инициализации создаваемого объекта окна. Состав полей структуры CREATESTRUCT таков:

typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; //данные для создания окна HINSTANCE hInstance; //дескриптор приложения HMENU hMenu; //дескриптор меню HWND hwndParent; //указатель на родительское окно int cy; //высота окна int cx; //ширина окна int y; //координата верхнего угла окна int x; //координата левого угла окна LONG style; //стиль LPCTSTR lpszName; //указатель на имя окна LPCTSTR lpszClass; //указатель на имя класса окна DWORD dwExStyle; //расширенный стиль окна } CREATESTRUCT;

В ответ на сообщение WM_CREATE приложение может выполнить необходимые функции инициализации перед отображением окна. Это сообщение обрабатывается обработчиком сообщений CWnd::OnCreate(). Обычно в этой функции инициализируют панель инструментов, строку состояния и другие объекты, которые отображаются вместе с окном. В частности, курсор должен появиться, как только откроется окно приложения, значит, его нужно инициализировать при поступлении сообщения WM_CREATE. Прототип функции CWnd::OnCreate() показан ниже:

afx_msg int CWnd::OnCreate( LPCREATESTRUCT lpCreateStruct );

Функция принимает в качестве аргумента указатель на структуру CREATESTRUCT, назначение которой рассмотрено выше. Возвращаемое значение, равное 0, означает продолжение процесса создания объекта CWnd, а значение, равное -1, разрушает окно.

В нашем проекте Echo для создания мигающего курсора вставки символов (caret) необходимо вычислить и сохранить его размер. Вместо установки некоторой произвольной ширины для отображения мигающего курсора можно рассчитать его ширину по текущей ширине рамки окна так, чтобы ширина курсора масштабировалась в соответствии с размерами других стандартных элементов окна независимо от видеорежима. Высоту курсора устанавливают равной высоте символов в стандартном системном шрифте, используемом для отображения текста. Для определения высоты курсора функция CEchoView::OnCreate() должна создать объект контекста устройства и вызвать функцию CDC::GetTextMetrics(). Для определения ширины курсора функция CEchoView::OnCreate() получает ширину рамки окна, вызывая функцию ::GetSystemMetrics() с аргументом SM_CXBORDER. Напомним прототип функции:

int GetSystemMetrics( int nIndex );

Функция возвращает требуемую системную метрику или конфигурационную установку. Параметр функции может принимать значения, которые приведены в табл. В6 приложения В

Когда окно представления получает фокус ввода, в частности, при первоначальном создании окна представления и при каждом переключении на программу Echo после работы в другой программе, то ему посылается сообщение WM_SETFOCUS. Параметром сообщения является дескриптор окна, потерявшего фокус. В ответ на это сообщение вызывается функция CWnd::OnSetFocus(), которой передается указатель на объект CWnd, потерявший фокус ввода. Функция имеет такой прототип:

afx_msg void CWnd::OnSetFocus( CWnd* pOldWnd );

Когда окно представления создано, функция CWnd::OnSetFocus() вызывается после функции CWnd::OnCreate(). Следовательно, можно использовать размеры курсора, установленные функцией CWnd::OnCreate().

Для создания курсора можно использовать функцию CWnd::CreateSolidCaret(). Курсор, используемый для указания места вставки символа, имеет форму линии или сплошного прямоугольника. Поэтому функции CWnd::CreateSolidCaret() передается значения ширины (nWidth) и высоты (nHeight) курсора в логических единицах. Функция объявлена таким образом:

void CWnd::CreateSolidCaret( int nWidth, int nHeight );

Функция CWnd::CreateSolidCaret() автоматически уничтожает предыдущую форму курсора вставки символа, независимо от того, какое окно владеет этим курсором. Обратите внимание на то, что курсор вставки можно создать только тогда, когда окно становится активным и получает фокус ввода. Однако созданный курсор является скрытым.

После запуска программы и появления окна на экране в области представления уже должен находиться курсор. Обычно он помещается в левый верхний угол окна представления. Чтобы поместить курсор в нужную позицию можно вызвать функцию CWnd::SetCaretPos(), которой передаются координаты позиции, в которую надо переместить курсор вставки. Прототип функции объявлен в классе CWnd:

static void PASCAL CWnd::SetCaretPos( POINT point );

Напомним, что тип POINT представляет такую структуру:

typedef struct tagPOINT { LONG x; //координата x точки LONG y; //координата y точки } POINT;

Так как вновь созданный курсор невидим, необходимо его отобразить. Курсор начинает мигать, как только он отобразится на экране с помощью функции CWnd::ShowCaret(), прототип которой показан ниже.

void CWnd::ShowCaret( );

Чтобы скрыть курсор вызывают функцию CWnd::HideCaret(), которая объявлена, как показано ниже:

void CWnd::HideCaret();

Легко понять, почему курсор необходимо создавать до создания окна представления. Но почему же его необходимо создавать каждый раз при получении окном представления фокуса? Причина в том, что курсор удаляется всякий раз, когда окно представления теряет фокус. Перед потерей фокуса ввода окну посылается сообщение WM_KILLFOCUS. Параметром сообщения является дескриптор окна, теряющего фокус ввода. В ответ на это сообщение вызывается функция CWnd::OnKillFocus(), которой в качестве аргумента передается указатель на окно, получающее фокус ввода. Функция имеет такой прототип:

afx_msg void CWnd::OnKillFocus( CWnd* pNewWnd );

Функция CWnd::OnKillFocus() вызывается каждый раз, когда окно представления теряет фокус ввода. Если перед потерей фокуса ввода в окне представления отображался курсор вставки символов, то последний будет уничтожен. Для этого в функции CWnd::OnKillFocus() необходимо вызвать Win32 API функцию ::DestroyCaret(). Последняя функция возвращает ненулевое значение, если ее работа завершается успешно. В противном случае возвращается 0. Прототип функции представлен ниже:

BOOL DestroyCaret(VOID)

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

Для создания эффекта перемещения курсора необходимо выполнить такие действия.

  • Вставить новый символ в конец строки с помощью функции CSimpleStringT::AppendChar().

  • Вызвать функцию CWnd::HideCaret(), чтобы сделать курсор невидимым.

  • Вывести текст, используя функцию CDC::TextOut().

  • Определить новую длину строки с учетом добавленного символа, вызвав функцию CDC::GetTextExtent().

  • Переместить курсор в конец строки, т.е. в место вставки следующего символа, вызвав функцию CWnd::SetCaretPos().

  • Вызвать функцию CWnd::ShowCaret(), чтобы снова сделать курсор видимым. Это делается потому, что запись в окно при видимом курсоре может вызывать искажение экрана в позиции курсора.

Пользователь не должен скрывать курсор при рисовании функцией OnDraw() класса представления, потому что Windows автоматически скрывает его до вызова этой функции и восстанавливает после возврата из нее. По той же причине не нужно скрывать курсор в функции OnPaint() класса представления, которая рисует в окне, не являющемся окном представления. Поэтому функцию OnDraw() в нашей программе не нужно изменять.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]