Blog. Just Blog

Установка точного размера клиентской области окна

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Очередная тривиальная задача с нетривиальным решением и очередной пламенный привет разработчикам Windows. Начнем с задачи. Надо изменить размер окна таким образом, чтобы его клиентская область, то есть внутреннее содержимое без учета заголовка, рамок и полос прокрутки, стала четко заданной ширины и высоты. Создатели Windows, безусловно, позаботились о программистах, для решения этой задачи есть как минимум две функции - SetWindowPos и MoveWindow, все хорошо и замечательно, но... Как метко подметили в одном телесериале, все, что сказано до слова "но" - чушь собачья. Эти функции действительно выполняют свое предназначение, меняют размер окна, только применяют переданные им значения к внешней границе окна. При этом не учитываются ни визуальные темы, ни стили окна, ни наличие полос прокрутки, ни толщина рамки, короче, решения поставленной задачи "в лоб" дадут гарантированно неправильный результат.

Размеры областей окна
Размеры областей окна

К счастью, в Windows имеется вспомогательная функция AdjustWindowRectEx, которая используется как раз для подобных ситуаций. Он вычисляет фактические размеры окна, которые соответствуют заданным размерам его клиентской области. И вот здесь как раз учитываются стили, в том числе и расширенные, наличие у окна строки меню и установленная визуальная тема, но... Да-да, очередное "но". При расчете размеров никак не учитывается наличие в окне полос вертикальной и/или горизонтальной прокрутки. Они могут входить в размер клиентской области, а могут и не входить, в зависимости от задачи.

После обобщения всех исходных данных, у меня получилась вот такая универсальная функция. Она изменяет размер указанного окна под заданные размеры его клиентской области как с учетом полос прокрутки, так и без этого.
  1. ;------------------------------------------------------------------------
  2. ; Установить размеры окна с учетом точных размеров клиентской области
  3. ;------------------------------------------------------------------------
  4. ; hWnd    - хэндл окна
  5. ; dWidth  - ширина клиентской области
  6. ; dHeight - высота клиентской области
  7. ; dScroll - учитывать размеры полос прокрутки (1 = ДА, 0 = НЕТ)
  8. ;------------------------------------------------------------------------
  9. proc set_client_size hWnd:DWORD,dWidth:DWORD,dHeight:DWORD,dScroll:DWORD
  10.         locals
  11.                 coord RECT
  12.         endl
  13.  
  14.         pusha
  15.  
  16.         lea     esi,[coord]
  17.  
  18.         ; Заполнить начальные размеры окна
  19.         mov     [esi+RECT.left],0
  20.         mov     [esi+RECT.top],0
  21.         mov     eax,[dWidth]
  22.         mov     [esi+RECT.right],eax
  23.         mov     eax,[dHeight]
  24.         mov     [esi+RECT.bottom],eax
  25.  
  26.         ; Учитывать полосы прокрутки?
  27.         cmp     [dScroll],1
  28.         jne     .no_scroll
  29.  
  30.         ; Получить стиль окна
  31.         invoke  GetWindowLong,[hWnd],GWL_STYLE
  32.         mov     ebx,eax
  33.         ; Проверить наличие горизонтальной прокрутки
  34.         test    ebx,WS_HSCROLL
  35.         jz      @f
  36.         invoke  GetSystemMetrics,SM_CXHSCROLL
  37.         add     [esi+RECT.right],eax
  38. @@:
  39.         ; Проверить наличие вертикальной прокрутки
  40.         test    ebx,WS_VSCROLL
  41.         jz      .no_scroll
  42.         invoke  GetSystemMetrics,SM_CYVSCROLL
  43.         add     [esi+RECT.bottom],eax
  44. .no_scroll:
  45.         ; Подготовить данные для корректировки
  46.         invoke  GetWindowLong,[hWnd],GWL_EXSTYLE
  47.         push    eax
  48.  
  49.         ; Проверить наличие меню и его видимость
  50.         invoke  GetMenu,[hWnd]
  51.         or      eax,eax
  52.         jz      @f
  53.         invoke  GetMenuItemCount,eax
  54.         or      eax,eax
  55.         jz      @f
  56.         mov     eax,1
  57. @@:
  58.         push    eax
  59.  
  60.         invoke  GetWindowLong,[hWnd],GWL_STYLE
  61.         push    eax
  62.  
  63.         ; Скорректировать размер окна
  64.         invoke  AdjustWindowRectEx,esi
  65.  
  66.         ; Установить новые размеры окна с учетом всего
  67.         push    SWP_NOZORDER+SWP_NOMOVE
  68.         mov     eax,[esi+RECT.bottom]
  69.         sub     eax,[esi+RECT.top]
  70.         push    eax
  71.         mov     eax,[esi+RECT.right]
  72.         sub     eax,[esi+RECT.left]
  73.         push    eax
  74.         invoke  SetWindowPos,[hWnd],NULL,NULL,NULL
  75.  
  76.         popa
  77.         ret
  78. endp
Вызов будет примерно такой:
  1.         ; Установить точный размер окна без учета полос прокрутки
  2.         stdcall set_client_size,[hwnddlg],500,200,0
  3.         ...
  4.         ...
  5.         ; Установить точный размер окна с учетом полос прокрутки
  6.         stdcall set_client_size,[hwnddlg],300,300,1
Меняется только размер окна, его положение на экране остается без изменений. При необходимости перемещения окна можете дополнить функцию соответствующим кодом или воспользоваться чем-нибудь готовым.

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Precision.Window.Size.Demo.zip (2,671 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 925 | Комментариев: 2

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (23.06.2022 в 12:56):
Да, интересное замечание. Добавил, спасибо! Архив обновлен.
toor (23.06.2022 в 12:25):
Меню может присутствовать, но быть невидно, например если у него нет ни одного элемента. С практической точки зрения это конечно ерунда (зачем делать меню без элементов?), но чисто технически такое может быть. Чтобы процедура работала точнее, проверку наличия меню лучше выполнять так:

b := (GetMenu(Handle) > 0) and (GetMenuItemCount(GetMenu(Handle)) > 0)

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0052 сек.) / Память: 4.5 Mb
Наверх