在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一直对这两个消息的关系不是太了解,借重新深刻学习windows编程的机会研究一番。 1)当窗口从无效变为有效时,比方将部分覆盖的窗口恢复时会重绘窗口时:程序首先会通过发送其他消息调用DefWindowProc,它内部会发送WM_ERASEBKGND消息,然后才会发送WM_PAINT消息,而且不经过消息队列(笔记:这结论从而何来?)。用Delphi的代码当场验证: procedure TWinControl.WMSize(var Message: TWMSize); begin UpdateBounds; // 类函数 inherited; Realign; // 类函数 if not (csLoading in ComponentState) then Resize; // 类函数,简单调用程序员事件 end; procedure TWinControl.WMMove(var Message: TWMMove); begin inherited; UpdateBounds; end; 果然发现两个inherited,会把WM_SIZE和WM_MOVE消息发送到DefWindowProc,从而触发WM_ERASEBKGND。 2)诸如UpdateWindow也会先调用WM_ERASEBKGND消息的处理过程,然后才会调用WM_PAINT消息的处理过程。 procedure TControl.Update; begin if Parent <> nil then Parent.Update; end; procedure TControl.Repaint; var DC: HDC; begin if (Visible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and (Parent <> nil) and Parent.HandleAllocated then if csOpaque in ControlStyle then begin DC := GetDC(Parent.Handle); try IntersectClipRect(DC, Left, Top, Left + Width, Top + Height); Parent.PaintControls(DC, Self); finally ReleaseDC(Parent.Handle, DC); end; end else begin Invalidate; Update; end; end; procedure TWinControl.Update; begin if HandleAllocated then UpdateWindow(FHandle); // API,只有这一处使用这个API函数,但是有另外三处会来调用当前函数,因为这有这个API函数可以真正做到立刻刷新 end; procedure TWinControl.Repaint; begin Invalidate; Update; end; 3)当调用InvalidateRect时(三个参数:hWnd,RECT,bErase),如果bErase参数为TRUE时会先调用WM_ERASEBKGND消息的处理过程。为False时,只会发送WM_PAINT消息到队列。所以InvalidateRect不是立刻调用WM_PAINT消息的处理过程,他只是给程序窗口增加一个更新区域(参阅MSDN)。 Delphi里一共有三处调用这个API函数: procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean); var Rect: TRect; begin if (IsVisible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and (Parent <> nil) and Parent.HandleAllocated then begin Rect := BoundsRect; InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or // 注意,处理的是父窗口的句柄 (csOpaque in Parent.ControlStyle) or BackgroundClipped)); end; end; procedure TWinControl.CMInvalidate(var Message: TMessage); var I: Integer; begin if HandleAllocated then begin if Parent <> nil then Parent.Perform(CM_INVALIDATE, 1, 0); if Message.WParam = 0 then begin InvalidateRect(FHandle, nil, not (csOpaque in ControlStyle)); // 注意,处理的是自己的句柄,nil表示自己的整个客户区都重绘 { Invalidate child windows which use the parentbackground when themed } if ThemeServices.ThemesEnabled then for I := 0 to ControlCount - 1 do if csParentBackground in Controls[I].ControlStyle then Controls[I].Invalidate; end; end; end; procedure TWinControl.InvalidateFrame; var R: TRect; begin R := BoundsRect; InflateRect(R, 1, 1); InvalidateRect(Parent.FHandle, @R, True); // 也是重绘自己,但却是无条件重绘,而不需要考虑是否透明之类的问题,因为设计期也要绘制边框。其次,边框绘制出来以后,其内容可以被覆盖,因此不用管内容是否需要使用Theme end; 另外关于WM_ERASEBKGND处理函数的返回值,如果处理WM_ERASEBKGND消息时返回FALSE,BeginPaint标记pt.fErase为TRUE, 如果处理WM_ERASEBKGND时返回TRUE,BeginPaint标记pt.fErase为FALSE。注意,Delphi就是这样做的,意思就是Delphi已经处理过了,不需要进一步处理。看这里: 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; 如果pt.fErase标记为TRUE,指示应用程序应该处理背景,但是应用程序不一定需要处理,pt.fErase只是作为一个标记(笔记:留给程序员的灵活性依然很强)。 http://www.aichengxu.com/view/2426114 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论