Blog. Just Blog

Как сделать ProgressBar с надписью

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Не перестаю удивляться, почему разработчики Microsoft с самого начала не реализовали "из коробки" очевиднейшие решения. Например, есть хороший и удобный элемент диалоговых окон - Progress Bar. Легко реализовать, удобно управлять, но по какой-то причине нет никаких инструментов, чтобы системными средствами наложить на индикатор прогресса какую-нибудь надпись типа "42% завершено". Градиентный ProgressBar мы уже делали, настала очередь реализовать ProgressBar с надписью.

ProgressBar с надписью
ProgressBar с надписью

Использовать стандартный элемент msctls_progress32 не будем, вместо него возьмем элемент EDIT или STATIC, как и в предыдущем примере. Полоса индикатора и надпись реализуется путем рисования прямо на канве элемента диалогового окна, которое выделено под индикатор.

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

Взаимодействие основного потока приложения и кастомного индикатора реализуется через субклассирование. Установка обработчика ничем не отличается от десятка других примеров, которые мы ранее разбирали.
  1.         ; Субклассирование ProgressBar
  2.         invoke  GetDlgItem,[hwnddlg],ID_PROGRESS
  3.         mov     ebx,eax
  4.  
  5.         ; Установить наш собственный обработчик
  6.         invoke  SetWindowLong,ebx,GWL_WNDPROC,ProgressWindowProc
  7.         ; Сохранить хэндл предыдущего обработчика
  8.         mov     [OldProc],eax
Процедура обработчика заточена на перехват сообщения WM_PAINT, при поступлении которого рисуется все содержимое ProgressBar. Главная особенность заключается в том, что сперва весь элемент заливается фоновым цветом, на нем рисуется текст, а затем с помощью функции SelectClipRgn от всей площади элемента выделяется участок, на котором выполняется рисование полосы прогресса и дублируется тот же текст, но уже другим цветом. При таком способе незадействованная часть остается без изменений. Ширина полосы прогресса вычисляется в процентном значении от ширины всей площади элемента.
  1. ;------------------------------------------------
  2. ; Субклассированный обработчик
  3. ;------------------------------------------------
  4. proc ProgressWindowProc hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
  5.         locals
  6.                 text     dd ?
  7.                 ps       PAINTSTRUCT
  8.                 hDC      dd ?
  9.                 rcClient RECT
  10.         endl
  11.  
  12.         cmp     [uMsg],WM_PAINT
  13.         je      .wm_paint
  14. .char_ok:
  15.         ; Передать управление предыдущему обработчику
  16.         invoke  CallWindowProc,[OldProc],[hEdit],[uMsg],\
  17.                 [wParam],[lParam]
  18.         ret
  19. .wm_paint:
  20.         mov     ebx,[hEdit]
  21.         ; Размеры окна
  22.         lea     edi,[rcClient]
  23.         invoke  GetClientRect,ebx,edi
  24.  
  25.         ; Получить текст из свойств
  26.         invoke  GetProp,ebx,szProp
  27.         mov     [text],eax
  28.  
  29.         ; Начать рисование
  30.         lea     eax,[ps]
  31.         invoke  BeginPaint,ebx,eax
  32.         mov     [hDC],eax
  33.  
  34.         ; Шрифт
  35.         invoke  SelectObject,[hDC],[hFont]
  36.         invoke  SetBkMode,[hDC],TRANSPARENT
  37.  
  38.         ; Отрисовать общий фон
  39.         invoke  GetSysColorBrush,COLOR_WINDOW
  40.         invoke  FillRect,[hDC],edi,eax
  41.  
  42.         ; Текст для отрисовки есть?
  43.         cmp     [text],0
  44.         je      @f
  45.         invoke  GetSysColor,COLOR_WINDOWTEXT
  46.         invoke  SetTextColor,[hDC],eax
  47.         invoke  DrawText,[hDC],buff,-1,edi,\
  48.                 DT_NOPREFIX+DT_SINGLELINE+DT_VCENTER+DT_CENTER
  49. @@:
  50.         ; Вычислить размеры области для заливки
  51.         invoke  GetProp,ebx,szPerc
  52.         mov     ecx,eax
  53.         mov     eax,[edi+RECT.right]
  54.         sub     eax,[edi+RECT.left]
  55.         xor     edx,edx
  56.         mul     ecx
  57.         mov     ecx,100
  58.         div     ecx
  59.         ; Позиция выполненного прогресса
  60.         add     eax,[edi+RECT.left]
  61.  
  62.         ; Наложить выполненный прогресс
  63.         invoke  CreateRectRgn,[edi+RECT.left],[edi+RECT.top],\
  64.                 eax,[edi+RECT.bottom]
  65.         invoke  SelectClipRgn,[hDC],eax
  66.         invoke  GetSysColorBrush,COLOR_HIGHLIGHT
  67.         invoke  FillRect,[hDC],edi,eax
  68.  
  69.         ; Текст для отрисовки есть?
  70.         cmp     [text],0
  71.         je      @f
  72.         invoke  GetSysColor,COLOR_HIGHLIGHTTEXT
  73.         invoke  SetTextColor,[hDC],eax
  74.         invoke  DrawText,[hDC],buff,-1,edi,\
  75.                 DT_NOPREFIX+DT_SINGLELINE+DT_VCENTER+DT_CENTER
  76. @@:
  77.         lea     eax,[ps]
  78.         invoke  EndPaint,[hDC],eax
  79.  
  80.         invoke  ReleaseDC,ebx,[hDC]
  81.  
  82.         xor     eax,eax
  83.         ret
  84. endp
  85.  
  86. szProp  db 'text',0
  87. szPerc  db 'percent',0
Я использую системные цвета, но никто не мешает вам заменить их по своему усмотрению. Шрифт настраивается в основном приложении, накладываемый текст и значение процентов передается через свойства функцией SetProp, что позволяет использовать одновременно несколько независимых индикаторов. В принципе, точно так же через свойства можно передавать шрифты и цвета, но это все тоже в зависимости от ваших задач.
  1.         ; Шрифт текста
  2.         invoke  CreateFont,32,12,0,0,FW_BOLD,FALSE,FALSE,FALSE,\
  3.                 ANSI_CHARSET,OUT_RASTER_PRECIS,\
  4.                 CLIP_DEFAULT_PRECIS,PROOF_QUALITY,\
  5.                 FIXED_PITCH+FF_DONTCARE,szFont
  6.         mov     [hFont],eax
  7.  
  8.         ; Хэндл ProgressBar
  9.         invoke  GetDlgItem,[hwnddlg],ID_PROGRESS
  10.         mov     ebx,eax
  11.  
  12.         ; Сформировать строку для отрисовки
  13.         invoke  wsprintf,buff,szText,[percent]
  14.         add     esp,12
  15.         ; Назначить строку свойством ProgressBar
  16.         invoke  SetProp,ebx,szProp,buff
  17.         ; Назначить значение процента свойством ProgressBar
  18.         invoke  SetProp,ebx,szPerc,[percent]
  19.  
  20.         ; Перерисовать содержимое окна
  21.         invoke  InvalidateRect,ebx,0,FALSE
Непосредственно обновление ProgressBar выполняется при помощи функции InvalidateRect, ее надо вызывать сразу после установки новых значений в свойствах.

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

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

ProgressBar.with.Text.Demo.zip (3,675 bytes)


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

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

Комментарии

Отзывы посетителей сайта о статье
Сергей Озеров (16.05.2022 в 11:57):
Утащил себе,буду изучать

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

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

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