I hate the title of this question. Anyway:
If you call TForm.Show with a custom theme (Windows10 Dark in this case), then close that form, then change the theme to the system Windows theme, then change back to the Windows10 Dark theme, and finally call TForm.Show on that form again, the border renders incorrectly and certain controls do not render properly, like a TComboBox.
I have a test project below, and a "fix" of sorts. But I do not like my fix and the reason for this question is that I do not really understand what is happening here that causes the form to render incorrectly only if it was hidden while the theme changed, and only if the theme is changed away from, and then back to, Windows10 Dark.
My fix is to track the theme change. If the condition I describe above occurs, I intercept the CM_SHOWINGCHANGED message, ignore it, then force the window to be recreated and then process the inherited CM_SHOWINGCHANGED. It is a very brittle fix and obviously not the way to go, so I am hoping someone can show me what is actually happening so I can fix it "for real."
Incidentally, I have submitted this as a bug to Embarcadero already. https://quality.embarcadero.com/browse/RSP-33977
Here is the test code. You’ll need to add Windows10 Dark to the application’s styles, obviously.
unit Unit22; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Unit23, Vcl.Themes; type TForm22 = class(TForm) Panel1: TPanel; ComboBox1: TComboBox; RadioGroup1: TRadioGroup; ButtonShow: TButton; Memo1: TMemo; procedure ButtonShowClick(Sender: TObject); procedure RadioGroup1Click(Sender: TObject); procedure FormShow(Sender: TObject); private { Private declarations } FAllowChange: Boolean; public { Public declarations } end; var Form22: TForm22; implementation {R *.dfm} procedure TForm23.Button1Click(Sender: TObject); begin PostMessage(Handle, CM_RECREATEWND, 0, 0); end; procedure TForm23.CMShowingChanged(var Message: TMessage); var DoFix: Boolean; begin if not Showing then inherited else begin // if the theme changed away from dark, then back to dark, while we were // not visible, then we need to force the window to be recreated again // before showing. // This is a really bad hack but basically I am just preventing the // normal response to CMShowingChanged and then setting up a message // queue that will recreate the window and then process the CM_SHOWINGCHANGED // message again. This will probably break the universe but it appears to work // in this test. FShowedStyle := StyleServices.Name; Panel1.Caption := FShowedStyle; DoFix := not FFixing and (FNeedFix <> '') and (FNeedFix = FShowedStyle); FNeedFix := ''; if DoFix and CheckBox1.Checked then begin FFixing := True; // SendMessage(Handle, WM_SETREDRAW, Winapi.Windows.WPARAM(LongBool(False)), 0); PostMessage(Handle, CM_RECREATEWND, 0, 0); // PostMessage(Handle, CM_SHOWINGCHANGED, Message.WParam, Message.LParam); // do not allow inherited. end else begin FFixing := False; inherited; end; end; end; procedure TForm23.CMStyleChanged(var Message: TMessage); begin FNeedFix := FShowedStyle; inherited; end; procedure TForm23.DoShow; var DoFix: Boolean; begin inherited; exit; end; end.
Unit23 DPR:
object Form23: TForm23 Left = 0 Top = 0 Caption = 'Form23' ClientHeight = 253 ClientWidth = 360 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 = 0 Top = 0 Width = 360 Height = 253 Align = alClient Alignment = taRightJustify BevelEdges = [] BevelOuter = bvNone Caption = 'Panel1' TabOrder = 0 object ComboBox1: TComboBox Left = 16 Top = 32 Width = 145 Height = 21 Style = csDropDownList ItemIndex = 0 TabOrder = 0 Text = 'one' Items.Strings = ( 'one' 'two' 'three') end object ComboBox2: TComboBox Left = 16 Top = 59 Width = 145 Height = 21 Style = csDropDownList ItemIndex = 1 TabOrder = 1 Text = 'two' Items.Strings = ( 'one' 'two' 'three') end object ComboBox3: TComboBox Left = 16 Top = 86 Width = 145 Height = 21 Style = csDropDownList ItemIndex = 2 TabOrder = 2 Text = 'three' Items.Strings = ( 'one' 'two' 'three') end object Button1: TButton Left = 16 Top = 136 Width = 75 Height = 25 Caption = 'RecreateWnd' TabOrder = 3 OnClick = Button1Click end object CheckBox1: TCheckBox Left = 16 Top = 167 Width = 273 Height = 17 Caption = 'Fix with CM_SHOWINGCHANGED hack' TabOrder = 4 end end end
Recent Comments