PDA

Просмотр полной версии : [Руководство] Добавляем к окну элементы управления.


Ivan_32
23.01.2009, 03:57
Немного теории
У окон в Win32 есть несколько классов, каждый из них имеет свои сообщения, нотификации и конечно же внешний вид. Сегодня мы познакомимся с некоторыми распространенными видами оконных классов, вернее говоря с базовыми и напишем на их основе простенькую программу использующую контролы. Ну что ж приступим.

Открываем нашу заготовку, сделанную в предыдущем уроке и начинаем ваять. Освежим память. Заготовка выглядит вот так:

#include "stdafx.h"
#include <windows.h>
LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HWND hMain=CreateWindowEx(0,WC_DIALOG,"Hello world",WS_VISIBLE|WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT ,200,200,0,0,0,0);
SetWindowLong(hMain,DWL_DLGPROC,(long)mainProc);//Примечание 1.
SendMessageA(hMain,WM_CREATE,0,0);
while(GetMessage(&msg,0,0,0)!=NULL)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp)
{
switch(message)
{
case WM_CREATE:
{

}break;
case WM_CLOSE:
{
PostQuitMessage(0);
}break;
}
return 0;
}


Этот код немного отличается от того который мы писали в прошлый раз. Отличие состоит лишь в том что тут функция mainProc сначала объявлена, а потом уже инициализирована. Stdafx.h это дефолтовый файл-заголовок для проекта, там прописана директива WIN32_LEAN_AND_MEAN и не более.
Примечание 1: Эта строка посылает сообщение нашему окну, оно обрабатывается функцией mainProc как и другие сообщения.

Думаю тут все понятно. Как я уже говорил любой элемент управления это по сути окно, которому дали предка(parent), сами по себе они тоже могут жить, если не указать в функции CreateWindowEx окно-предок.

Вот код который добавит к нашей форме кнопку:

CreateWindowEx(0,"button","Press Me",WS_VISIBLE|WS_CHILD,50,73,100,20,hWnd,(HMENU)1000 ,0,0);

WS_CHILD - это стиль дочернего окна, без него наша кнопка появится в отдельном окне и ее события не будут обрабатываться функцией mainProc.
Эту строку следует поместить в событие WM_CREATE, можно конечно создавать кнопки еще на начальном этапе в функции WinMain, тогда вместо hWnd стоит поставить имя дескриптора окна(в нашем случае hMain).
1000 это уникальный идентификатор нашего окна(контрола).

Наша кнопка уже отображается и на нее даже можно нажать, но почему то ничего не происходит. Это так потому что мы не добавили обработчик событий для нашей кнопки. За обработку ВСЕХ сообщений контролов отвечает сообщение WM_COMMAND но к нему поступают сообщения для всех контролов, но каждое сообщение в WPARAM содержит идентификатор контрола о котором говорилось выше. Видоизменим наш код, так что бы при нажатии на нашу кнопку выскакивал MessageBox:


case WM_COMMAND:
{
if(wp==1000)
{
MessageBox(0,"You pressd me, thank you!","Press Me message",0);
}
}break;


Этот обработчик нужно добавить в функцию mainProc.

Ну что ж теперь наша кнопка реагирует на нажатие. Теперь поставим себе задачу написать программу которая будет копировать текст из первого текстового поля во второе. Это наименее сложный пример использования контролов. Класс контрола текстовго поля называется "edit".

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

int GetWindowText(
HWND hWnd,
LPTSTR lpString,
int nMaxCount
);

1. Дескриптор(хендл - HWND) окна.
2. Адрес строки приемника.
3. Максимальное количество получаемых символов.
Функция возвращает количество полученных символов.

Функция которая установит второму текстовому полю текст из первого поля:

BOOL SetWindowText(
HWND hWnd,
LPCTSTR lpString
);

1. Дескриптор окна приемника.
2. Адрес строки для установки.
Функция возвращает TRUE (1) если операция прошла успешно.

Так же нам понадобится выделить память-буффер для строки. Сделаем мы это с помощью вот такой нехитрой строки:

char* szBuffer=(char*)malloc(260);

Функция malloc - дефолтовая функция в С++ для выделения памяти, для работы с ней надо подключить файл stdlib.h.

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



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

Требования к программе:
1. Программа должна копировать строку из одного контрола в другой.
2. Корректно работать и иметь переносимый функционал.

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

Алгоритм работы нашей функции:
1. В аргументы передаются такие параметры:
1.Адрес текстового буфера.
2.Дескриптор первого окна.
3.Дескриптор второго окна.
2. С помощью функции GetWindowText, текст из первого контрола будет перенесен в текстовый буфер(адрес которого передается в функцию первым параметром)
3. С помощью функции SetWindowText, текст второго окна будет установлен как текст содержащийся в текстовом буфере.
Функция будет иметь тип возвращаемого значение BOOL , в случае удачного завершения функция возвратит результат TRUE.

Код функции CopyText :

bool CopyText(char* szBf,HWND hEdit0,HWND hEdit1)
{
bool res=FALSE;
if(GetWindowTextA(hEdit0,szBf,260)!=NULL)
{
if(SetWindowTextA(hEdit1,szBf)!=NULL)
{
res=TRUE;
}
}
return res;
}

Как видно все предельно просто.

Теперь напишем графическую оболочку.
Для начала нужно определится с подключаемыми файлами и константами. Ими станут windows.h и stdio.h. Так же очень неплохо будет сделать макросы (препроцессорной директивой #define) для идентификаторов наших контролов. Контролов у нас всего 3, значит и идентификаторов тоже будет три : 1000(Кнопка) 1001(текстовое поле 1) 1002(текстовое поле 2). Так же нам понадобятся 3 дескриптора окна для наших контролов. С учетом всех приведенных требований можно написать такой код:

#include <windows.h>
#include <stdlib.h>
#define button 1000
#define edit1 1001
#define edit2 1002
static HWND hBt;
static HWND hEditA;
static HWND hEditB;


Далее нужно указать прототипы наших функций:

bool CopyText(char* szBf,HWND hEdit0,HWND hEdit1);
LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp);


Функция WinMain выглядит вот так:

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HWND hMain=CreateWindowEx(0,WC_DIALOG,"Hello world",WS_VISIBLE|WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT ,200,200,0,0,0,0);
SetWindowLong(hMain,DWL_DLGPROC,(long)mainProc);
SendMessageA(hMain,WM_CREATE,0,0);
while(GetMessage(&msg,0,0,0)!=NULL)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}

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

LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp)
{
Примечание 0:
char* szTextBuffer=(char*)malloc(260);
switch(message)
{
case WM_CREATE:
{
//Примечание 1: hEditA=CreateWindowEx(0,"edit",0,WS_VISIBLE|WS_CHILD|WS_BORDER,10,10,100,20,hWnd ,(HMENU)edit1,0,0);
hEditB=CreateWindowEx(0,"edit",0,WS_VISIBLE|WS_CHILD|WS_BORDER,10,35,100,20,hWnd ,(HMENU)edit2,0,0);
hBt=CreateWindowEx(0,"button","Press Me",WS_VISIBLE|WS_CHILD,10,65,100,20,hWnd,(HMENU)butt on,0,0);
}break;
case WM_COMMAND:
{
//Примечание 2:
if(wp==button)
{
CopyText(szTextBuffer,hEditA,hEditB);
}
//Конец второго примечания.
}break;
case WM_CLOSE:
{
PostQuitMessage(0);
}break;
}
return 0;
}

Примечание 0: То самое выделение памяти, 260 байт должно вполне хватить.
Примечание 1: WS_BORDER - стиль рамки вокруг контрола. Для текстовго поля это черное обрамление подчеркивающее его границы. Третий параметр с конца это идентификатор окна. Второй параметр это имя класса контрола.
Примечание 2: Если в wp находится идентификатор кнопки то выполнится наша функция.

Вот собственно и все. В заключение приведу весь код нашей программы:


#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#define button 1000
#define edit1 1001
#define edit2 1002
static HWND hBt;
static HWND hEditA;
static HWND hEditB;
bool CopyText(char* szBf,HWND hEdit0,HWND hEdit1);
LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HWND hMain=CreateWindowEx(0,WC_DIALOG,"Hello world",WS_VISIBLE|WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT ,200,200,0,0,0,0);
SetWindowLong(hMain,DWL_DLGPROC,(long)mainProc);
SendMessageA(hMain,WM_CREATE,0,0);
while(GetMessage(&msg,0,0,0)!=NULL)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT WINAPI mainProc(HWND hWnd,UINT message,WPARAM wp,LPARAM lp)
{
char* szTextBuffer=(char*)malloc(260);
switch(message)
{
case WM_CREATE:
{
hEditA=CreateWindowEx(0,"edit",0,WS_VISIBLE|WS_CHILD|WS_BORDER,10,10,100,20,hWnd ,(HMENU)edit1,0,0);
hEditB=CreateWindowEx(0,"edit",0,WS_VISIBLE|WS_CHILD|WS_BORDER,10,35,100,20,hWnd ,(HMENU)edit2,0,0);
hBt=CreateWindowEx(0,"button","Press Me",WS_VISIBLE|WS_CHILD,10,65,100,20,hWnd,(HMENU)butt on,0,0);
}break;
case WM_COMMAND:
{
if(wp==button)
{
CopyText(szTextBuffer,hEditA,hEditB);
}
}break;
case WM_CLOSE:
{
PostQuitMessage(0);
}break;
}
return 0;
}
bool CopyText(char* szBf,HWND hEdit0,HWND hEdit1)
{
bool res=FALSE;
if(GetWindowTextA(hEdit0,szBf,260)!=NULL)
{
if(SetWindowTextA(hEdit1,szBf)!=NULL)
{
res=TRUE;
}
}
return res;
}



To Be Continued...