Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Такая вот загадка...


Автор: Limonadni Joe 19.6.2005, 22:39
Посмотрим на код обработчика события WM_PAINT в классе TWinControl:

Код

  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
>>    inherited   <<
    else
      PaintHandler(Message);
  end
  else
  ...

На сколько я смог отследить, именно за счёт этого inherited рисуется фон компонента. А загадка в том, что обработчика события WM_PAINT у класса TControl нет, хотя именно на него ссылается inherited. Как такое может быть? Может я что-то путаю? Или кривыми руками искал событие?

Помогите разобраться откуда берётся серый фон у TWinControl. У компонента-брата TGraphicControl этого фона нет, хотя он тоже наследует от TControl.

Автор: Yanis 19.6.2005, 23:11
Inherited говорит, что сначала вступает в работу переопределённый метод родителя.

Автор: p0s0l 20.6.2005, 01:19
В случае с обработчиками сообщений (message-методы), если в предках не находится обаботчика нужного сообщения (в данном случае WM_PAINT нет у предков TControl..TObject), то вызывается виртуальный метод DefaultHandler...
А идейное отличие TControl от TWinControl заключается в том, что TWinControl имеет своё окно, свой DC, может содержать дочерние контролы. Тогда как TGraphicControl рисует не на своём окне (которого у него нет вообще), а на окне родителя (Parent).


Автор: Limonadni Joe 20.6.2005, 14:37
Цитата
В случае с обработчиками сообщений (message-методы), если в предках не находится обаботчика нужного сообщения (в данном случае WM_PAINT нет у предков TControl..TObject), то вызывается виртуальный метод DefaultHandler...

Спасибо, не знал. Но вот что получается. Это код метода DefaultHandler для TControl:
Код

procedure TControl.DefaultHandler(var Message);
var
  P: PChar;
begin
  with TMessage(Message) do
    case Msg of
      WM_GETTEXT:
        begin
          if FText <> nil then P := FText else P := '';
          Result := StrLen(StrLCopy(PChar(LParam), P, WParam - 1));
        end;
      WM_GETTEXTLENGTH:
        if FText = nil then Result := 0 else Result := StrLen(FText);
      WM_SETTEXT:
        begin
          P := StrNew(PChar(LParam));
          StrDispose(FText);
          FText := P;
          SendDockNotification(Msg, WParam, LParam);
        end;
    end;
end;

и в нём, как видим, нет упоминания о WM_PAINT. Где же этот WM_PAINT тогда обрабатывается?

Автор: p0s0l 20.6.2005, 18:48
DefaultHandler - метод виртуальный smile
Ищи не в TControl, а например, в TWinControl или TGraphicControl, смотря какой у тебя созданный объект
Но не стоит надеятся найти что-то интересное, т.к. оба этих класса лишь основа для создания более сложных классов.
TGraphicControl в обработчике WM_PAINT вызывает виртуальный метод Paint, который у самого TGraphicControl абсолютно ничего не делает, а в наследниках, например, в TImage выводит картинку.
TWinControl в обработчике WM_PAINT вызывает стандартную оконную процедуру, которая обрабатывает это сообщение (заливая фоном)...

Автор: Rennigth 20.6.2005, 19:56
Цитата

Помогите разобраться откуда берётся серый фон у TWinControl

а он не помоему не серый, он по умолчанию ничего не рисует на себе, прозрачный он.

Автор: Limonadni Joe 21.6.2005, 09:51
p0s0l, посмотрел DefaultHandler у TWinControl, проследил цепочку кода, но так и не нашёл места где ловится WM_PAINT smile. Вообще, поиск в юните Controls по строке "WM_PAINT:" ничего не даёт. Что же это такое получается?

Rennigth, ан-нет, я специально проверил: создал чистый компонент на основе TWinControl, кинул на форму и положил под него TextBox, и на TextBox'е покорно отобразилась часть серого фона от моего контрола. Хотя поначалу я тоже думал, что он прозрачный, пока не сделал ему Bring to Front smile .

Автор: p0s0l 21.6.2005, 15:35
Зайди в Project-Options->Complier, поставь Use Debug DCUs
Теперь, сделай так:
Код
wc : TWinControl;
...
wc.Perform(WM_PAINT, 0, 0);
Жми F7 на Perform и смотри что куда идёт...


Автор: Limonadni Joe 21.6.2005, 17:10
Дошёл до вызова функции CallWindowProc:
Код
Result := CallWindowProc(FDefWndProc, FHandle, Msg, WParam, LParam);
а дальше - ни-ни, это - WinAPI. Подозрение именно на эту строчку, т.к. всё, что я встречал на пути следования отладчика - это отказы "согласиться" хоть на один if или else, и больше ничего.

Что же получается - серый фон рисует винда? Хотя, это вполне вероятно. Ведь у некоторых контролов (табов, например) градиентный фон, не будет же Delphi заниматься его прорисовкой. Мои догадки верны?

Автор: Girder 21.6.2005, 17:20
Код
procedure TWinControl.WMPaint(var Message: TWMPaint);
var
  DC, MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  PS: TPaintStruct;
begin
  if not FDoubleBuffered or (Message.DC <> 0) then
  begin
    if not (csCustomPaint in ControlState) and (ControlCount = 0) then
      inherited
    else
      PaintHandler(Message);
  end
  else
  begin
    DC := GetDC(0);
    MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, ClientRect.Bottom);
    ReleaseDC(0, DC);
    MemDC := CreateCompatibleDC(0);
    OldBitmap := SelectObject(MemDC, MemBitmap);
    try
      DC := BeginPaint(Handle, PS);
      Perform(WM_ERASEBKGND, MemDC, MemDC);
      Message.DC := MemDC;
      WMPaint(Message);
      Message.DC := 0;
      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
      EndPaint(Handle, PS);
    finally
      SelectObject(MemDC, OldBitmap);
      DeleteDC(MemDC);
      DeleteObject(MemBitmap);
    end;
  end;
end;

Автор: Limonadni Joe 21.6.2005, 17:38
Girder, это ты к чему написал? Я вроде и сам видел этот код.

Автор: Rrader 21.6.2005, 18:17
Цитата

Что же получается - серый фон рисует винда? Хотя, это вполне вероятно. Ведь у некоторых контролов (табов, например) градиентный фон, не будет же Delphi заниматься его прорисовкой. Мои догадки верны?

Что касается табов, то они полностью отрисовываются Windows, в Delphi даже событий перерисовки у них нет. Думаю, что твои догадки насчёт TWinControl'а верны. smile

Автор: p0s0l 21.6.2005, 20:06
Цитата(Limonadni @ 21.6.2005, 17:10)
Что же получается - серый фон рисует винда? Хотя, это вполне вероятно. Ведь у некоторых контролов (табов, например) градиентный фон, не будет же Delphi заниматься его прорисовкой. Мои догадки верны?
Надо было читать внимательнее:
Цитата(p0s0l @ 20.6.2005, 18:48)
TWinControl в обработчике WM_PAINT вызывает стандартную оконную процедуру, которая обрабатывает это сообщение (заливая фоном)...

smile

Автор: Limonadni Joe 21.6.2005, 20:16
p0s0l, да, спасибо. Просто я тогда не имел богатого представления о том, что такое стандартная оконная процедура и что (как я понял?) принадлежит она целиком и полностью винде.

Автор: Girder 22.6.2005, 00:02
Цитата(Limonadni @ 21.6.2005, 18:38)
Girder, это ты к чему написал? Я вроде и сам видел этот код.
Ты же спрашивал где обрабатывается WM_Paint smile

По сабжу: Глянул исходники Controls... т.в. там регистрируется класс окна с _WNDCLASSEX.hbrBackground равным 0... а енто значит:
Цитата
When this member is NULL, an application must paint its own background whenever it is requested to paint in its client area. To determine whether the background must be painted, an application can either process the WM_ERASEBKGND message or test the fErase member of the PAINTSTRUCT structure filled by the BeginPaint function.


Т.е. вот оно (FillRect):
Код
procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  with ThemeServices do
  if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
    begin
      { Get the parent to draw its background into the control's background. }
      DrawParentBackground(Handle, Message.DC, nil, False);
    end
    else
    begin
      { Only erase background if we're not doublebuffering or painting to memory. }
      if not FDoubleBuffered or
         (TMessage(Message).wParam = TMessage(Message).lParam) then
        FillRect(Message.DC, ClientRect, FBrush.Handle);
    end;

  Message.Result := 1;
end;

Автор: p0s0l 22.6.2005, 01:14
Мда, мне самому надо было продебаггить smile, забыл, попутал себя и других на счет стирания фона, хотя это очевидно :-/...

Автор: Limonadni Joe 22.6.2005, 12:30
Так... ну вроде разобрались откуда берётся фон компонента. А как сделать так, чтобы фон компонента был прозрачным? Так, как это реализовано, например, в TImage. Причём предок моего компонента - это не TGraphicControl, а TWinControl.

Я пробовал перекрыть обратчик события WM_ERASEBKGND пустым методом, но тогда вместо компонента отображается то, что находиться аж под самой фомой - редактор кода smile .

Никак не могу разобраться smile . Пробовал смотреть, как прозрачность реализована у TToolBar (наследник TWinControl), но так ничего путного и не нашёл. (Кстати, так и не обнаружил, как свойство TToolBar.Transparent отражается на практике. Может я и не то вовсе исследовал?)

Обсуждение можно продолжить в http://forum.vingrad.ru/index.php?showtopic=55913 топике. Только, пожалуйста, не надо корректировать то, что дано по условию.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)