Опубликован: 10.03.2009 | Уровень: специалист | Доступ: платный
Лекция 2:

Библиотека классов MFC

Продолжение программы. Создание меню

Задача. Создать меню со следующими элементами: Программа \to Выход; Движение \to Старт; Движение \to Стоп. Добавить обработку выбора пункта меню Программа \to Выход, остальные пункты пока не обрабатывать. Чтобы создать меню, необходимо создать файл ресурсов:

  • Resource View \to клик правой кнопкой мыши \to Add \to Resource…
  • Выберем тип ресурса Menu.
  • Создаем меню (в свойствах меню укажем имя идентификатора: IDR_MENU, в свойствах каждого из пунктов также укажем нужные имена идентификаторов).

Добавим код:

…
#include "afxext.h"    // MFC Расширения
#include "resource.h"    // Идентификаторы ресурсов
#define IDC_MYBUTTON 100  // Идентификатор кнопки
…
class CMainWnd : public CFrameWnd
{
  …
  int OnCreate(LPCREATESTRUCT lpCreateStruct);//функция вызывается при создании окна
  void MenuExit();    //Процедура реакции на выбор пункта меню
   ~CMainWnd();    //Деструктор 
private:
  CStatic* MyStatic;    //Указатель на объект надпись
  CMyButton* MyButton;    //Элемент управления кнопка
  CEdit* MyEdit;      //Указатель на объект поле редактирования
  CStatusBar m_wndStatusBar;  //Класс панели состояния
  CMenu m_wndMenu;      //Это наш класс Меню
  DECLARE_MESSAGE_MAP();    //Таблица сообщений
};

CMainWnd::CMainWnd()
{
  Create(NULL,"Окно приложения пользователя",WS_OVERLAPPEDWINDOW,rectDefault,NULL,NULL);  //Создать окно программы
  ON_WM_CREATE()          //Событие создания окна
  ON_COMMAND(ID_FILE_EXIT, MenuExit)    //Обработка реакции на выбор меню
END_MESSAGE_MAP()
  …
  if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
  m_wndStatusBar.Create(this); 
  m_wndMenu.LoadMenu(IDR_MENU);    // Загрузить меню из файла ресурса
  SetMenu(&m_wndMenu);      // Установить меню
    return 0;
}
void CMainWnd::MenuExit()
{
  DestroyWindow();       // Уничтожить окно 
}

CMyApp theApp;

Скомпилируем и проверим работу программы с меню.

Продолжение программы. Таймер

Задача. Вывести в клиентскую часть окна круг, который при выборе пункта меню Движение \to Старт, начинает движение и отскакивает от стенок клиентской области, а при выборе пункта меню Движение \to Стоп, останавливается. Обработать нажатие клавиш: " \gets, \uparrow, \to, \downarrow " ускоряющих (замедляющих) движение шарика в направлениях указанных стрелками. Добавим в описание класса окна следующий код:

class CMainWnd : public CFrameWnd
{
…
private:
  int VX, sgn_x;  //Приращение координаты по оси x и знак приращения
  int VY, sgn_y;  //Приращение координаты по оси y и знак приращения
  CRect newPlace;  //квадрат, в который вписан отображаемый круг
  CRect oldPlace;  //квадрат, в который был вписан отображаемый круг в предыдущий момент
  …
  DECLARE_MESSAGE_MAP();    // таблица откликов окна
public:
  …
  afx_msg void MenuStart();  // процедура реакции на выбор пункта меню 
  afx_msg void MenuStop();  // процедура реакции на выбор пункта меню 
  afx_msg void OnTimer(UINT_PTR);  // обработка сообщения WM_TIMER
  afx_msg void OnPaint();      // обработка WM_PAINT
  afx_msg void OnKeyDown(UINT, UINT, UINT);  //обработка нажатия клавиши клавиатуры
};

В карту сообщений окна добавим:

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)    // таблица откликов на сообщения
  …
  ON_COMMAND(ID_MOTION_START,MenuStart)  //выбор пункта "старт"
  ON_COMMAND(ID_MOTION_STOP,MenuStop)    //выбор пункта "стоп"
  ON_WM_TIMER()
  ON_WM_PAINT()
  ON_WM_KEYDOWN()      // реакция на нажатие клавиши
END_MESSAGE_MAP()

Добавим инициализацию полей в конструкторе класса:

CMainWnd::CMainWnd()
{
  oldPlace.left = 150;
  oldPlace.top = 150;
  oldPlace.right = 170;
  oldPlace.bottom = 170;
  newPlace = oldPlace;
  VX = VY = sgn_x = sgn_y = 1;
  …
}

Реализация объявленных методов:

void CMainWnd::MenuStart()
{
  SetTimer(ID_TIMER_FOR_MOT,10,NULL);    //Запуск таймера
}

void CMainWnd::MenuStop()
{
  KillTimer(ID_TIMER_FOR_MOT);      //Остановка таймера
}

void CMainWnd::OnTimer(UINT_PTR nIDEvent)
{
  CRect rectClient;
  GetClientRect(&rectClient);
  rectClient.bottom -= 20;
  rectClient.left += 110;
  if(newPlace.right > rectClient.right || newPlace.left < rectClient.left)
    sgn_x = -sgn_x;
  if(newPlace.bottom > rectClient.bottom || newPlace.top < rectClient.top)
    sgn_y = -sgn_y;
  newPlace.left+= sgn_x*VX;  
  newPlace.right += sgn_x*VX;
  newPlace.top += sgn_y*VY;
  newPlace.bottom += sgn_y*VY;
  InvalidateRect(oldPlace);    //Затирается круг со старым центром
  ValidateRect(newPlace);    //Прорисовка круга с новым центром
  oldPlace = newPlace;
}

void CMainWnd::OnPaint()
{
  CPaintDC dc(this);
  CBrush bBrush(RGB(0,0,255));
  dc.SelectObject(&bBrush);
  dc.Ellipse(newPlace);
}

void CMainWnd::OnKeyDown(UINT nChar, UINT, UINT)
{
  switch(nChar)  //Обработка символов с соответствующими кодами
  {
  case 40: if(sgn_y > 0) VY++;
       else VY--;
       break;
  case 38: if(sgn_y > 0) VY--;
       else VY++;
       break;
  case 37: if(sgn_x > 0) VX--;
       else VX++;
       break;
  case 39: if(sgn_x > 0) VX++;
       else VX--;
       break;
  }
}

Комментарий

Функция SetTimer(…) запускает системный таймер с идентификатором, указанным в параметрах, и генерирует синхронно сообщение WM_TIMER для окна, вызвавшего ее, пока таймер не будет уничтожен функцией KillTimer(…) с соответсвующим параметром – идентификатором работающего таймера. Для одного окна может быть запущено несколько таймеров.

Продолжение программы. Вывод данных в строку состояния

Задача. Отобразить в панели состояния текущие координаты курсора.

В проекте создадим ресурс, содержащий две строки: Resource View \to клик правой кнопкой мыши \to Add Resource… Выберем тип ресурса String Table. Нажмем OK. Появится новый ресурс. Он используется для хранения текстовых строк. В него нужно добавить две строки:

  • кликаем правой кнопкой мыши и выбираем New String ( IDS_STRING_X );
  • Caption \to 10 символов подчеркивания (резервируем под вывод 10 символов);
  • создадим еще одну строку – IDS_STRING_Y.

Создадим массив, в котором будут находиться идентификаторы строк (после #define ):

static UINT indicators[] =
{
  IDS_STRING_X,    //Идентификатор первой строки в ресурсах
  IDS_STRING_Y    //Идентификатор второй строки в ресурсах
};

Изменим функцию OnCreate для того, чтобы установить индикаторы:

int CMainWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CFrameWnd::OnCreate(lpCreateStruct) == -1) 
    return -1;
  m_wndStatusBar.Create(this);    //Создать строку состояния
  m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT));  //Установить в строку идентификаторы
  m_wndStatusBar.SetPaneInfo(0,0,0,50);  //Изменение размеров первой секции строки состояния
  m_wndStatusBar.SetPaneInfo(1,0,0,50);  //Изменение размеров второй секции строки состояния
  m_wndMenu.LoadMenu(IDR_MENU);    //Загрузить 
  …
}

В таблицу откликов занесем реакцию на движение мыши:

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)  //Таблица откликов окна
  …
  ON_WM_CREATE()        //Событие создания окна
  ON_WM_MOUSEMOVE()        //Движение мыши
  …
END_MESSAGE_MAP()

И описание соответствующей функции в классе рамки окна:

class CMainWnd : public CFrameWnd
{
public:
  …
  afx_msg void OnMouseMove(UINT, CPoint cp);    //Движение мыши
  …
};

Реализация:

void CMainWnd::OnMouseMove(UINT, CPoint cp)
{
  char chX[10];          //Буфер для координат
  char chY[10];          //Буфер для координат
  itoa(cp.x,chX,10);        //Число переводим в строку
  itoa(cp.y,chY,10);        //Число переводим в строку
  CString csStatusX(chX);        //Формируем строку
  CString csStatusY(chY);        //Формируем строку
  m_wndStatusBar.SetPaneText(0,csStatusX);  //Выводим первую панель
  m_wndStatusBar.SetPaneText(1,csStatusY);  //Выводим вторую панель 
}

Скомпилировать и запустить проект – при перемещении курсора его координаты отображаются в строке состояния.

Комментарии

Для отображения в панели состояния, необходимо создать массив идентификаторов. Это необходимо для начала работы панели, а массив служит как шаблон. При запуске будет выведено именно то, что есть в строках, находящихся в ресурсах. Функция SetIndicators(...) говорит панели состояния о том, что у нее будут две панели. В параметрах – массив идентификаторов и количество элементов:

BOOL SetIndicators(const UINT* lpIDArray, int nIDCount);

Параметр lpIDArray – указатель на массив, nIDCount – количество элементов в массиве, sizeof(indicators)/sizeof(UINT) – размер массива/размер одного элемента

Движение мыши отслеживается с помощью сообщения – ON_WM_MOUSEMOVE(). Обработчиком этого сообщения является функция – OnMouseMove(). Этой функции передается положение мыши в виде Point. Далее, необходимо сделать перевод числа в строку - itoa, использовать объект класса CString, и, для вывода в строку состояния, использовать функцию SetPaneText(...)

BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );

nIndex – номер панели для вывода, lpszNewText – текст для вывода, bUpdate – обновлять.

Жанат Агайдаров
Жанат Агайдаров
Казахстан
Сергей Пузырев
Сергей Пузырев
Украина