2016-05-27 12 views
5

Das Szenario ist folgendes:Warum ist meine Delphi-Formularsteuerung abgeschnitten, wenn mein Formular größer als mein Bildschirm ist?

  • ich eine Delphi (XE2) Formular erstellt haben.
  • Darauf ist eine einzelne TGroupBox (oder anderes Steuerelement) gestreckt, so dass es die volle Breite des Formulars mit der Spitze einnimmt.
  • Rechter Anker (zusätzlich zu links und oben) auf TGroupBox gesetzt ist.
  • Formularbreite auf 1200px (um den Punkt zu veranschaulichen).

Wenn ich diese Anwendung auf einem Monitor laufen, deren Screen.Width Eigenschaft größer als 1200px (Ich laufe ohne DPI-Virtualisierung AFAIK), dann macht die TGroupBox wie man erwarten würde.

Wenn jedoch die Breite des Monitors weniger als 1200 Pixel beträgt, fehlt der rechte Teil des Steuerelements auf dem Bildschirm, unabhängig davon, wie Sie die Größe des Formulars ändern.

Ich habe die Create() Methode meines Formulars mit der override; Direktive außer Kraft gesetzt und verifiziert, dass ich die width Eigenschaft richtig einstelle, aber die Steuerung wird immer noch beschnitten.

Kann jemand raten entweder, wie man:

a) setzen die Breite Eigenschaft des Formulars, so dass es die Positionierung der untergeordneten Komponenten oder ... wirkt

b) deuten auf eine Art und Weise zu zwingen, ein Relayout aller untergeordneten Komponenten, sobald das Formular gerendert wurde?

+1

Wie wäre es, wenn Sie die 'Align'-Eigenschaft der GroupBox auf' alTop' setzen? Dies entspricht der Aktivierung der Anker für den linken, oberen und rechten Anker, so dass sie gedehnt werden, wenn die Größe des übergeordneten Formulars geändert wird. –

+0

Ich habe eine ähnliche Situation mit einem Frame in einer Form beobachtet. Wenn der Rahmen größer als das Formular ist, wird die Größe des Rahmens nicht auf die Abmessungen des Formulars geändert, selbst wenn die Eigenschaft Ausrichten auf Client festgelegt ist. Mit dem Rahmen ist der Trick kleiner zu machen und die Client-Eigenschaft den Job erledigen zu lassen. –

Antwort

3

Verfolgen Sie den Code, um zu sehen, was passiert, ich kam mit der folgenden Anpassung.

procedure TForm1.WMWindowPosChanging(var Message: TWMWindowPosChanging); 
var 
    MessageWidth: Integer; 
begin 
    MessageWidth := Message.WindowPos.cx; 
    inherited; 
    if MessageWidth > Message.WindowPos.cx then 
    GroupBox1.Width := GroupBox1.Width - MessageWidth + Message.WindowPos.cx; 
end; 

Dies ist keine verallgemeinerte Lösung, aber es macht deutlich, was das Problem ist. VCL fragt nach einer Fenstergröße für seine Form, die vom Betriebssystem nicht gewährt wird, da sie größer als der Desktop ist. Von nun an wird das Formular wieder verankert, um das untergeordnete Steuerelement mit seiner festgelegten Entwurfszeit zu verankern, die größer ist als die Clientbreite des Formulars, sodass die rechte Seite des untergeordneten Steuerelements überläuft.

Eine andere Lösung kann sein, die Behandlung der Nachricht WM_GETMINMAXINFO zu überschreiben, damit das Betriebssystem die angeforderte Breite gewährt.

Dies ist möglicherweise keine gute Lösung, denn dann wird das Formular größer als der Desktop sein.

In Bezug auf Ihre 'a' und 'b' Elemente, glaube ich nicht, 'b' ist möglich - oder zumindest nicht möglich, die VCL-Relayout selbst zu machen - weil VCL Anker Anwenden von Ankerregeln bis nach der Komponente (Form) wird geladen. Bis dahin ist die Breite des Formulars anders als die Entwurfszeitbreite, aber die Platzierung der untergeordneten Steuerelemente bleibt davon unberührt. Keine Menge an Forcing zum Layout wird sie wieder synchronisieren.

Es sollte jedoch möglich sein, alles von Grund auf neu zu berechnen, wenn Ihr eigener Code einen Verweis auf die Entwurfszeitbreite enthält. Der folgende Code ist nicht vollständig.

type 
    TForm1 = class(TForm) 
    .. 
    private 
    FAdjustShrinkWidth, FAdjustShrinkHeight: Integer; 
    protected 
    procedure Loaded; override; 
    public 
    procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; 
     AHeight: Integer); override; 
    end; 

... 

procedure TForm1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); 
var 
    TrackWidth, TrackHeight: Boolean; 
begin 
    TrackWidth := AWidth = 1200; 
    TrackHeight := AHeight = ??; 
    inherited; 
    if TrackWidth and (Width < AWidth) then 
    FAdjustShrinkWidth := AWidth - Width; 
    if TrackHeight and (Height < AHeight) then 
    FAdjustShrinkHeight := AHeight - Height; 
end; 

procedure TForm1.Loaded; 

    procedure ReadjustControlAnchors(Control: TWinControl); 
    var 
    i: Integer; 
    begin 
    for i := 0 to Control.ControlCount - 1 do 
     if (akRight in Control.Controls[i].Anchors) or (akBottom in Control.Controls[i].Anchors) then begin 
     Control.Controls[i].Left := // some complex calculation depending on the anchors set; 
     Control.Controls[i].Top := // same as above; 
     Control.Controls[i].Width := // same as above; 
     Control.Controls[i].Height := // same as above; 
     if (Control.Controls[i] is TWinControl) and (TWinControl(Control.Controls[i]).ControlCount > 0) then 
      ReadjustControlAnchors(TWinControl(Control.Controls[i])); 
     end; 
    end; 

begin 
    inherited; 
    ReadjustControlAnchors(Self); 
end; 

Ich habe keine Ahnung, wie Sie die Lücken im obigen Code ausfüllen. Das Lesen und Aufspüren von VCL-Code kann erforderlich sein, um die VCL-Verankerung zu imitieren.

Ich kann nichts für 'a' denken.


Update:

VCL hat tatsächlich eine Backdoor-links für eine Kontrolle seiner unmittelbaren Kinder über ihre Eltern Größe zu liegen, während sie zu verankern. Documentation erklärt es ein bisschen anders:

UpdateControlOriginalParentSize ist ein geschütztes Verfahren, das die Steuer Originalgröße der Mutter aktualisiert. Es wird intern verwendet, um die Ankerregeln des Steuerelements zu aktualisieren.

Wir können damit der Groupbox die beabsichtigte Originalgröße mitteilen.

type 
    TForm1 = class(TForm) 
    .. 
    private 
    FWidthChange, FHeightChange: Integer; 
    protected 
    procedure UpdateControlOriginalParentSize(AControl: TControl; 
     var AOriginalParentSize: TPoint); override; 
    public 
    procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer; 
     AHeight: Integer); override; 
    end; 

... 

procedure TForm1.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); 
var 
    RequestedWidth, RequestedHeight: Integer; 
begin 
    RequestedWidth := AWidth; 
    RequestedHeight := AHeight; 
    inherited; 
    if csLoading in ComponentState then begin 
    if RequestedWidth <> Width then 
     FWidthChange := Width - AWidth; 
    if RequestedHeight <> Height then 
     FHeightChange := Height - AHeight; 
    end; 
end; 

procedure TForm1.UpdateControlOriginalParentSize(AControl: TControl; 
    var AOriginalParentSize: TPoint); 
begin 
    inherited; 
    if akRight in AControl.Anchors then 
    AOriginalParentSize.X := AOriginalParentSize.X - FWidthChange; 
    if akBottom in AControl.Anchors then 
    AOriginalParentSize.Y := AOriginalParentSize.Y - FHeightChange; 
end; 


Ich stelle fest, wieder, dass dies die Form der unmittelbaren Kinder nur beeinflussen. Wenn das Groupbox Steuerelemente hostet, die rechts und unten verankert sind, muss es auch die gleiche Methode überschreiben.

Beachten Sie auch, dass dies nicht die Tatsache rückgängig macht, dass die Breite des Formulars geändert hat. Das heißt, wenn ein linkes verankertes Steuerelement ganz rechts im Formular vorhanden ist, ersetzt es sich nicht selbst gegenüber der Client-Grenze. Es wird sich so verhalten, als ob die Breite der Form verringert worden wäre, d.h. außerhalb der Sichtweite geblieben wäre.