scopre che la funzione che si rompe tutto è UpdateAnchorRules
. TControl
memorizza FOriginalParentSize
e ha la propria dimensione originale in FAnchorRules
e lo utilizza per ridimensionare automaticamente come ridimensionamento padre. UpdateAnchorRules()
prende la dimensione principale corrente e il controllo corrente Width
e Height
e li salva in FOriginalParentSize
e FAnchorRules
.
Se tutto ha funzionato correttamente, non avrebbe avuto alcun effetto durante le normali ridimensionazioni, poiché il controllo e il genitore cambiano di dimensioni in accordo.
Ma quando il controllo Width
è inferiore a zero a causa dell'ancoraggio, Windows, e di conseguenza Delphi lo considera ancora 0
. Se a questo punto viene chiamato il numero UpdateAnchorRules
, viene salvato il valore errato 0
per la larghezza originale. Dopo questo il layout è irreparabile.
(Se non è chiamato, Width
continua ad essere aggiornato in relazione proprio genitore Width
a causa di dimensioni originali conservati)
scopre tutto ciò che comporta la creazione di un handle di finestra chiama UpdateAnchorRules
due volte: prima in WinAPI CreateWindow
come invia WM_SIZE
prima di restituire (e WM_SIZE
il gestore chiama UpdateAnchorRules
) e il secondo, esplicitamente in CreateHandle
dopo la creazione della maniglia.
Sembrerebbe che finché potremo disabilitare per la durata di CreateHandle
, avremo successo. Ma ci sono chiamate esplicite a in CreateHandle
, il che significa che qualcuno ha pensato che ha bisogno di per essere un adeguamento delle regole di ancoraggio dopo la creazione della maniglia.
Quindi forse mi manca qualcosa e disabilitandolo qualcosa si romperà?
In ogni caso, esistono due metodi pronti per disabilitare UpdateAnchorRules
: per impostare FAnchorMove
o per impostare csLoading
. Il primo non va bene dato che c'è il codice che lo cancella a metà del numero RecreateWnd
e poi chiama di nuovo UpdateAnchorRules
.
seconda funziona ed ecco una soluzione:
type
TComponentHack = class helper for TComponent
public
procedure SetCsLoading(Value: boolean);
end;
procedure TComponentHack.SetCsLoading(Value: boolean);
var i: integer;
begin
if Value then
Self.FComponentState := Self.FComponentState + [csLoading]
else
Self.FComponentState := Self.FComponentState - [csLoading];
for i := 0 to Self.ComponentCount-1 do
if Self.Components[i] is TControl then
TControl(Self.Components[i]).SetCsLoading(Value);
end;
procedure SafeRecreateWnd();
begin
MyControl.SetCsLoading(true);
try
MyControl.RecreateWnd(); //or any operation which triggers it -- such as docking or making the window visible first time after RecreateWnd()
finally
MyControl.SetCsLoading(false);
end;
end;
responsabilità:
Non ho idea di che cosa sarà spezzato eseguendo TControl
operazioni con set csLoading.
migliore alternativa sarebbe quella di agganciare UpdateAnchorRules
procedura e aggiungere un altro controllo bandiera specificamente per questo scopo, ma che avrebbe richiede né reimplementando UpdateAnchorRules
completamente (tendenti alla rottura su diverse versioni di Delphi con differente originale UpdateAnchorRules
) o inventare qualche modo per chiama l'originale UpdateAnchorRules
che di solito viene distrutto riscrivendolo con un gancio.
"Impossibile da usare" è un po 'esagerato. Un sacco di casi d'uso in cui la maniglia della forma non viene mai ricreata. –
Io non la penso così. Anche quando apparentemente non lo fai ora, un mese dopo fai qualcosa che innesca la ricreazione e il botto, hai introdotto dei bug senza saperlo. Le ancore sono imprevedibili. – himself
Hmm si, certo, è per questo che sono usati così tanto ... Le ancore funzionano abbastanza bene e lo hanno fatto per me e molti altri per anni. Apparentemente il tuo chilometraggio è diverso, ma la tua incapacità di usarli non li rende impossibili da usare per tutti gli altri. –