包含TWebBrowser时,TPanel不会自动调整大小 [英] TPanel does not AutoSize when containing a TWebBrowser

查看:93
本文介绍了包含TWebBrowser时,TPanel不会自动调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了),面板将不会自动调整大小:





必须是TWebBrowser的错



必须自动调整 TWebBrowser VCL包装程序错误所需的一些VCL管道。我需要知道XE6中损坏的内容及其解决方法。



用户user1611655有一个很好的解决方法


我遇到了类似的问题。



通过在 TWebBrowser 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆