包含TWebBrowser时,TPanel不会自动调整大小 [英] TPanel does not AutoSize when containing a TWebBrowser
问题描述
我找到了),面板将不会自动调整大小:
必须是TWebBrowser的错
必须自动调整 TWebBrowser
VCL包装程序错误所需的一些VCL管道。我需要知道XE6中损坏的内容及其解决方法。
我遇到了类似的问题。
通过在
TWebBrowser $ c下方放置
TPanel
来解决此问题$ c>,然后将网络浏览器与alClient
对齐。
作为修复,我对解决方法的兴趣不大-我可以将其添加到其他VCL源代码修复程序中。实际上,由于我使用了经过改进的 TEmbeddedWB
控件,因此可以在其中进行修复。使得 TWebBrowser
损坏。
复制步骤
Form1.pas :
个单位Unit1;
界面
使用
Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,
Vcl.Controls,Vcl.Forms,Vcl.Dialogs,Vcl.ComCtrls,Vcl.ExtCtrls,Vcl.OleCtrls,SHDocVw;
类型
TForm1 = class(TForm)
Panel1:TPanel;
WebBrowser1:TWebBrowser;
私人
{私人声明}
公共
{公开声明}
结尾;
var
Form1:TForm1;
实施
{$ R * .dfm}
结尾。
Form1.dfm :
对象Form1:TForm1
左= 0
顶部= 0
标题='Form1'
ClientHeight = 248
ClientWidth = 373
颜色= clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name =' Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Panel1:TPanel
Left = 32
顶部= 32
宽度= 209
高度= 97
AutoSize =真
BevelOuter = bvNone
颜色= clLime
ParentBackground = False
TabOrder = 0
对象WebBrowser1:TWebBrowser
左= 0
顶部= 0
宽度= 190
高度= 161
ParentShowHint = False
ShowHint = False
TabOrder = 0
ControlData = {
4C00000 023260000E40500000000000000000000000000000000000000000000000000
000000004C000000000000000000000001000000E0D057007335CF11AE690800
2B2E126208000000000000004C0000000114020000000000C000000000000046
8000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000100000000000000000000000000b pre>
解决方案问题是由两次回归引起的。
- 一个在 TWinControl.AlignControls
中
- 通过更改 TOleControl.SetBounds 进行更改,尽管实际的错误在 TWinControl.WMWindowPosChanged 中。
什么都不会自动调整大小错误
我在Stackoverflow问题包含TPanel时,TPanel无法自动调整大小:
过程TWinControl.AlignControls(AControl :TControl; var Rect:TRect);
开始
//...snip
//如果显示并显示(((FScalingFlags中的sfWidth)或(FScalingFlags中的sfHeight))然后应用
DoAdjustSize;
// ...剪切
结束;
此处的错误是它不会调用 DoAdjustSize
除非存在 sfWidth 或 sfHeight 缩放标志。
解决方法是不要试图超越自己,而 DoAdjustSize
不管:
程序TWinControl.AlignControls(AControl:TControl; var Rect:TRect);
开始
// ...剪切
//应用任何约束
// QC125995:不要看缩放标志来决定是否应该调整大小
如果显示{和(((FScalingFlags中的sfWidth)或(FScalingFlags中的sfHeight)))},则
DoAdjustSize;
// ...剪断
结束;
在调整大小时不会自动调整大小错误
先前的修复程序在包含子级 TControl 或 TWinControl 时使面板具有AutoSize。但是,当面板包含 TOleControl 时,还有另一个错误。该错误是在Delphi XE中引入的。与上述错误不同,该错误是由于有人认为自己很聪明而导致的,因此此错误更加微妙。
调整 TOleControl 的大小后,调用 SetBounds 方法。这是原始的功能代码:
过程TOleControl.SetBounds(ALeft,ATop,AWidth,AHeight:Integer);
如果((AWidth<>宽度)和(Width> 0))或((AHeight<> Height)和(Height> 0))然后
开始
开始
//...snip:也许调整AWidth和AHeight
end;
继承了SetBounds(ALeft,ATop,AWidth,AHeight);
结尾;
在XE2时间范围内,代码已更改为,以便通知基础 Ole 控制范围即将改变:
过程TOleControl.SetBounds(ALeft,ATop,AWidth,AHeight:Integer) ;
var
LRect:TRect;
如果((AWidth<>宽度)和(Width> 0))或((AHeight<> Height)和(Height> 0))然后
开始
开始
// ...剪切:也许要调整AWidth和AHeight
//通知基础Ole控件,如果FOleInplaceObject<>的范围即将更改
。 nil然后
开始
LRect:= Rect(Left,Top,Left + AWidth,Top + AHeight);
FOleInplaceObject.SetObjectRects(LRect,LRect);
结尾;
结尾;
继承了SetBounds(ALeft,ATop,AWidth,AHeight);
结尾;
作者不知道,这暴露了 TWinControl 中的错误。调用 IOleInPlaceObject.SetObjectRects
是Ole控件(例如Internet Explorer)转身并发送 WM_WindowPosChanged
消息。 TWinControl 中的 WMWindowPoschanged 处理程序无法正确处理该消息。
常规 SetBounds
方法正确调用时:
过程SetBounds;
开始
UpdateAnchorRules;
UpdateExplicitBounds;
RequestAlign; //我们需要
结尾的重要位置;
WMWindowPosChanged
方法仅调用:
程序WMWindowPosChanged;
开始
UpdateBounds; //只调用UpdateAnchorRules
end;
这意味着WinControl会调整其大小;
修复
修复方法是:
- 根本不要从SetBounds调用
IOleInPlaceObject.SetObjectRects
。 Delphi 5并没有这样做,而且效果很好
-
更改WMWindowPosChanged,以便它也调用 RequestAlign :
过程TWinControl.WMWindowPosChanged;
开始
UpdateBounds;
RequestAlign; //不要忘记自动调整父级的尺寸,因为我们正在改变后台尺寸(例如TOleControl)
end;
-
将UpdateBounds更改为也调用 RequestAlign :
过程TWinControl.UpdateBounds;
开始
UpdateAnchorRules;
// UpdateExplicitBounds; SetBounds称为此;我们为什么不打电话呢?
RequestAlign; //响应WM_WindowPosChanged
结尾;
我选择了第四个解决方案;
该错误是:
- WMWindowPosChanged 无法正确处理尺寸更改
- 但 SetBounds 可以
所以让我们先使用 SetBounds 。
利用 SetBounds 中的(大部分)正确的代码进行所有大小调整。然后,我们可以调用 SetObjectRects
。当 WMWindowPosChanged 收到其 WM_WindowPosChanging
消息时,它将无事可做-因此不会做错任何事。
tl; dr
过程TOleControl.SetBounds(ALeft,ATop,AWidth,AHeight:Integer);
var
LRect:TRect;
如果((AWidth<>宽度)和(Width> 0))或((AHeight<> Height)和(Height> 0))然后
开始
开始
// ...剪裁:也许用AWidth或AHeight摆弄
{已删除。调用* after * inheirted SetBounds
// //如果FOleInplaceObject<>通知基础Ole控件其边界将要更改
。 nil然后
开始
LRect:= Rect(Left,Top,Left + AWidth,Top + AHeight);
FOleInplaceObject.SetObjectRects(LRect,LRect);
结尾;}
结尾;
继承了SetBounds(ALeft,ATop,AWidth,AHeight);
//移动到* SetBounds之后*,我们需要SetBounds首先发生。
// TWinControl的WMWindowPosChanged无法正确地自动调整大小
// SetBounds可以。
//如果FOleInplaceObject<>通知基础Ole控件其边界已经要更改
。 nil然后
开始
LRect:= Rect(Left,Top,Left + AWidth,Top + AHeight);
FOleInplaceObject.SetObjectRects(LRect,LRect);
结尾;
结尾;
注意:任何公开发布的代码域。无需注明出处。
I've found a another regression between Delphi 5 and Delphi XE6.
I have a TPanel
that is set to AutoSize
itself to its contents (Panel is green):
When the TPanel
contains any other control, e.g. a TListView
, the panel will auto-size itself to the size of the contained listview:
But when the contained control is a TWebBrowser
(or the replacement TEmbeddedWB
), the panel will not auto-size:
Must be TWebBrowser's fault
There must be some VCL plumbing needed for auto-sizing that the TWebBrowser
VCL wrapper gets wrong. What i need to know what was broken in XE6 and the fix for it.
User user1611655 had a good workaround:
I had a similar problem.
It was solved by putting a TPanel
"underneath" the TWebBrowser
, and aligning the web browser to alClient
.
I'm less interested in a workaround, as a fix - I can add it to our other pile of VCL source fixes. In reality, since i use the much improved TEmbeddedWB
control, the fix can be put in there; leaving TWebBrowser
broken.
Steps to Reproduce
The Form1.pas:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.ExtCtrls, Vcl.OleCtrls, SHDocVw;
type
TForm1 = class(TForm)
Panel1: TPanel;
WebBrowser1: TWebBrowser;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
end.
The Form1.dfm:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 248
ClientWidth = 373
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 32
Top = 32
Width = 209
Height = 97
AutoSize = True
BevelOuter = bvNone
Color = clLime
ParentBackground = False
TabOrder = 0
object WebBrowser1: TWebBrowser
Left = 0
Top = 0
Width = 190
Height = 161
ParentShowHint = False
ShowHint = False
TabOrder = 0
ControlData = {
4C00000023260000E40500000000000000000000000000000000000000000000
000000004C000000000000000000000001000000E0D057007335CF11AE690800
2B2E126208000000000000004C0000000114020000000000C000000000000046
8000000000000000000000000000000000000000000000000000000000000000
00000000000000000100000000000000000000000000000000000000}
end
end
end
解决方案 The issue is caused by two regressions.
- One in in TWinControl.AlignControls
- The other was caused by a change put into TOleControl.SetBounds, although the actual bug is in TWinControl.WMWindowPosChanged.
The "Nothing autosizes ever" bug
The first bug i detailed in the Stackoverflow question TPanel does not AutoSize when containing a TPanel:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
begin
//...snip
// Apply any constraints
if Showing and ((sfWidth in FScalingFlags) or (sfHeight in FScalingFlags)) then
DoAdjustSize;
//...snip
end;
The bug here is that it will not call DoAdjustSize
unless either sfWidth or sfHeight scaling flags are present.
The fix is to not try to outsmart yourself, and DoAdjustSize
regardless:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
begin
//...snip
// Apply any constraints
//QC125995: Don't look to scaling flags to decide if we should adjust size
if Showing {and ((sfWidth in FScalingFlags) or (sfHeight in FScalingFlags))} then
DoAdjustSize;
//...snip
end;
The "Doesn't autosize on resize" bug
The previous fix makes the panel AutoSize when it contains child TControl or TWinControl. But there is another bug when the panel contains a TOleControl. The bug was introduced in Delphi XE. Unlike the above bug, caused by someone thinking they were being clever, this one is much more subtle.
When a TOleControl is resized, its SetBounds method is called. This is the original, functional, code:
procedure TOleControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
if ((AWidth <> Width) and (Width > 0)) or ((AHeight <> Height) and (Height > 0)) then
begin
//...snip: perhaps tweak AWidth and AHeight
end;
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
end;
In XE2 timeframe, the code was changed to so that it notifies the underlying Ole control that it's bounds are about to change:
procedure TOleControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
LRect: TRect;
begin
if ((AWidth <> Width) and (Width > 0)) or ((AHeight <> Height) and (Height > 0)) then
begin
//...snip: perhaps tweak AWidth and AHeight
//Notify the underlying Ole control that its bounds are about to change
if FOleInplaceObject <> nil then
begin
LRect := Rect(Left, Top, Left+AWidth, Top+AHeight);
FOleInplaceObject.SetObjectRects(LRect, LRect);
end;
end;
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
end;
Unbeknownst to the author, this exposes a bug in TWinControl. The problem with calling IOleInPlaceObject.SetObjectRects
is that it the Ole control (e.g. Internet Explorer) turns around and sends the WM_WindowPosChanged
message. The WMWindowPoschanged handler in TWinControl doesn't handle the message correctly.
While the regular SetBounds
method correctly calls:
procedure SetBounds;
begin
UpdateAnchorRules;
UpdateExplicitBounds;
RequestAlign; //the important one we need
end;
The WMWindowPosChanged
method only calls:
procedure WMWindowPosChanged;
begin
UpdateBounds; //which only calls UpdateAnchorRules
end;
This means that the WinControl adjusts its size; but its parent is never realigned to handle the new auto size.
The Fix
The fix is either:
- don't call
IOleInPlaceObject.SetObjectRects
from SetBounds at all. Delphi 5 didn't do it and it worked fine
change WMWindowPosChanged so that it also calls RequestAlign:
procedure TWinControl.WMWindowPosChanged;
begin
UpdateBounds;
RequestAlign; //don't forget to autosize our parent since we're changing our size behind our backs (e.g. TOleControl)
end;
change UpdateBounds to also call RequestAlign:
procedure TWinControl.UpdateBounds;
begin
UpdateAnchorRules;
//UpdateExplicitBounds; SetBounds calls this; why are we not calling it?
RequestAlign; //in response to WM_WindowPosChanged
end;
I settled on a fourth solution; one that leaves the bug intact, but fixes it enough for me.
The bug is that:
- WMWindowPosChanged doesn't handle size changes correctly
- but SetBounds does
So lets use SetBounds first.
Leverage the (mostly) correct code in SetBounds to do all the autosizing. Then we can call SetObjectRects
. When WMWindowPosChanged receives its WM_WindowPosChanging
message, it will have nothing to do - and therefore not do anything wrong.
tl;dr
procedure TOleControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
LRect: TRect;
begin
if ((AWidth <> Width) and (Width > 0)) or ((AHeight <> Height) and (Height > 0)) then
begin
//...snip: perhaps fiddle with AWidth or AHeight
{Removed. Call *after* inheirted SetBounds
//Notify the underlying Ole control that its bounds are about to change
if FOleInplaceObject <> nil then
begin
LRect := Rect(Left, Top, Left+AWidth, Top+AHeight);
FOleInplaceObject.SetObjectRects(LRect, LRect);
end;}
end;
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
//moved to call *after* SetBounds, we need SetBounds to happen first.
//TWinControl's WMWindowPosChanged does not handle autosizing correctly
//while SetBounds does.
//Notify the underlying Ole control that its bounds are already about to change
if FOleInplaceObject <> nil then
begin
LRect := Rect(Left, Top, Left+AWidth, Top+AHeight);
FOleInplaceObject.SetObjectRects(LRect, LRect);
end;
end;
Note: Any code released into public domain. No attribution required.
这篇关于包含TWebBrowser时,TPanel不会自动调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!