Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 7:

Меню в приложениях MFC

Создание контекстных меню

Контекстное меню отображается в том случае, когда пользователь, выбрав объект, щелкает на нем правой кнопкой мыши. Именно за правой кнопкой мыши Microsoft рекомендует закреплять вызов контекстного меню. При клавиатурном вызове для новых 104-клавишных клавиатур типа Microsoft Natural keyboard контекстное меню вызывается закрепленной за этим клавишей " Applications " (на этой клавише, расположенной внизу перед правой клавишей Ctrl, изображен курсор мыши поверх открытого контекстного меню). На случай, если у пользователя не окажется такой клавиши, клавиатурный вызов контекстного меню следует определить акселератором " hift+F10 ".

Обработчик для вызова контекстного меню можно закрепить за любым из двух сообщений Windows:

  1. WM_RBUTTONDOWN - генерируется системой при нажатии правой кнопки мыши.
  2. WM_CONTEXTMENU - генерируется системой при отпускании правой кнопки мыши или нажатии клавиатурной кнопки " Applications " вызова контекстного меню, которая есть на новых клавиатурах Windows.

Контекстные меню могут ссылаться на раскрывающиеся списки опций существующего меню или создаваться динамически, загружая нужный ресурс из заранее созданных меню.

Построение областей чувствительности

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

  • Чтобы все шло согласованно, установите размер главного диалогового окна приложения 320х210 пикселов

Создадим функцию, которая нарисует на форме один цветной прямоугольник, имитирующий контекстную зону. Для этого:

  • Выделите в панели Class View класс CMenuDlg и в его контекстном меню (или главном меню оболочки Project ) выполните опцию Add Function. Появится мастер, который нужно заполнить так

Для окончательного ввода аргументов не забывайте каждый раз нажимать кнопку Add мастера.

Мастер создаст объявление и пустую заготовку определения нашей функции

Объявление функции ShowRectangle()
// CMenuDlg dialog
class CMenuDlg : public CDialog
{
....................................
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedExit();
	afx_msg void OnFileHello();
	afx_msg void OnHelpAbout();
	afx_msg void OnFileExit();
private:
	// Рисуем прямоугольник на контексте
	void ShowRectangle(int * color, CRect rect);
};
Заготовка определения функции ShowRectangle()

// Рисуем прямоугольник на контексте
void CMenuDlg::ShowRectangle(int * color, CRect rect)
{
}

Через аргументы функции будем передавать цвет кисти для заливки прямоугольника и координаты места рисования прямоугольника на контексте формы.

  • Заполните код функции
    Функция ShowRectangle()
    // Рисуем прямоугольник на контексте
    void CMenuDlg::ShowRectangle(int * color, CRect rect)
    {
    
    	// для мнемонического смысла
    	const int RED = color[0], GREEN = color[1], BLUE = color[2];  
    	
    	// Содать контекст для текущего окна
    	CClientDC dc(this); 
    	
    	// Создать новое перо
    	CPen MyNewPen; 
    	MyNewPen.CreatePen(PS_SOLID, 1, RGB(RED, GREEN, BLUE));
    	CPen *pOriginalPen = dc.SelectObject(&MyNewPen);
    	
    	// Новая кисть
    	CBrush brush(HS_DIAGCROSS, RGB(RED, GREEN, BLUE)); 
    	dc.SelectObject(&brush);
    	
    	// Нарисовать прямоугольник
    	dc.Rectangle(&rect);
    	
    	// Вернуть установку первоначального пера
    	dc.SelectObject(pOriginalPen);
    	
    	// Отсоединить кисть
    	dc.SelectStockObject(GRAY_BRUSH);
    }
  • Создайте вручную без использования мастера массив из четырeх переменных класса CRect для хранения координат прямоугольников

Они нам понадобятся в обработчике сообщения WM_CONTEXTMENU для того, чтобы определять, в область какого прямоугольника мы попали и в зависимости от этого вызывать соответствующее меню.

  • Поместите создание массива в описание класса CMenuDlg в раздел private, чтобы он был виден во всех функциях-членах класса

    Получится код

    Массив для хранения координат прямоугольников
    class CMenuDlg : public CDialog
    {
    .............................................
    private:
    	// Рисуем прямоугольник на контексте
    	void ShowRectangle(int * color, CRect rect);
    	// Переменные для хранения координат прямоугольников
    	// в порядке слева-направо сверху-вниз
        CRect rectCoord[4];
    };
  • Создайте еще одну функцию, которая будет рисовать четыре разноцветных прямоугольника, имитирующих разные зоны чувствительности экрана

  • Заполните код функции
    Код функции рисования всех прямоугольников
    // Рисует все прямоугольники
    void CMenuDlg::ShowAll(void)
    {
    	// Рисуем прямоугольники
    	const int BORDER = 8, PODVAL = 50; // Отступ от краев в пикселах
    	CRect clientRect, currentRect;
    	// Определяем координаты клиентской области главного окна
    	GetClientRect(&clientRect);
    	int width = (clientRect.Width() - 2 * BORDER) / 2; // ширина прямоугольника
    	int height = (clientRect.Height() - BORDER - PODVAL) / 2; // высота
    	int x1, y1; // Левый верний угол
    	int x2, y2; // Правый нижний угол
    	x1 = y1 = BORDER; x2 = x1 + width; y2 = y1 + height;
    	currentRect.SetRect(x1, y1, x2, y2);
    	rectCoord[0] = currentRect;// Сохраним координаты первого прямоугольника
    	int color[] = {255, 0, 0}; // Цвет первого прямоугольника
    	ShowRectangle(color, currentRect); // рисуем первый прямоугольник
    	
    	x1 = x2+1; x2 = x1 + width; // y1, y2 - прежние
    	currentRect.SetRect(x1, y1, x2, y2);
    	rectCoord[1] = currentRect; // Сохраним координаты второго прямоугольника
    	color[0] = 0; color[1] = 255; // color[2] - прежний
    	ShowRectangle(color, currentRect); // рисуем второй прямоугольник
    	
    	x1 = BORDER; x2 = x1 + width; y1 = y2+1; y2 = y1 + height;
    	currentRect.SetRect(x1, y1, x2, y2);
    	rectCoord[2] = currentRect; // Сохраним координаты третьего прямоугольника
    	color[1] = 0; color[2] = 255; // color[0] - прежний
    	ShowRectangle(color, currentRect); // рисуем третий прямоугольник
    	
    	x1 = x2+1; x2 = x1 + width;  // y1, y2 - прежние
    	currentRect.SetRect(x1, y1, x2, y2);
    	rectCoord[3] = currentRect;// Сохраним координаты четвертого прямоугольника
    	color[2] = 0; // color[0], color[1] - прежние
    	ShowRectangle(color, currentRect); // рисуем четвертый прямоугольник
    }
  • Поместите вызов функции в автоматически вызываемую функцию перерисовки экрана, которая является обработчиком сообщения WM_PAINT операционной системы

    Это сообщение посылается системой активному приложению всякий раз, когда экран разрушается и требует перерисовки.

    Вызов функции перерисовки прямоугольников в обработчике OnPaint()
    void CMenuDlg::OnPaint() 
    {
    	ShowAll(); // рисовать прямоугольники
    	
    	if (IsIconic())
    	{
    		CPaintDC dc(this); // device context for painting
    
    		SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);
    .................................
    	}
    	else
    	{ 
    		CDialog::OnPaint();
    	}
    }
  • Запустите приложение и должно получиться примерно следующее

Отладочный обработчик сообщения WM_CONTEXTMENU

Создадим обработчик сообщения WM_CONTEXTMENU. Для этого:

  • Выделите класс диалогового окна, которое будет принимать и обрабатывать это сообщение

    В нашем случае это главное диалоговое окно приложения, имеющее ресурс IDD_MENU_DIALOG и поддерживающий этот ресурс класс CMenuDlg.

  • В панели Properties установите режим Messages и найдите интересующее нас сообщение WM_CONTEXTMENU. Через находящееся справа поле добавьте обработчик

    Оболочка сгенерирует код в нескольких местах приложения

    Объявление обработчика в классе CMenuDlg
    class CMenuDlg : public CDialog
    {
    ....................................................
        CRect rectCoord[4];
    	// Рисует все прямоугольники
    	void ShowAll(void);
    public:
    	afx_msg void OnContextMenu(CWnd* /*pWnd*/, CPoint /*point*/);
    };
    Заготовка определения обработчика в классе CMenuDlg
    void CMenuDlg::OnContextMenu(CWnd* /*pWnd*/, CPoint /*point*/)
    {
    }
    Регистрация сообщения в таблице сообщений класса CMenuDlg
    BEGIN_MESSAGE_MAP(CMenuDlg, CDialog)
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	//}}AFX_MSG_MAP
    	ON_BN_CLICKED(IDC_EXIT, OnBnClickedExit)
    	ON_COMMAND(ID_FILE_HELLO, OnFileHello)
    	ON_COMMAND(ID_HELP_ABOUT, OnHelpAbout)
    	ON_COMMAND(ID_FILE_EXIT, OnFileExit)
    	ON_WM_CONTEXTMENU()
    END_MESSAGE_MAP()

Наша задача на данном этапе - организовать и проверить в принципе работоспособность обработчика. А дальше мы настроим его на вызов контекстных меню. Для этого:

  • Разблокируйте аргументы и заполните обработчик отладочным кодом
    Отладочный код обработчика сообщения WM_CONTEXTMENU
    void CMenuDlg::OnContextMenu(CWnd* pWnd, CPoint point)
    {
    	CPoint myPoint = point; // сохраним, чтобы не портить основные
    	ScreenToClient(&myPoint);	// переведем экранные координаты
    								// курсора в клиентские для окна
    	int numRect = 0; // Номер прямоугольника
    	for(int i = 0; i < 4; i++){
    		if(rectCoord[i].PtInRect(myPoint)){
    			// Точка внутри прямоугольника
    			numRect = i + 1;
    			break;
    		}
    	}
    	
    	if(numRect){
    		char str[2]; // Для цифра и '\0'
    		itoa(numRect, str, 10);  // Преобразование цифры в строку
    		CString string = "Попали в прямоугольник ";
    		string += str;
    		MessageBox(string);
    	}
    	else{
    		MessageBeep((WORD) - 1); // Звукнуть
    		MessageBox("Никуда не попали");
    	}
    }
  • Постройте приложение, пощелкайте правой кнопкой мыши и убедитесь, что все работает как задумано

    Программа всегда должна работать как задумано. Если она работает хуже, чем задумано, - это плохая программа; если она работает лучше, чем задумано, - это еще более плохая программа, она просто опасна (как улыбающийся тайный враг!).

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

Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .