五种情况下会刷新控件状态(刷新所有子FWinControls的显示)——从DFM读取数据时新增加子控件时重新创建当前控件的句柄时设置父控件时显示状态被改变时
Posted 朝闻道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五种情况下会刷新控件状态(刷新所有子FWinControls的显示)——从DFM读取数据时新增加子控件时重新创建当前控件的句柄时设置父控件时显示状态被改变时相关的知识,希望对你有一定的参考价值。
五种情况下会刷新控件状态(刷新控件状态才能刷新所有子FWinControls的显示):
在TWinControls.PaintControls中,对所有FWinControls只是重绘了边框,而没有整个重绘这些FWinControl子控件。那么什么时候才整个重绘全部FWinControls呢?这时候,就不是一个单纯的WM_PAINT来解决控件重绘的问题了,而是这个TWinControl.UpdateShowing函数:
procedure TWinControl.UpdateShowing; var ShowControl: Boolean; I: Integer; begin ShowControl := (FVisible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and not (csReadingState in ControlState); if ShowControl then begin if FHandle = 0 then CreateHandle; if FWinControls <> nil then for I := 0 to FWinControls.Count - 1 do TWinControl(FWinControls[I]).UpdateShowing; end; if FHandle <> 0 then if FShowing <> ShowControl then begin FShowing := ShowControl; try Perform(CM_SHOWINGCHANGED, 0, 0); except FShowing := not ShowControl; raise; end; end; end;
那么什么时候才会调用TWinControl.UpdateShowing;呢?回答是,就一种情况下:当控件状态改变的时候:
procedure TWinControl.UpdateControlState; var Control: TWinControl; begin Control := Self; while Control.Parent <> nil do begin Control := Control.Parent; if not Control.Showing then Exit; end; if (Control is TCustomForm) or (Control.FParentWindow <> 0) then UpdateShowing; // 必须有父窗口,才给显示。TCustomForm那已经是顶级窗口 end;
那么什么时候算控件状态改变?回答是,一共五种情况:从DFM读取数据时、新增加子控件时、重新创建当前控件的句柄时、设置父控件时、显示状态被改变时:
// 情况一: procedure TWinControl.ReadState(Reader: TReader); begin DisableAlign; try inherited ReadState(Reader); finally EnableAlign; end; FixupTabList; if FParent <> nil then Perform(CM_PARENTCTL3DCHANGED, 0, 0); UpdateControlState; end; // 情况二: procedure TWinControl.InsertControl(AControl: TControl); begin AControl.ValidateContainer(Self); Perform(CM_CONTROLLISTCHANGE, Integer(AControl), Integer(True)); Insert(AControl); if not (csReading in AControl.ComponentState) then begin AControl.Perform(CM_PARENTCOLORCHANGED, 0, 0); AControl.Perform(CM_PARENTFONTCHANGED, 0, 0); AControl.Perform(CM_PARENTSHOWHINTCHANGED, 0, 0); AControl.Perform(CM_PARENTBIDIMODECHANGED, 0, 0); if AControl is TWinControl then begin AControl.Perform(CM_PARENTCTL3DCHANGED, 0, 0); UpdateControlState; end else if HandleAllocated then AControl.Invalidate; AlignControl(AControl); end; Perform(CM_CONTROLCHANGE, Integer(AControl), Integer(True)); end; // 情况三: procedure TWinControl.CMRecreateWnd(var Message: TMessage); var WasFocused: Boolean; begin WasFocused := Focused; DestroyHandle; UpdateControlState; if WasFocused and (FHandle <> 0) then Windows.SetFocus(FHandle); end; // 情况四: procedure TWinControl.SetParentWindow(Value: HWnd); begin if (FParent = nil) and (FParentWindow <> Value) then begin if (FHandle <> 0) and (FParentWindow <> 0) and (Value <> 0) then begin FParentWindow := Value; Windows.SetParent(FHandle, Value); if (Win32MajorVersion >= 5) and (Win32Platform = VER_PLATFORM_WIN32_NT) then Perform(WM_CHANGEUISTATE, MakeWParam(UIS_INITIALIZE, UISF_HIDEACCEL or UISF_HIDEFOCUS), 0); end else begin DestroyHandle; FParentWindow := Value; end; UpdateControlState; end; end; // 情况五: procedure TWinControl.CMVisibleChanged(var Message: TMessage); begin if not FVisible and (Parent <> nil) then RemoveFocus(False); if not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle) then UpdateControlState; end;
但是还是有个问题,在TWinControl.UpdateShowing里执行了CM_SHOWINGCHANGED消息,也就是:
procedure TWinControl.CMShowingChanged(var Message: TMessage); const ShowFlags: array[Boolean] of Word = ( SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_HIDEWINDOW, SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_SHOWWINDOW); begin SetWindowPos(FHandle, 0, 0, 0, 0, 0, ShowFlags[FShowing]); end;
可是SetWindowPos会触发WM_PAINT消息吗?看MSDN没有这么说啊,那么显示所有WinControl子控件和调用它的WM_PAINT真正自绘,两者怎样才能联系起来呢?
以上是关于五种情况下会刷新控件状态(刷新所有子FWinControls的显示)——从DFM读取数据时新增加子控件时重新创建当前控件的句柄时设置父控件时显示状态被改变时的主要内容,如果未能解决你的问题,请参考以下文章