0

Screenshot showing the problem

The screenshot was taken during window activation. As it can be seen some controls are being painted with black background first. Those are TCheckBox, TButton, TStringGrid. I would like the black regions under the controls to be in color of the form which is clBtnFace. How could I fix this?

Edit: It happens in VCL, Delphi 10.3.3, Windows 10. By window activation I meant that the application is already running and was minimised to taskbar. There are just a lot of controls put onto form. There are 7 TPanels, around 60 TEdits as can be seen, next to them there are around 60 TLabels which are not yet painted and the rest with black background are few TCheckBoxes, TButtons and one empty TStringGrid.

Tested with empty form, put one TPanel and on top of it 300 TEdits and behaviour the same. So it is by design that some of the controls are painted first with black background. So how could I change that default background color to something else?

Edit2: Tested with 400 TPanels on form. These are painted as expected without setting of the Rect under TPanel to black.

Edit3: Unfortunately with 1000 TPanels on form repainting is enough slow so I am able to observe the black background.

Here is the code with which I test controls:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ExtCtrls, Vcl.StdCtrls;

const
  ControlsNum=800;
  Columns=40;

type
  TControlTestList=array of TButton;
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    var CList:TControlTestList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i:integer;
  W,H:integer;
begin
  Self.Width:=1400;
  Self.Height:=800;
  W:=Self.ClientWidth div Columns;
  H:=Self.ClientHeight div (ControlsNum div Columns);
  SetLength(CList,ControlsNum);
  for i:=0 to ControlsNum-1 do
  begin
    CList[i]:=TButton.Create(Self);
    with (CList[i] as TButton) do
    begin
      Parent:=Self;
      Top:=H*(i div Columns);
      Left:=W*(i mod Columns);
      Width:=W;
      Height:=H;
      Caption:=IntToStr(i);
    end;
  end;

end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  i:integer;
begin
  for i:=0 to Length(CList)-1 do FreeAndNil(CList[i]);
  SetLength(CList,0);
end;

end.

Edit4: On Windows 10: The black background under controls doesn't appear during any type of form resize. Only when minimalistation of window and bringing it back is done. On Windows 8.1: Black background also appears when form is resized.

Thwei
  • 15
  • 4
  • VCL or Firemonkey? Which Delphi version? Which Windows version? – Andreas Rejbrand May 12 '20 at 12:47
  • Anyhow, this appearance shouldn't be present more than a tiny fraction of a second. I only see this kind of behaviour when I accidentally run out of RAM and the entire system becomes extremely sluggish. If you see this behaviour in your app when the system is fine, you are doing something wrong in your app, like doing a lot of work in the GUI thread when the form is activated. That you really shouldn't do. Do work in a separate thread, or do it in `FormCreate` instead (or `FormShow` if that is more appropriate in your case). – Andreas Rejbrand May 12 '20 at 12:48
  • Are you sure that the said controls are painted black first and then with their actual content? Since you have a black desktop background it is possible that the black areas are just parts of your application window not being painted and thus desktop background is seen there. As far as I know Windows Vista introduced different way of Window compositioning system with ability to render only visible part of the window not unlike on Windows XP and odler where entire window had to be rendered followed by rendering all child windows. – SilverWarior May 12 '20 at 14:53
  • Yes I am sure the controls are painted black first. – Thwei May 12 '20 at 16:19
  • Notice that you can skip your `OnDestroy` handler altogether. Since the form owns the buttons (`TButton.Create(Self)`) the buttons are all freed when the form is destroyed. And dynamic arrays are managed by the compiler, so you don't need that `SetLength(CList, 0)` either. In addition, you try to free `CList[Length(CList)]` which is a bug -- the last button is `CList[Length(CList) - 1]` = `CList[High(CList)]`. – Andreas Rejbrand May 12 '20 at 16:32
  • Returning to the actual topic, I do get a huge black area for a second or so, but as I stated in a previous comment, this isn't quite a normal situation. You shouldn't really have this number of windowed controls on your form. Although there are probably tricks you can employ to fix this issue, who knows how they will work after the next Windows update. Best is to use a "sane" number of windowed controls on your form. – Andreas Rejbrand May 12 '20 at 16:38
  • If you really need a grid of several hundreds of buttons, it is better to create a custom control (a `TCustomControl` child class) that manually draws on the screen and handles mouse and keyboard input. This way you get only a single windowed control for all buttons and you get 100% control over the rendering. Example: https://stackoverflow.com/a/3902049/282848 – Andreas Rejbrand May 12 '20 at 16:41
  • I have an observation that the first time the form with all controls is painted the background under controls is not black but `clWindow` (expected behaviour). Then I minimize window, bring it back and get that black area for a while under controls. So there must be something different between the first and latter window paints. – Thwei May 12 '20 at 16:44
  • @Thwei: I observe the same behaviour. – Andreas Rejbrand May 12 '20 at 16:46

1 Answers1

2

Try this:

interface

  TForm1 = class(TForm)
  ....
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
  ....
  end;


implementation

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
    inherited;
    Params.WindowClass.hbrBackground := COLOR_BTNFACE + 1;
end;
dwrbudr
  • 607
  • 4
  • 8