这个问题的确需要从源码级解答。<br>TControl 有一个私有的 SetVisible 方法和 Visible 属性。<br>private<br> FVisible: Boolean;<br> procedure SetVisible(Value: Boolean);<br>public // 公共属性<br> property Visible: Boolean read FVisible write SetVisible stored IsVisibleStored default True;<br> ...<br>end;<br>procedure TControl.SetVisible(Value: Boolean);<br>begin<br> if FVisible <> Value then<br> begin<br> VisibleChanging; // TCustomForm 重载该方法,防止改变 MDI Child 的 Visible,但是与本题无关<br> FVisible := Value;<br> Perform(CM_VISIBLECHANGED, Ord(Value), 0); // 关键是这句话<br> RequestAlign;<br> end;<br>end;<br><br>但是 TCustomForm 又重写了 SetVisible 方法和 Visible 属性,注意是重写不是重载:<br>private<br> procedure SetVisible(Value: Boolean);<br>protected // 保护属性<br> property Visible write SetVisible default False;<br> ...<br>end;<br>procedure TCustomForm.SetVisible(Value: Boolean);<br>begin<br> if fsCreating in FFormState then<br> if Value then<br> Include(FFormState, fsVisible) else<br> Exclude(FFormState, fsVisible)<br> else<br> begin<br> if Value and (Visible <> Value) then SetWindowToMonitor;<br> inherited Visible := Value; // 我们只关心这句话<br> end;<br>end;<br><br> 象这样的写法我还是第一次看见,不是重载虚函数,而是重写,不知道 Object Pascal 的<br>语法为什么允许这样的写法。但是先不管它。<br><br> 我们设置 Form.Visible ,其实首先调用的是 TCustomForm.SetVisible ,在这里,<br> inherited Visible := Value;<br>产生对 TControl.SetVisible 的调用,然后 Perform(CM_VISIBLECHANGED, Ord(Value), 0);<br>发出 CM_VISIBLECHANGED 消息,但是这个消息不是被 TControl.CMVisibleChanged 方法得到,<br> procedure CMVisibleChanged(var Message: TMessage); message CM_VISIBLECHANGED;<br>因为 TWinControl 截获了它,<br>procedure TWinControl.CMVisibleChanged(var Message: TMessage);<br>begin<br> if not FVisible and (Parent <> nil) then RemoveFocus(False);<br> if not (csDesigning in ComponentState) or<br> (csNoDesignVisible in ControlStyle) then <font color = #ff0000><strong>UpdateControlState</font></strong>; // 这里产生了对子控件的遍历刷新<br>end;<br><br>procedure TWinControl.UpdateControlState;<br>var<br> Control: TWinControl;<br>begin<br> Control := Self;<br> while Control.Parent <> nil do<br> begin<br> Control := Control.Parent;<br> if not Control.Showing then Exit;<br> end;<br> // 上面的代码是找到本控件的最上级父亲,赋给 Control 变量<br> if (Control is TCustomForm) or (Control.FParentWindow <> 0) then <font color = #ff0000><strong>UpdateShowing</font></strong>; ;<br>end;<br><br>procedure TWinControl.UpdateShowing;<br>var<br> ShowControl: Boolean;<br> I: Integer;<br>begin<br> ShowControl := (FVisible or (csDesigning in ComponentState) and<br> not (csNoDesignVisible in ControlStyle)) and<br> not (csReadingState in ControlState);<br> if ShowControl then // 如果需要显示<br> begin<br> if FHandle = 0 then CreateHandle;<br> if FWinControls <> nil then<br> for I := 0 to FWinControls.Count - 1 do // 遍历控件的所有直接儿子并刷新,注意这里类似递归,其实不是递归<br> TWinControl(FWinControls).<font color = #ff0000><strong>UpdateShowing</font></strong>;<br> end;<br> if FHandle <> 0 then<br> if FShowing <> ShowControl then<br> begin<br> FShowing := ShowControl;<br> try<br> Perform(CM_SHOWINGCHANGED, 0, 0); // 通过发送 CM_SHOWINGCHANGED 消息刷新控件<br> except<br> FShowing := not ShowControl;<br> raise;<br> end;<br> end;<br>end;<br><br>procedure TWinControl.CMShowingChanged(var Message: TMessage);<br>const<br> ShowFlags: array[Boolean] of Word = (<br> SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_HIDEWINDOW,<br> SWP_NOSIZE + SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_SHOWWINDOW);<br>begin<br> SetWindowPos(FHandle, 0, 0, 0, 0, 0, ShowFlags[FShowing]); // 真正显示各个控件<br>end;<br><br> 由于在处理 CM_VISIBLECHANGED 消息的时候需要判断 FVisible ,如果 FVisible = False<br>将不会继续处理,但是 FVisible 是 TControl 的私有变量,我想了一下,也没有别的方法<br>直接存取 FVisible ,所以提出了上面的办法,仍然通过 Visible 属性去设置它,好处就是<br>不会闪烁。