Form positioning when using TStyleManager in Delphi XE7 application - delphi

I'm trying to make a Delphi application form remember it's position and recall it the next time the application is started:
procedure TForm1.LoadSettings;
var
xml : IXMLDocument;
node : IXMLNode;
begin
[ ... initializing XML ...]
// Loading theme
if node.HasAttribute('Theme') then TStyleManager.SetStyle(node.Attributes['Theme']);
// Loading position & size
if node.HasAttribute('PosTop') then self.Top := StrToInt(node.Attributes['PosTop']);
if node.HasAttribute('PosLeft') then self.Left := StrToInt(node.Attributes['PosLeft']);
if node.HasAttribute('PosWidth') then self.Width := StrToInt(node.Attributes['PosWidth']);
if node.HasAttribute('PosHeight') then self.Height := StrToInt(node.Attributes['PosHeight']);
[ ... some other stuff ... ]
end;
Everything works fine when the "Theme" is set to default "Windows" theme. With any theme, the form appears at it's default position set up by Windows, close to the upper left display corner, no matter where the form was located before.
Any ideas about what may be the cause and how to fix that?
Thanks in advance!

If you want to properly restore form's position you have to set it initial Position property to poDesigned in form designer.
Default Position property is poDefaultPosOnly. That means your form position will be determined automatically by Windows. Depending at which point you trigger your own form positioning during the form creating process and depending on other code you may either successfully change forms position or not.
However, if the Position property is poDesigned all positioning will be left to Delphi code itself and you will be able to properly apply your setting.
Why?
Because if you use any code that will trigger form's window recreation, that code will again try to set your form's position to Windows default one and can interfere with your positioning process.
Calling TStyleManager.SetStyle does exactly that.
Simple test case for above would be creating new VCL project, adding some style to it and put following code in FormCreate event
procedure TForm1.FormCreate(Sender: TObject);
var Styled: boolean;
begin
Styled := true;
if Styled then TStyleManager.SetStyle('Silver');
Top := 200;
Left := 300;
Width := 800;
Height := 600;
end;
If Styled is false setting position works, if it is true it doesn't.

Related

Correct way to change WIndow Size before showing

I have project moved from XE7, in OnCreate of the child form I'm change size of window, this form have Position := MainFormCenter.
Now with Berlin I have Left = 0 and Top = 0 after showing this child form. If window size is not changed in OnCreate, than this child form created correctly and it is centered over Main Form.
How I should change form size during initialization now with Delphi Berlin and not lost form position settings?
Form position changed here to TFormPosition.Designed:
unit FMX.Forms
procedure TCommonCustomForm.Show;
var
LPosition: TFormPosition;
...
begin
...
// If you changed the original coordinates or size
if TBoundChange.Location in FBoundChanges then
begin
if LPosition = TFormPosition.Default then
LPosition := TFormPosition.DefaultSizeOnly
else if LPosition in [TFormPosition.DefaultPosOnly, TFormPosition.ScreenCenter, TFormPosition.DesktopCenter,
TFormPosition.MainFormCenter, TFormPosition.OwnerFormCenter] then
LPosition := TFormPosition.Designed; // Changed here to Default
end;
...
end;
Update:
One way which I found - change size from OnShow method of the form
No other answers - one way to fix this is changing form size in OnShow method of the form

What causes a control to be placed more left-right-top-bottom than another which has same alignment? [duplicate]

In this particular case I'm using PowerPDF library to dynamically build a PDF document, but the same applies for the general concept of dynamically aligning controls sequentially inside of a parent control. In this library, TPRPage is the base control to contain all element controls, in this case, sequential instances of TPRLayoutPanel.
What I do when dynamically adding controls:
Create a control (TPRLayoutPanel)
Set the control's parent (TPRPage)
Align the control to top (PRLayoutPanel.Align:= alTop;)
The problem is it gets forced to the very beginning (top) instead of the very end (bottom) of the page.
I've tried setting its order PRLayoutPanel.SendToBack; or PRLayoutPanel.BringToFront but with no luck.
How can I dynamically create and align multiple controls within a parent control sequentially? My only current work-around is to add the controls in reverse order (from end to beginning) which is ridiculously unnecessary.
Here's my universal function which creates every new instance of an aligned control in this parent:
function TfrmReport.InsertPanel: TPRLayoutPanel;
begin
Result:= TPRLayoutPanel.Create(PRPage);
Result.Parent:= PRPage;
Result.Align:= alTop;
Result.Height:= 40; //Default, may change later
end;
Once again, DisableAlign and EnableAlign to the rescue:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
P: TPanel;
begin
DisableAlign;
try
for I := 0 to 4 do
begin
P := TPanel.Create(Self);
P.Caption := IntToStr(I);
P.Align := alTop;
P.Parent := Self;
end;
finally
EnableAlign;
end;
end;
Explanation:
When alignment is enabled, every single addition of a control to a container (the form itself in this specific case) will re-evaluate all alignment (and anchor) settings of all other controls within that container. In case that control has no specific Top property set, then Top will be 0. When there is already another control aligned to the top, then there are two controls with Top = 0, and the one which is about to inserted wins. I (currently) have no in-depth explanation for that, but it just is, and the position order indeed gets reversed from the creation order.
Now, when alignment of the container is disabled, then consecutive added controls are simply just inserted with all their positioning properties unaltered. When alignment is enabled again, then all those controls are re-evaluated in the same manner, with the difference that this takes place in one single loop in the order of the index in the Controls array; i.e. the order in which they were created.
You need to set the Top property to be the bottom of the previous panel. For example, like this:
PanelTop := 0;
for i := 0 to 5 do
begin
Panel[i] := TPanel.Create(Self);
Panel[i].Parent := Self;
Panel[i].Height := PanelHeight;
Panel[i].Align := alTop;
Panel[i].Top := PanelTop;
inc(PanelTop, PanelHeight);
end;
To fit it into your code you'd have to keep track of the location of the most recently added panel. Perhaps you could add a var parameter to your InsertPanel function:
function TfrmReport.InsertPanel(var PanelTop: Integer): TPRLayoutPanel;
begin
Result:= TPRLayoutPanel.Create(PRPage);
Result.Parent:= PRPage;
Result.Top:= PanelTop;
Result.Height:= 40;
Result.Align:= alTop;
inc(PanelTop, Result.Height);
end;
I trust you get the idea!
You may use alCustom align type and control all of your panels positions via CustomAlignPosition method (you will need to override it in parent control). This will give you more flexibility and control.

Centering a Tframe within a TTabSheet;

I have a delphi application that used a PageControl with a number of TabSheets. I also create new TabSheets at runtime and populate them with instances of predefined frames. These frames work well, except for the cosmetic problem of not centering on the TabSheet. I have tried to use Frame.Align := alClient, but that didn't do it. The relevant code follows:
CreateNewPage(3);
NewLimitedChoiceFrame := TLimitedChoiceFrame.Create(NewInputPage);
NewLimitedChoiceFrame.Parent := NewInputPage;
CreateNewPage creates a new instance of a TabSheet and makes the PageControl it's owner and parent. The result is assigned to the global variable NewInputPage.
To centre a control in its parent do this:
procedure CentreControl(Control: TControl);
begin
Control.Left := (Control.Parent.ClientWidth-Control.Width) div 2;
Control.Top := (Control.Parent.ClientHeight-Control.Height) div 2;
end;
Call this function, passing the frame. Obviously you need to wait until you've assigned the parent before doing so.
If the page control can be re-sized at runtime, add a call to this function from the tabsheet's OnResize event. Or, as NGLN points out simply set the control's Anchors to [] and the VCL framework will take care off centring the control when its parent is resized.

How make a firemonkey HUD window

I wanna to replicate the HUD functionality of https://github.com/jdg/MBProgressHUD in Delphi with firemonkey.
This is what look like in iPhone:
The main issue is how make the form semi-transparent & completely remove the borders.
Create your Firemonkey HD form, set it's Fill.Kind to bkNone, and it's Fill.Color to Null. Additionally, set it's Transparency property to True, and it's BorderStyle to bsNone.
Create a TRectangle (or any shape), and set the Stroke.Kind property to bkNone. Set it's Fill.Color to Gray, it's Opacity to 0.5.
Create a TAniIndicator and TLabel with parent of both as the form. It's Opacity remains at 1.0. Optionally, also create a TImage and make it the exact same size and position as the TAniIndicator.
From there, it's simply a case of working with TFloatAnimation on the TAniIndicator when you want to change the image (to a tick or such) and the label text to simply change to whatever message you want to display. Ideally, you simply create a procedure that accepts either a string or integer as a variable, and then modify the text and indicator/image to match that. For example;
Procedure TForm1.Process(Mode : Integer);
Begin
if Mode = 1 then
begin
AniIndicator1.Enabled := True;
AniIndicator1.Visible := True;
Image1.Visible := False;
Label1.TextAlign := TTextAlign.taCenter; // Must be called to reset alignment
Label1.Text := 'Loading';
End
else if Mode = 2 then
Begin
AniIndicator1.Enabled := False;
AniIndicator1.Visible := False;
Label1.TextAlign := TTextAlign.taCenter; // Must be called to reset alignment
Image1.Bitmap.LoadFromFile('Tick.png');
Image1.Visible := True;
Label1.Text := 'Complete!';
end;
end;
You can then create a tpanel in your main form, and then add the above form (that contains the TAniIndicator, label, and rectangle) as a child component. You then call the procedure you created with a valid mode variable and it'll run as you indicated in the code. It's easy enough to add more modes and i've done something similar with one of my own applications (although it was related to TRectangle rather than creating an indicator).

Delphi How to force Main form scrollbars to be visible

What options/properties should use to show a main from scrolbars when I want to?
or always visible in Delphi 2010
The help is as too often useless
thanks
Pw
#Philippe, you can use the ShowScrollBar function and the HorzScrollBar, VertScrollBar propeties to do this.
check this code
procedure TForm1.FormCreate(Sender: TObject);
begin
HorzScrollBar.Range := 10000; // set the range to an higher number
VertScrollBar.Range := 10000; // set the range to an higher number
ShowScrollBar(Handle, SB_BOTH, True);
end;
If you set AutoScroll = true, they should show up if needed. That is, if any visual component is placed outside of the visible client area.
If you do not have any components 'off-screen', why would you need the scrollbar showing?
Anyway, you can set Horz-/VertScrollBar.Range to anything larger than the clientheight/width, and they will show up.
If you need the scrollbar for something else, you can always drop a TScrollBar component on the form.

Resources