Kann das jetzt bestätigen. Aber es liegt nicht an der zweiten Checkbox, sondern daran, dass die zweite den State cbGrayed hat. Wenn ich in meinem ursprünglichen Projekt Checkbox1.State im O.I. auf cbGrayed setze, kann ich das Problem ebenfalls reproduzieren.
Das Problem entsteht, weil, wie du vermutet hast, OnClick ausgelöst wird, und in dem OnClick-Handler auf Label1 verwiesen wird, das noch gar nicht existiert.
Warum wird OnClick ausgelöst? Zunächst mal wird beim Streamen der State der Checkbox geändert, die ja mit State = cbUnChecked konstruiert worden ist. Dabei dabei wird TCheckbox.SetState aufrufen. Diese Routine führt zu
Code: Alles auswählen
procedure TCustomCheckBox.DoClickOnChange;
begin
Changed;
// emulate delphi OnClick behaviour (click will call OnChange)
if not ClicksDisabled then
inherited Click
else
DoOnChange;
end;
ClicksDisabled ist false, also geht's weiter in
Code: Alles auswählen
procedure TButtonControl.Click;
begin
DoOnChange;
inherited Click;
end;
mit
Code: Alles auswählen
procedure TButtonControl.DoOnChange;
begin
if [csLoading, csDestroying, csDesigning] * ComponentState <> [] then Exit;
EditingDone;
if Assigned(OnChange) then OnChange(Self);
end;
Weil wir in der csLoading Phase sind wird DoOnChange gleich in der 1.Zeile verlassen:
Bleibt die von TControl geerbte Click Methode:
Code: Alles auswählen
procedure TControl.Click;
function OnClickIsActionExecute: boolean;
begin
Result:=false;
if Action=nil then exit;
if not Assigned(Action.OnExecute) then exit;
if not Assigned(FOnClick) then exit;
Result:=SameMethod(TMethod(FOnClick),TMethod(Action.OnExecute));
end;
var
CallAction: Boolean;
begin
//DebugLn(['TControl.Click ',DbgSName(Self)]);
CallAction:=(not (csDesigning in ComponentState)) and (ActionLink <> nil);
// first call our own OnClick if it differs from Action.OnExecute
DebugLn(['CallAction = ', CallAction]);
DebugLn(['OnClickIsActionExecute = ', OnClickIsActionExecute]);
if Assigned(FOnClick)
and ((not CallAction) or (not OnClickIsActionExecute)) then
FOnClick(Self);
// then trigger the Action
if CallAction then
ActionLink.Execute(Self);
end;
Ich habe hier zwei DebugLn-Anweisungen reingeschrieben, um zu sehen, wie die Bedingungen sind, damit der OnClick-Handler (FOnClick) ausgelöst werden könnte.
Und jetzt wird's mysteriös: Egal wie State eingestellt ist, wird für beide der Wert false ausgegeben (CallAction = false, OnClickIsActionExecute = false). "Not" macht daraus ein true, und weil Assigned(FOnClick) ebenfalls true ist, müsste FOnClick eigentlich immer ausgeführt werden ("if true and (true or true)" --> true). Dennoch geschieht dies nur wenn State = cbGrayed ist. Seltsam...
Aber egal was der Grund für dieses Verhalten ist, eigentlich würde man erwarten, dass während der Ladephase überhaupt keine Events getriggert werden. Jetzt ist aber TControl sehr zentral, und ich fürchte, wenn Delphi ein OnChange-Ereignis zu einem OnClick macht (* P.S.), gibt es noch mehrere solche Ungereimtheiten. Also: wenn man hier etwas ändert, hat das weitreichende Auswirkungen.
Ich denke, ich werde mal die anderen Entwickler kontaktieren...
Zur Sicherheit sollte man Event-Handler, die auf andere Controls zugreifen, immer vorzeitig verlassen, wenn "(csLoading in ComponentState)" ist. Wird bei 1000 Controls natürlich etwas lästig...
P.S.
Und bevor jemand sich wundert, warum OnClick ausgelöst wird, wo doch niemand geklickt hat: Das ist eine Unart, die uns Delphi ins Nest gelegt hat. Aber wenn das Event OnChange heißen würde, hätten wir dasselbe Problem.