API и FreeBasic. (позиционирование и размеры окна)

В одной из прошлых статей изменяя стили, мы познакомились с функцией SetWindowPos. Ничто не мешат применить эту функцию для изменения размеров и расположения окна. И так пример с использованием SetWindowPos:

#INCLUDE "windows.bi"
Dim msg As MSG 'структурированная переменная MSG
Dim As WNDCLASSEX wc 'структурированная переменная WNDCLASSEX
Dim As RECT  FWRECT ' структура Rect для определения размеров рабочего стола
Dim As String NameClass="MyClass" ' переменная имени класса
Dim As HINSTANCE Hinst=GetModuleHandle(0) ' хендл модуля
Dim Shared As Integer  Dwidth 'ширина рабочего стола

' функция класса
Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Static width_ As Integer=300
    Select Case msg
        Case WM_DESTROY
            PostQuitMessage(0)
        Case WM_MOUSEWHEEL
            Dim As Long wp = wparam 'делаем значение со знаком
            If wp<0 Then
                width_+=1
            Else
                width_-=1
            Endif
            'изменяем размеры окна
            SetWindowPos(hwnd,0,0,0,width_,width_,SWP_NOMOVE Or SWP_NOZORDER)
        Case WM_LBUTTONDOWN
            'Установка окна в верхний левый угол
            SetWindowPos(hwnd,0,0,0,0,0,SWP_NOSIZE Or SWP_NOZORDER)
        Case WM_RBUTTONDOWN
            'Установка окна в верхний правый угол
            SetWindowPos(hwnd,0,Dwidth-width_,0,0,0,SWP_NOSIZE Or SWP_NOZORDER)
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function

' получаем хендл рабочего стола
Var win=GetDesktopWindow()
' получаем размеры рабочего стола в структуру
GetWindowRect(win,@FWRECT)
' заносим ширину рабочего стола в переменную
Dwidth = FWRECT.right

' Заполнение структуры WNDCLASSEX
With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_HELP)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
    .lpszClassName=StrPtr(NameClass)
    .hIconSm=.hIcon
End With
' Регистрация класса окна
If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

'Создание окна
CreateWindowEx(0,NameClass,"Первое окно",_
WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,300,300,0,0,Hinst,0)

' Цикл сообщений
While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend

В данном примере по нажатию правой кнопки мыши, окно перемещается в правый угол. По нажатию левой кнопки мыши в левый угол. А если покрутить колесико мыши, то окно изменяет свои размеры в соответствии с направлением.

В этом примере появилось кое-что новое. И я попробую объяснить это. В первую очередь появилась структурированная переменная FWRECT, имеющая тип RECT (ее поля):

  • left
  • top
  • right
  • bottom

Все они имеют тип Long. Очень много функций используют в своем параметре эту структуру. И функция GetWindowRect одна из них. Ее параметры:

  • HWND -хендл окна для определения его характеристик
  • RECT -  структура типа RECT 

В результате работы функции GetWindowRect  в поля структуры запишется расположение (x,y -верхний левый угол) и значение по осям X и Y от верхнего левого угла (right , bottom) . Но перед тем как определить размеры рабочего стола , нужно получить его хендл. В этом поможет функция GetDesktopWindow(). Определив хендл, а потом характеристики окна, нам нужно всего одно из полей структуры (right). Это даст нам ширину окна, ведь ширина окна вычисляется так (right-left) , но поскольку параметр Left у рабочего стола всегда нулевой, то вычислять нет надобности. Получив ширину, заносим значение в глобальную переменную для позиционирования окна в правом углу рабочего стола. Как вы понимаете, для того чтобы наше окошко было в рамках рабочего стола и примыкало к правой его границе, нам нужно вычесть из общей ширины окна рабочего стола ширину нашего окошка. Это и будет координата по оси X для правого позиционирования. Для левого позиционирования окна ничего вычислять не надо, координаты рабочего стола и нашего окна в этом случае совпадают.(0,0).  

В нашей функции сообщений для всех трех случаев используется функция SetWindowPos. Для изменения размеров окна используются 5 и 6 параметры и флаги SWP_NOMOVE Or SWP_NOZORDER, говорящие системе, что нужно игнорировать параметры перемещения и стиля окна. Сообщение WM_MOUSEWHEEL  определяет движение колесика мыши и в параметр WPARAM заносит  числовое значение, которое характеризует движение в ту или иную сторону. Положительное значение указывает, что колесо было повернуто вперед, в сторону от пользователя; отрицательное значение указывает, что колесо было повернуто назад, в сторону пользователя. Однако значение в параметре WPARAM базируется на типе UINTEGER (беззнаковое) , Именно поэтому мы создали переменную WP с типом Integer и присваиваем ему значение WPARAM.

Для изменения расположения используются параметры 3 и 4, а так же флаги SWP_NOSIZE Or SWP_NOZORDER , говорящие системе, что нужно игнорировать параметры изменения размеров и стиля окна. При нажатии левой кнопкой мыши, мы в параметры заносим (0,0) . При нажатии правой кнопкой мыши (Dwidth-width_ , 0)  

Все таки великолепная функция SetWindowPos!

Как бы там не было, но для изменения размеров и расположения чаще применяют функцию MoveWindow, ее параметры:

  • hWnd  -  хендл окна
  • X  -  расположение по оси Х
  • Y  -  расположение по оси Y
  • nWidth  -  ширина окна
  • nHeight  -  высота окна
  • bRepaint  -  флаг перерисовки клиентской области (если 1 - то перерисовать , если 0 не перерисовывать)

Я считаю что объяснять по этой функции ничего не нужно, все и так интуитивно понятно. Поэтому сразу пример:

#INCLUDE "windows.bi"
'Создание структурированной переменной MSG
Dim msg As MSG
'Создание окна
Var hwnd=CreateWindowEx(0,"#32770","Первое окно",WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,300,300,0,0,0,0)
MoveWindow(hwnd,0,0,640,480,TRUE)
'Обработка сообщений в цикле
While GetMessage(@msg,0,0,0)
    DispatchMessage(@msg)
    If msg.message=WM_COMMAND Then Exit While
Wend

Я для примера взял самый маленький исходный текст создания окошка из нашей первой статьи. Здесь изначально создается окно с размерами 300х300 и в координатах 100,100. Далее с помощью MoveWindow мы меняем характеристики диалогового окна.

Иногда требуется выставить размеры окна и ограничить их по высоте и ширине. То есть окно должно иметь свойство менять размеры, но в определенных рамках. Скажем не больше 500 и не меньше 200 по ширине и высоте. Давайте это реализуем.

Делается это с помощью отлавливания сообщения WM_GETMINMAXINFO , которое вынимает из параметра Lparam адрес структуры MINMAXINFO. В зависимости от того, что мы занесем в эту структуру, таковы и будут характеристики окна. После того, как сообщение будет обработано, нужно возвратить ноль.И так по порядку:

Структура MINMAXINFO (точнее ее поля):

  • ptReserved  -   не испольуется
  • ptMaxSize  -   устанавливает развернутую ширину (point.x) и развернутую высоту (point.y) окна
  • ptMaxPosition  -   устанавливает позицию левой стороны развернутого окна (point.x) и позицию верхней части развернутого окна (point.y)
  • ptMinTrackSize  -   определяет минимум устанавливаемой ширины (point.x) и минимум устанавливаемой высоты (point.y) окна.
  • ptMaxTrackSize  -   определяет максимум устанавливаемой ширины (point.x) и максимум устанавливаемой высоты (point.y) окна

Все поля структуры MINMAXINFO  имеют тип структуры POINT (ее поля):

  • X - координата х
  • Y - координата y

Вы наверно уже догадались, что для нашей задачи потребуются два поля структуры MINMAXINFO (ptMinTrackSize и ptMaxTrackSize). В эти поля и надо прописать наши значения. И так пример:

#INCLUDE "windows.bi"
Dim msg As MSG 'структурированная переменная MSG
Dim As WNDCLASSEX wc 'структурированная переменная WNDCLASSEX
Dim Shared As MINMAXINFO Ptr  minmax ' структура для определения рамок размеров
Dim As String NameClass="MyClass" ' переменная имени класса
Dim As HINSTANCE Hinst=GetModuleHandle(0) ' хендл модуля

' функция класса
Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Static width_ As Integer=300
    Select Case msg
        Case WM_DESTROY
            PostQuitMessage(0)
        Case WM_GETMINMAXINFO
            minmax=Cast(MINMAXINFO Ptr,lparam)
            minmax->ptMinTrackSize.x=200 'минимальный размер по оси X
            minmax->ptMinTrackSize.y=200 'минимальный размер по оси Y
            minmax->ptMaxTrackSize.x=500 'максимальный размер по оси X
            minmax->ptMaxTrackSize.y=500 'максимальный размер по оси Y
            Return 0
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function


' Заполнение структуры WNDCLASSEX
With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_HELP)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
    .lpszClassName=StrPtr(NameClass)
    .hIconSm=.hIcon
End With
' Регистрация класса окна
If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

'Создание окна
CreateWindowEx(0,NameClass,"Первое окно",_
WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,300,300,0,0,Hinst,0)

' Цикл сообщений
While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend

Как видите мы отловили сообщение WM_GETMINMAXINFO , вынули адрес из LPARAM и занесли этот адрес в заранее объявленную структурированную переменную minmax. Далее в каждое из полей структуры прописали нужные значения и вернули 0 как и прописано по спецификации к этому сообщению. Аналогичным образом можно установить значения в остальные поля структуры.

На этом все, всего доброго!

содержание | назад | вперед