Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
// Сохранить копию таблицы цветов.
// Сохранить маску.
for (every color in the color table) {
if (color == rgbTransparent) color = white;
else color = black;
}
// Подготовить приемник с помощью переноса маски.
StretchDIBits(hdcDest, lpDIB, SRCAND);
// (Да, там есть еще параметры)
// Теперь подготовим "зачерненный" источник для маскированного переноса.
for (every color in the color table) {
if (color == white) // (мы его изменяли ранее)
color = black;
else color = original color from color table;
}
// Выведем приемник с эффектом прозрачности.
StretchDIBits(hdcDest, lpDIB, SRCPAINT); // (Да, там есть еще параметры)
// Восстановим первоначальную таблицу цветов.
Заметьте, что в данном способе требуется только одна копия растра – и для источника, и для маски прозрачности, так как используется преимущество в виде таблицы цветов. Однако остаются дополнительные расходы по преобразованию DIB в аппаратно-зависимый растр.
ВОПРОС – ОТВЕТ
Как обработать нажатие Enter в edit box'е?
Автор: Игорь Вартанов
Начнем с того, что для обработки нажатия Enter необходимо, чтобы (в общем случае) окно редактирования ожидало этого нажатия (т.е. имело стиль ES_MULTILINE). В противном случае система выполнит трансляцию этого нажатия в нажатие кнопки родительского окна, имеющей в текущий момент стиль BS_DEFAULTPUSHBUTTON. Кстати, это довольно неплохая методика для диалога, содержащего единственное окно ввода и имеющего кнопку по-умолчанию OK. Если же диалог (или окно) имеет несколько окон ввода, и логика работы приложения подразумевает, что нажатие Enter означает окончание ввода в выбранном окне и перевод фокуса на следующее, то скорее всего вам подойдет нижеследующая методика.
Основной вариантДемонстрационный проект EditDlg
WinAPIПРИМЕЧАНИЕ
Обратите внимание, окно редактирования должно иметь стиль ES_MULTILINE.
Основная идея состоит в подмене стандартной процедуры окна редактирования (т.н. subclassing) при инициализации окна диалога, и выполнение в новой процедуре обработки нажатия клавиши. В нашем примере при обнаружении нажатия Enter выполняется копирование текста окна в буфер текста и перевод фокуса на следующий контрол диалогового окна. Если же была нажата иная клавиша, выполняется вызов стандартной оконной процедуры для окон класса "edit".
#include <windows.h>
#include "resource.h"
WNDPROC oldEditProc = NULL;
LRESULT CALLBACK newEditProc(HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_KEYDOWN:
{
if (VK_RETURN == wParam) {
HWND hParent = GetParent(hEdit);
SendMessage(hParent, msg, wParam, lParam);
SetFocus(GetNextDlgTabItem(hParent, hEdit, FALSE));
return 0; // запрет обработки по-умолчанию
}
}
break;
case WM_CHAR:
if (VK_RETURN == wParam) return 0; // запрет обработки по-умолчанию
break;
}
return CallWindowProc(oldEditProc, hEdit, msg, wParam, lParam);
}
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
static char m_edText[256] = "";
switch (msg) {
case WM_INITDIALOG:
oldEditProc = (WNDPROC) SetWindowLong(
GetDlgItem(hDlg, IDC_EDIT1), GWL_WNDPROC, (LONG)newEditProc);
break;
case WM_COMMAND:
if (wParam == IDCANCEL) EndDialog(hDlg, 0);
break;
case WM_KEYDOWN:
if (VK_RETURN == wParam)
GetDlgItemText(hDlg, IDC_EDIT1, m_edText, 256);
break;
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBox(hInstance, "MAINDLG", HWND_DESKTOP, (DLGPROC)DlgProc);
return 0;
}
Обратите внимание на то, что обработчики сообщений при обнаружении нажатия Enter возвращают из оконной процедуры нуль. Это делается для того, чтобы сообщения не передавались обработчику по-умолчанию (и, следовательно, не выполнялось нажатие кнопки по-умолчанию).
MFCПРИМЕЧАНИЕ
Обратите внимание, окно редактирования должно иметь стиль ES_MULTILINE.
Для реализации поведения приложения, аналогичного только что описанному, необходимо создать класс, производный от CEdit, имеющий собственные обработчики сообщений WM_KEYDOWN и WM_CHAR (при создании класса и добавлении обработчиков используйте ClassWizard).
// .h-файл класса ////////////////////////////////////////////////
...
class CEnterEdit : public CEdit {
public:
CEnterEdit();
public:
virtual ~CEnterEdit();
protected:
//{{AFX_MSG(CEnterEdit)
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
// .cpp-файл класса //////////////////////////////////////////////
...
BEGIN_MESSAGE_MAP(CEnterEdit, CEdit)
//{{AFX_MSG_MAP(CEnterEdit)
ON_WM_KEYDOWN()
ON_WM_CHAR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CEnterEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
if (nChar == VK_RETURN) {
// Предполагаем, что родительское окно эдит-бокса -
// диалог класса CEditDlgDlg, который имеет буфер хранения
// введенного текста m_edText типа CString.
CEditDlgDlg* pDlg = (CEditDlgDlg*) GetParent();
GetWindowText(pDlg->m_edText);
pDlg->GetNextDlgTabItem(this)->SetFocus();
return; // запрет обработки по-умолчанию
}
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CEnterEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
if (nChar == VK_RETURN) return; // запрет обработки по-умолчанию
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
ПРИМЕЧАНИЕ
Подмена оконной процедуры – универсальный метод для получения необходимой функциональности. Если же есть возможность получить доступ к циклу сообщений, то можно воспользоваться альтернативной методикой – обработкой сообщения WM_KEYDOWN в самом цикле (см. далее – Альтернативный вариант).
Пример EditDlg демонстрирует обработку нажатия клавиши Enter. Он содержит два проекта – WinAPI и MFC.
Альтернативный вариантНе всегда целесообразно обработку нажатия Enter возлагать на окно редактирования. Если в поведение приложения необходимо добавить указанную реакцию, но для самого окна достаточно обычной функциональности (однострочное окно редактирования), можно, не меняя стиля окна редактирования, самостоятельно обрабатывать нажатие Enter, анализируя содержимое сообщений в цикле обработки сообщений.
Необходимо помнить, что цикл обработки сообщений модального диалога реализуется самой системой и недоступен для программиста. В этом случае остается единственное средство – подмена оконной процедуры окна редактирования, описанная выше (см. Основной вариант).
Детали реализации этого метода очень сильно зависят от постановки задачи, среды разработки и организации цикла обработки сообщений. Общая схема такова:
1. До выполнения DispacthMessage(&msg) необходимо проанализировать поле msg.message на приход сообщения WM_KEYDOWN.
2. Если получено сообщение WM_KEYDOWN, и поле msg.wParam содержит VK_RETURN, то выполнить вызов функции-диспетчера нажатия enter. При этом обычно необходимо избегать передачи полученного сообщения в функцию DispatchMessage(), чтобы не выполнялась обработка по-умолчанию.
3. Для всех иных сообщений выполнить стандартную обработку.
MFCДля программ, использующих MFC, все необходимые проверки выполняются в методе PreTranslateMessage() класса приложения или окна.
BOOL CMyWinApp::PreTranslateMessage(MSG* pMsg) {
if ((WM_KEYDOWN == pMsg->message) && (VK_RETURN == pMsg->wParam)) {
OnEnterPressed(); // вызов диспетчера нажатия Enter
return TRUE; // запрет дальнейшей обработки
}
// стандартная обработка сообщения
return CWinApp::PreTranslateMessage(pMsg);
}
WinAPIДля приложений WinAPI реализация цикла обработки сообщений может выглядеть таким образом:
...
while (GetMessage(&msg, NULL, 0, 0)) {
if ((WM_KEYDOWN == pMsg->message) && (VK_RETURN == pMsg->wParam)) {
OnEnterPressed(); // вызов диспетчера нажатия Enter
continue; // запрет дальнейшей обработки
}
// стандартная обработка сообщения
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
В функции OnEnterPressed() вы можете анализировать, которое из окон ввода в момент нажатия имеет фокус, и в зависимости от этого принимать решение о выполнении необходимых действий, обеспечивающих логику работы приложения.