Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
388
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

Создание главного окна SDI-приложения

В рассмотренной ранее программе First мы образовали класс нашего окна из базового CWnd. Это допустимый, но очень неэффективный путь. Пришло время познакомить вас с одним из возможных подходов для создания главного окна SDI-приложения.1

Прежде всего обратим внимание на базовый класс нашего главного окна:

class CMainFrame : public CFrameWnd

Процесс создания окна состоит из трех шагов. Прежде всего необходимо создать объект "главное окно":

CMainFrame *pMainWnd = new CMainFrame;

Следующий шаг состоит в вызове специальной функции, которая создает окно Windows и присоединяет его к нашему объекту:

pMainWnd->LoadFrame (IDR_MA IN FRAME) ;

И, наконец, следует отобразить окно на экране, чтобы пользователь мог с ним взаимодействовать:

pMainWnd->SetWindowPos (NULL, ex, су, 6*сх, 2*су, SWP_NOZORDER | SWP_SHOWWINDOW) ;

Рассмотрим сначала процесс создания окна Windows, или, используя терминологию MFC, фрейма окна.

Из двух функций, реализованных в классе CFrameWnd, мы остановили свой выбор на виртуальной функции CFrameWnd::LoadFrame, которая берет на себя задачу регистрации оконного класса, используя для этого предопределенный шаблон, и извлекает из файла ресурсов, задаваемого идентификатором, остальные необходимые для создания окна Windows параметры:

virtual BOOL CFrameWnd::LoadFrame( UINT nIDResource,

DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, CWnd *pParentWnd = NULL, CCreateContext *pContext = NULL)

Функция возвращает значение TRUE, если создание фрейма прошло успешно, и FALSE в противном случае. Она имеет следующие параметры: nIDResource — идентификатор разделяемых ресурсов, ассоциированных с фреймом, который определяет меню, таблицу акселераторов, пиктограмму и строку, помещаемую в заголовок окна; pParentWnd — указатель на родительское окно данного фрейма (необязательный параметр); pContext — указатель на структуру CCreateContex, которая определяет объекты, связанные

данным фреймом; dwDefaultStyle — стиль фрейма (необязательный параметр), дополнительно к стандартным могут быть использованы следующие стили:

FWS_ADDTOTITLE FWS_PREFIXTITLE FWS_SNAPTOBAR

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

Основное назначение этой функции — максимальное упрощение процесса создания фрейма окна.1 CFrameWnd::Create, функция загружает ресурс меню и вызывает функцию CWnd::CreateEx, которая регистрирует имя оконного класса, заполняя структуру WNDCLASS, и вызывает виртуальную функцию

PreCreateWindow. В процессе выполнения функции Windows посылает в окно следующие сообщения: WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALKSIZE и WM_CREATE. Если при создании окна был определен стиль WSJVISIBLE, то в окно дополнительно посылаются все сообщения, необходимые для активизации и отображения окна на экране. Таким образом, переопределяя функцию PreCreateWindow и обработчик любого из представленных сообщений Windows, можно изменять параметры оконного класса и собственно окна, как до, так и после создания окна Windows.

Рассмотрим, как этот механизм реализован в нашей программе. Начнем с функции CMainFrame::PreCreate Window.

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { cs.lpszName = "Главное окно приложения \"Стили\""; return CFrameWnd::PreCreateWindow(cs);

Единственный параметр, передаваемый в эту функцию, — ссылка на структуру CREATESTRUCT, которая определяет параметры инициализации, посылаемые в оконную процедуру, и содержит следующие поля:

typedef struct tagCREATESTRUCT{ LPVOID IpCreateParams;

HANDLE hlnctance; KMENU hMenu;

HWND hwndParent; int cy;

int ex; int y; int x;

LONG style; LPCSTR IpszName; LPCSTR IpszClass;

DWORD dwExStyle; ) CREATESTRUCT;

Назначение полей этой структуры: IpCreateParams — указатель на данные, используемые при создании окна; hlnstance — дескриптор модуля, который владеет окном; hMenu — идентификатор меню, используемого окном; hwndParent — дескриптор окна-родителя, если этот параметр равен NULL, то новое окно является окном верхнего уровня; су, сх, у, х — целые числа, задающие соответственно ширину и высоту окна, а также его положение относительно своего владельца; IpszName — символьная строка, задающая имя окна; IpszClass — символьная строка, содержащая имя класса, которому принадлежит окно; dwExStyle — определяет атрибуты расширенного стиля окна.

Мы использовали эту функцию, чтобы не заниматься процедурой регистрации класса окна, возложив ее выполнение на библиотеку, поскольку принятые по умолчанию установки нас вполне устраивают. Единственное, что нас не устраивает, это заголовок окна, который мы и переопределили:

cs.lpszName = "Главное окно приложения \"Стили\"";

Это один из возможных способов присвоения имени окну.1

После того как окно Windows создано и присоединено к объекту фрейма окна, в ответ на сообщение WM_CREATE вызывается обработчик сообщения OnCreate (механизм вызова был нами подробно описан при обсуждении механизма работы с сообщениями в библиотеке MFC):

int CMainFrame: : OnCreate (LPCREATESTRUCT IpCreateStruct) { if (CFrameWnd: : OnCreate (IpCreateStruct) == -1) return -1; return 0;

Эта функция — одно из возможных мест, где создаются иприсоединяются кокну произвольные элементы пользовательского интерфейса. Для правильного завершения процесса создания окна не забывайте вызывать перед какими-либо своими действиями обработчик базового класса СFrame Wnd:: On Create.

После того как фрейм создан, его необходимо вывести на экран. Библиотека предоставляет несколько функций для реализации этого процесса. Для отображения нашего главного окна мы остановились на одной из них — функцииCWnd::SetWindowPos:

BOOL CWnd::SetWindowPos( const CWnd *pWndlnsertAfter, int x, inty, int ex, int cy, UINT nFlags)

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

Примечание

Windows располагает окна на экране согласно их положению в так называемом Z-порядке, который определяет позицию окна в стеке перекрывающихся окон. Стек окна ориентирован вдоль воображаемой оси (Z-оси) внутрь экрана. Окно, расположенное на вершине стека, перекрывает все другие окна, а расположенное внизу — перекрывается всеми другими окнами. Приложение не может активизировать окно без того, чтобы не перенести его на самый верх Z-порядка. Кроме того, расположение окна относительно экрана и других окон может из-

меняться без каких-либо

Мы использовали эту функцию для указания размеров и расположения окна, а также для того, чтобы сделать его видимым.1

Отложим пока обсуждение вопросов создания и разрушения объекта класса CStylesApp до тех пор, пока не рассмотрим, как создаются дочерние окна.

Создание дочерних окон

Теперь, после создания основного окна приложения, можно переходить к созданию некоторого дополнительного пространства для отображения информации или для получения команд от пользователя. Каждому "ребенку", как в свое время и основному окну, можно приписать конкретный стиль таким образом, чтобы приспособить его внешний вид и назначение к стоящим перед ним целям. В приложении Styles мы разбили создаваемые окна на два набора. Окна первого располагаются на экране независимо от положения основного окна — все они имеют стиль WS_POPUP, а для окон из второго набора установлен стиль WS_CHILD и, следовательно, они не могут покинуть пределы рабочей области основного окна.

Для того чтобы понять принципы построения и работу рассматриваемой программы, вернемся к обработчику сообщения WM_CREATE. Повторим ту часть программы, на которую следует обратить внимание:

extern char szNameWnd [ 128 ] ;

// Создание пяти дочерних окон for (int. i = 0; i < 5; i + +)

{

apChildWnd[i] = new CChildWnd;

apChildWnd[i] ->Create (NULL, NULL, WS_VISIBLE, CRect(0, 0, 0, 0), this, (UINT) i) ; }

//Установка нового курсора для класса дочерних окон : :SetClassLong (apChildWnd [ 0 ] - >GetSafeHwnd () , GCL_HCURSOR, (LONG) AfxGetApp () ->LoadStandardCursor (IDC_IBEAM) ) ;

//Создание четырех всплывающих окон for(i = 0; i < 4;

apPopupWnd [ i ]

= new CPopupWnd; apPopupWnd[i]->Create (NULL, NULL, 0,

rectDefault,

this);

1Существуютидругиеспособырешенияэтойзадачи. Ихмыопишемниже.

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

должны совершенно четко отличать технический терминWS_CHILD отболееширокогоразговорногопонятия"дочерний

//Указание номера окна в поле данных ассоциированных с окном : : SetWindowLong (apPopupWnd [ i ] ->GetSaf eHwnd ( ) , GWL_USERDATA, i) ;

//Формирование нового заголовка окна sprintf (szNameWnd, "Окно %d", i)

;apPopupWnd [ i ] ->SetWindowText (szNameWnd) ;

}

//Создание перекрывающегося окна apPopupWnd [4] = new CPopupWnd;

apPopupWnd [4] ->Create (NULL, NULL, 0, rectDef ault) ; : : SetWindowLon2g (apPopupWnd [ i ] ->GetSaf eHwnd ( ) , GWL_USERDATA, 4 ) ;

sprintf (szNameWnd, "Окно %d", i) ; apPopupWnd [4 ] ->SetWindowText (szNameWnd) ;

//Установка нового курсора для класса всплывающих окон : : SetClassLong (apPopupWnd [0] ->GetSaf eHwnd () , GCL_HCURSOR, (LONG) AfxGetApp () ->LoadStandardCursor (IDC_UPARROW) ) ;

//Создание и установка нового цвета фона для всплывающих окон HBRUSH hNewBrush = : : CreateSolidBrush (RGB (255, 255, 220)); : : SetClassLong (apPopupWnd [3] ->GetSaf eHwnd ( ) , GCL_HBRBACKGROUND, (LONG) hNewBrush) ;

//Отображение всплывающих окон for(i = 0 ; i < 5;

apPopupWnd [i] ->ShowWindow (SW_SHOWNORMAL) ; apPopupWnd [ i ] ->UpdateWindow ( )

;

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

class CChildWnd : public CWnd i

а объекты второго — всплывающие и/или перекрывающиеся окна (хотя с тем же успехом можно создавать и дочерние):

class CPopupWnd : public CFrameWnd

Такое разбиение не продиктовано необходимостью и произведено лишь для наглядности. Дело в том, что, как мы упоминали, на базе класса CWnd можно создавать только дочерние окна (имеющие стиль WS_CHILD). Его мы и решили использовать для этих целей. На базе же класса CFrameWnd могут быть созданы окна любого из трех имеющихся типов. Поэтому для создания всплывающих окон именно он был использован в качестве базового.

В основном процесс создания окон обоих типов стандартен: создаются объекты классов, после чего вызывается некоторая функция для создания окна Windows. С целью рассмотрения как можно большего числа предоставляемых библиотекой возможностей мы включили в текст программы дополнительные строки. Но обо всем по порядку. Начнем с дочерних окон, имеющих установленный бит стиля WS_CHILD (процесс создания объекта класса ничем не отличается от уже рассмотренных, и мы больше не будем останавливаться на этом вопросе). Итак, вызов функции CWnd::Create:

apChildWnd[i]->Create(NULL, NULL, WS_VISIBLE, CRect(0, 0, 0, 0), this, (UINT)i);

Подчеркнем некоторые моменты. Во-первых, не определен класс Windows, к которому относится окно,

— эту задачу мы переложили на плечи библиотеки. Во-вторых, установив бит стиля WSJV1SIBLE, мы, тем самым, задали, чтобы окно сразу же после создания было выведено на экран. В-третьих, окно мы превратили в точку и поместили его в левый верхний угол экрана. В-четвертых, окно имеет "родителя" — пятый параметр определен как this, И, наконец, в качестве последнего параметра мы передаем номер создаваемого окна.

Как и при создании главного окна мы переопределяем функцию

BOOL CChildWnd::PreCreateWindow(CREATESTRUCT& cs) { cs.style != adwStyles[nCount];

sprintf(szNameWnd, "Окно %d", nCount); cs.lpszName = szNameWnd;

return CWnd::PreCreateWindow(cs);

и обработчик сообщения WM_CREATE

int CChildWnd::OnCreate(LPCREATESTRUCT IpCreateStruct) { if (CWnd:rOnCreate(IpCreateStruct) == -I) return -1;

MoveWindow(nCount++ * 118, 24, 116, 96); return 0;

Первая заполняет поля style и IpszName структуры CREATESTRUCT соответственно значениями стиля и имени конкретного окна, а вторая — устанавливает размер и положение вновь созданного окна (напомним, что мы задали окну нулевые размеры). Со структурой CREATESTRUCT мы уже познакомились, поэтому рассмотрим изящную функцию CWnd::MoveWindow, которая реализована в двух вариантах:

void CWnd::MoveWindow( intx,

int у,

int nWidth, int nHeight,

BOOL bRepaint = TRUE);

и

void CWnd::MoveWindow( LPCRECT IpRect, BOOL bRepaint = TRUE)

С помощью любой из этих функций можно одновременно изменить размер и положение окна, задаваемые в параметрах х, у, nWidth, nHeight или IpRect.1 Последний параметр этой функции определяет, будет ли окно перерисовываться после перемещения. Если этот параметр равен TRUE (по умолчанию), то объект "окно" получает сообщение Windows WM_PAINT. Кроме того, эта функция посылает еще и сообщение WM_GETMINMAXINFO, которое вы также можете обработать.

Как мы уже отметили, задача регистрации оконного класса была поручена библиотеке. Нам лишь остается "подправить" некоторые поля в соответствии с собственными нуждами. Один из возможных путей при этом состоит в использовании функций API SetClassLong и SetWindowLong. Возможно, вы захотите их использовать в своих программах, поэтому рассмотрим их подробнее:

DWORD SetClassLong( HWND hWnd, int nlndex,

LONG dwNewLong) -

эта функция устанавливает в структуре WNDCLASS, описывающей оконный класс для hWnd, новое значение dwNewLong в поле, определяемом параметром nlndex, который может принимать одно из следующих значений:

GCL_CBCLSEXTRA устанавливает размер (в байтах) дополнительной памяти,

предназначенной для использования всеми окнами, создаваемыми на базе этого класса; размер памяти, уже ассоциированной с классом, при этом не изменяется

GCL_CBWNDEXTRA устанавливает размер (в байтах) дополнительной памяти,

предназначенной дляиспользованияотдельнокаждым окном, создаваемыми на базе этого класса; размер памяти, уже ассоциированной с окном, при этом не изменяется

GCLJHBRBACKGROUND устанавливает дескриптор кисти для фона, ассоциированного с этим классом

GCL_HCURSOR

устанавливает дескриптор курсора, ассоциированного с

этим классом

 

GCL_HICON устанавливает дескриптор пиктограммы, ассоциированной

с этим классом; это значение используется только в Windows 95 и не определено в Windows NT

GCL_HMODULE

устанавливает дескриптор модуля, который зарегистриро-

вал этот класс

 

GCL_MENUNAME

устанавливает адрес строки меню, которая идентифици-

рует ресурс меню, ассоциированного с этим классом

GCL_STYLE устанавливает биты стиля оконного класса

GCL_WNDPROC

устанавливает адрес оконной процедуры, ассоциирован-

ной с классом

 

При успешном выполнении функция возвращает значение указанного слова из структуры класса окна или нулевое значение при ошибке. Дополнительную информацию об ошибке можно получить, вызвав функцию API GetLastError.

Мы воспользовались этой функцией для того, чтобы установить новые значения для курсора и цвета фона.

Обратную задачу — получение информации из структуры класса окна — выполняет функция

DWORD GetClassLong(

HWND hWnd, int nlndex)

Для работы с дополнительной памятью в структуре окна API предоставляет также две функции:

DWORD SetWindowLong(

HWND hWnd, int nlndex,

LONG dwNewLong)

и

DWORD GetWindowLong(

HWND hWnd, int nlndex)

По своему принципу действия они аналогичны только что рассмотренным, но работают с дополнительной памятью в структуре окна. Параметр nlndex при этом может принимать следующие

значения:

GWL_EXSTYLE устанавливает новый расширенный стиль окна

GWL_STYLE устанавливает новый стиль окна

GWL_WNDPROC устанавливает новый адрес оконной процедуры

GWL_HINSTANCE устанавливает новый дескриптор приложения

GWLJD

устанавливает новыйидентификатор окна

GWL_USERDATA устанавливает 32-разрядное значение, ассоциированное с конкретным окном и предназначенное дляиспользованияприложением, которым это окно создано

Если дескриптор hWnd определяет блок диалога, то доступными являются следующие значения:

DWL_DLGPROC устанавливает новыйадрес процедурыблокадиалога

DWL_MSGRESULT устанавливает значение, возвращаемое при обработке сообщенияв процедуреблока диалога

DWL_USER устанавливает новую дополнительную информацию для приложения, такую как идентификатор или указатель

Для функции GetWindowLong кроме перечисленных определено еще одно доступное значение:

GWL_HWNDPARENT возвращает дескриптор родительского окна или NULL, если егонет

Мы использовали эти функции для записи в дополнительную память номера созданного окна. Другими словами, у первого окна в этом поле будет нуль, у второго — 1, у третьего — 2 и т. д. Позже, во время изображения окон на экране, программа воспользуется этими значениями для идентификации окон.

Перед тем как закончить обсуждение вопроса об изменении характеристик оконных классов и собственно окон, осталось отметить способ получения дескриптора окна Windows. Для этой цели в классе CWnd предусмотрена специальная функция

HWND CWnd::GetSafeWnd(),

которая позволяет получить дескриптор окна Windows, хранящийся в члене класса CWnd::m_hWnd, или NULL, если к объекту не присоединено никакое окно.

Но вернемся к нашей программе. Нам осталось разобраться в создании всплывающих окон. Обратите внимание на два несколько отличающихся вызова функции CFrameWnd::Create\

apPopupWndti]->Create(NULL, NULL, 0, rectDefault, this);

И

apPopupWnd[4]->Create(NULL, NULL, 0, rectDefauit);

и связанное с этим отличие в поведении всплывающих окон 3 и 4. Для четвертого окна мы не указали родителя, и поэтому оно ведет себя достаточно независимо от нашего главного окна.

Второй момент, на который хотелось бы обратить ваше внимание, связан с еще одним способом изменения стилей созданных окон. Обратимся к тексту:

int CPopupWnd::OnCreate(LPCREATESTRUCT IpCreateStruct) ( ModifyStyle((DWORD)-1, adwStyles[nCount]);

Как видите, для установки стиля окна мы воспользоватись функцией класса CWnd - ModifyStyle

BOOL CWnd::ModifyStyle( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0)

Эта функция позволяет удалить (dwRemove) или установить (dwAdd) определенные атрибуты стиля окна. Если установлен параметр nFlags, то вызывается функция API ;:SetWindowPos с этим параметром, являющимся комбинацией одного или нескольких значений: SWP NOMOVE, SWP_NOACTIVATE, SWP NOSIZE и SWP_NOZORDER.

Для изменения флагов расширенного стиля можно использовать функцию:

BOOL CWnd::ModifyStyleEx( DWORD dwRemove,

DWORD dwAdd, UINT nFlags = 0)