i try to maximize width and hold height. i use delphi xe4, windows 7, 1,440 * 900 2 monitors.
height constraint usually works well but when it exceeds certain value which is 859 it doesn't work.
i guessed that it was because of windows snap feature but even after the turning off that it's same.
when i do this in the sub monitor which doesn't have taskbar and in the main monitor with taskbar auto hide it works well. it seems the trouble with taskbar.
any help to solve this please. thanks.
procedure TForm1.Button1Click(Sender: TObject);
begin
Constraints.MaxHeight := 859; // works well
WindowState := wsMaximized;
Caption := IntToStr(Height);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Constraints.MaxHeight := 860; // doesn't work
WindowState := wsMaximized;
Caption := IntToStr(Height); // maximized as 876
end;
procedure TForm1.FormConstrainedResize(Sender: TObject; var MinWidth, MinHeight, MaxWidth, MaxHeight: Integer);
begin
MaxHeight := 860; // doesn't works and is maximized as 876
end;
If you are trying to restrict the maximum size of the form when the user tries to maximise it then this will work. I have my own Form class descended from TForm called TRGNewForm and all my forms descend from that. However the scheme will work for the form you put the code in.
In the Interface section, in your Form class Public definition
{ Trap Sys Commands }
procedure WMSysCommand (var Msg: TWMSysCommand); message WM_SYSCOMMAND;
In the Implementation
procedure TRGNewForm.WMSysCommand (var Msg : TWMSysCommand);
begin
{ If MdiChild and Maximize button pressed, then use our own routine,
as Windows has a bug }
if (Msg.CmdType = SC_MAXIMIZE) and
(FormStyle = fsMDIChild)
then Maximise_Child_Form
else DefaultHandler (Msg);
end;
In Maximise_Child_Form you set the height and width as required.
Related
I have created dynamic Form as the next :
procedure TForm1.Button1Click(Sender: TObject);
var
Frm:TForm2;
begin
frm:=TForm2.Create(nil);
Frm.Left:=5;
Frm.Top:=5;
Frm.Parent:=Self;
Frm.OnCreate:=OncreateFrm;
Frm.Show;
end;
and when am trying to change the AlphaBlend property, the transparency wouldn't change..
procedure TForm1.OncreateFrm(Sender: TObject);
begin
AlphaBlend:=True;
AlphaBlendValue:=200;
end;
Also overriding the constructor it gave the same result ..
Thanks.
Your approach
Frm := TForm2.Create(nil);
Frm.Left := 5;
Frm.Top := 5;
Frm.Parent := Self;
Frm.OnCreate := OncreateFrm;
Frm.Show;
cannot possibly work because you set the OnCreate handler on line 5, which is after the form has been created on line 1; consequently, at the time the form is created (line 1), it sees that OnCreate is nil and so does nothing. Your instruction on line 5 has no effect.
This is like telling your fiend "Please buy some milk on your way home from work" after your friend has already come home from work.
Solutions
1: Set the properties at design time
Of course, you can use the Object Inspector to set the AlphaBlend and AlphaBlendValue properties of TForm2 at design time. But I suspect you want to do it dynamically, because you ask this question.
2: Use the OnCreate handler on TForm2
Just open TForm2 in the form editor and double click it to give it its own OnCreate handler:
// in Unit2.pas
procedure TForm2.FormCreate(Sender: TObject);
begin
AlphaBlend := True;
AlphaBlendValue := 128;
end;
3: Override TForm2's constructor
// in Unit2.pas
constructor TForm2.Create(AOwner: TComponent);
begin
inherited;
AlphaBlend := True;
AlphaBlendValue := 128;
end;
4: Set the properties when you create the object
// in Unit1.pas
procedure TForm1.Button1Click(Sender: TObject);
var
Frm: TForm2;
begin
Frm := TForm2.Create(nil);
Frm.Left := 5;
Frm.Top := 5;
Frm.AlphaBlend := True;
Frm.AlphaBlendValue := 128;
Frm.Show;
end;
Unlike the previous three approaches, this one affects only this instance of TForm2 -- it doesn't affect the class itself.
All these approaches work.
There is a "but"
Your line
Frm.Parent := Self
means that you make this form into a control instead of a top-level window.
And layered windows (the Win32 feature on which the VCL's AlphaBlend feature is based) are only supported as child windows in Windows 8 and later.
Therefore, if you are using Windows 7 or earlier, you cannot use AlphaBlend in this case.
The scenario is this:
I've created a Delphi (XE2) form.
On it is a single TGroupBox (or other control) stretched so it occupies the full width of the form with the top.
Right anchor (in addition to left and top) on TGroupBox is set.
Form width set to 1200px (to illustrate the point).
If I run this application on a monitor whose Screen.Width property is greater than 1200px (I'm running without any DPI virtualization AFAIK) then the TGroupBox renders as you'd expect.
However.. if the monitor's width is less than 1200px then the right hand portion of the control is missing from the screen regardless of how your resize the form.
I've overridden the Create() method of my form with the override; directive and verified that I'm setting the width property correctly, however the control is still cropped.
Can anyone advise either how to:
a) set the width property of the form such that it is affects the positioning of the child components or...
b) suggest a way to force a relayout of all child components once the form is rendered?
Tracing the code to see what happens, I came up with the below adjustment.
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;
This is not a generalized solution, but it makes clear what the problem is. VCL asks for a window size for its form which is not granted by the OS since it is larger then the desktop. From then on the form resumes anchoring the child control with its design time specified width which is larger than the client width of the form, thus right side of the child control overflows.
Another solution can be to override handling of WM_GETMINMAXINFO message to let the OS grant the asked width.
procedure TForm1.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
begin
inherited;
Message.MinMaxInfo.ptMaxTrackSize.X := 1200;
end;
This may not be a good solution because then the form will be larger than the desktop.
Regarding your 'a' and 'b' items, I don't think 'b' is possible - or at least not possible to make the VCL relayout by itself - because VCL defers applying anchor rules until after the component (form) is done loading. By then, the form's width is different than the design time width but child controls' placement remain unaffected. No amount of forcing to layout will make them in sync again.
However it should possible to recalculate everything from scratch if your own code keeps a reference to the design time width. Below is not complete code.
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;
I have no idea how to fill in the blanks in the above code. Reading and tracing VCL code may be compulsory to imitate VCL anchoring.
I can't think of anything for 'a'.
Update:
VCL has actually left a backdoor for a control to lie to its immediate children about their parent's size while they are anchoring. Documentation explains it a bit different:
UpdateControlOriginalParentSize is a protected method that updates the
original size of the parent control. It is used internally to update
the anchor rules of the control.
We can use it to tell the groupbox the intended original size.
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;
I note again that this will affect the form's immediate children only. Should the groupbox hosts controls that anchors right and bottom, it also has to override the same method.
Also note that this will not undo the fact that the form's width has changed. That's if there was a left anchored control that's at the far right of the form, it will not replace itself to client boundary. It will act as if the form's width has been decreased, i.e. remain out of sight.
I create a captionless window.
I resize it manually (or programmatically) so that its height is 30 pixels or less.
When I then grab the bottom border to resize it vertically, it behaves as
if I were dragging the top border. Indeed when debugging the program, the WM_SIZING parameter contains WMSZ_TOP instead of WMSZ_BOTTOM.
My program is written in Delphi, basically the problem is reproducible with a main form with the following FormCreate:
procedure TForm2.FormCreate(Sender: TObject);
var oldStyle : LongInt;
var newStyle : LongInt;
begin
// Adapt windows style.
oldStyle := WINDOWS.GetWindowLong (
handle,
GWL_STYLE);
newStyle := oldStyle and
(not WS_CAPTION) and
(not WS_MAXIMIZEBOX);
WINDOWS.SetWindowLong(
handle,
GWL_STYLE,
newStyle);
// SetWindowPos with SWP_FRAMECHANGED needs to be called at that point
// in order for the style change to be taken immediately into account.
WINDOWS.SetWindowPos(
handle,
0,
0,
0,
0,
0,
SWP_NOZORDER or
SWP_NOMOVE or
SWP_NOSIZE or
SWP_FRAMECHANGED or
SWP_NOACTIVATE);
end;
Looks like a bug to me with the OS. Under the conditions of your test case, hit test handling is wrong, default window procedure returns HTTOP when it should return HTBOTTOM. You can override hit test handling for a workaround:
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
begin
inherited;
if (Message.Result = HTTOP) and
(Message.Pos.Y > Top + Height - GetSystemMetrics(SM_CYSIZEFRAME)) then
Message.Result := HTBOTTOM;
end;
Well done, thanks. I confirm that it is an OS bug and nothing related to delphi (I was able to reproduce the problem with a simple window created using the WINDOWS API).
I now ended up with:
procedure TForm2.WMNcHitTest(
var msg : TWMNCHitTest);
begin
inherited;
case msg.result of
HTTOP:
begin
if msg.pos.y > top + height div 2 then
msg.result := HTBOTTOM;
end;
HTTOPRIGHT:
begin
if msg.pos.y > top + height div 2 then
msg.result := HTBOTTOMRIGHT;
end;
HTTOPLEFT:
begin
if msg.pos.y > top + height div 2 then
msg.result := HTBOTTOMLEFT;
end;
end;
end;
I am trying to use TFlowPanel component in the following manner:
Place on the main form Form1 component FlowPanel1: TFlowPanel.
Set Form1.Width = 400, FlowPanel1.Align = alTop, FlowPanel1.AutoSize = True, FlowPanel1.AutoWrap = True.
Place on the FlowPanel1 5 SpeedButtons and set their Width to 64.
Compile and run.
Reduce width of the form (something about Form1.Width = 200).
For some reason, the speedbuttons do not automatically line up in two rows when user resizes the form. Although, they do line up in two rows when AutoSize = False, AutoWrap = True.
What is the reason for this behavior and how to solve it?
Edit: I've found "quick and dirty" solution. The following code is the event handler to the TFlowPanel.OnResize event:
procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
with FlowPanel1 do
begin
AutoSize := False;
Realign; // line up controls
AutoSize := True; // adjust TFlowPanel.Height
end;
end;
However, I still wonder if there is a standard way to solve the problem.
I wasn't able to find the exact reason of such behavior in code, but basically you've challenged two sizing properties to fight, the AutoSize and Align. The problem is, I think, that when you resize a form, the control with AutoSize configured to True and Align set to alTop will first try to autosize the control and then align to top of its parent. What I can tell for sure, these two properties shouldn't be combined at least from their logical meaning.
What I would suggest to your workaround is turn off the autosize by default and in OnResize event turn it temporary on and back to off to automatically adjust the height. So in code it would change simply to:
procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
// there's no Realign here, since the AlignControls request is called
// at control resize, so here you have children already aligned, what
// you then need is to request the control to autosize the height and
// turn off the autosizing to the default, disabled state
FlowPanel1.AutoSize := True;
FlowPanel1.AutoSize := False;
end;
tl,dr: It's a bug in TFlowPanel.
Normally, the AutoSize and Align properties go together very well by default since this is taken care of already at TControl level, so I wondered why this happened. I noticed an overriden AlignControls method in TFlowPanel and thought to bypass it for testing purposes:
type
TWinControlAccess = class(TWinControl);
TAlignControls = procedure(Instance: TObject; AControl: TControl;
var Rect: TRect);
TFlowPanel = class(Vcl.ExtCtrls.TFlowPanel)
protected
procedure AlignControls(AControl: TControl; var Rect: TRect); override;
end;
TForm1 = class(TForm)
...
procedure TFlowPanel.AlignControls(AControl: TControl; var Rect: TRect);
begin
// Skip TCustomFlowPanel.AlignControls
TAlignControls(#TWinControlAccess.AlignControls)(Self, AControl, Rect);
end;
procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
// Do my own aligning of the last button
if FlowPanel1.ClientWidth < Button5.BoundsRect.Right then
begin
Button5.Left := 1;
Button5.Top := Button1.Height + 1;
end
else if FlowPanel1.ClientWidth > Button4.BoundsRect.Right + Button5.Width then
begin
Button5.Left := Button4.BoundsRect.Right;
Button5.Top := 1;
end;
end;
Now, this works as expected. So what's wrong with TFlowPanel's implementation of AlignControls? It looks like the following snippet is the reason:
if AutoSize then
Rect := TRect.Create(
Rect.Left,
Rect.Top,
Rect.Left + (ExplicitWidth - (Width - (Rect.Right - Rect.Left))),
Rect.Top + (ExplicitHeight - (Height - (Rect.Bottom - Rect.Top))));
When this part is commented out, the behaviour is as expected with Align set as well as not. Now, I would like to submit this to QC, but maybe I am overlooking some of its aspects. Please edit or comment when (and then why) this code indeed is needed.
Is it possible to remove a border of TabSheet (~4px)? I am using PageControl as a switch-panel instead of frames, windows etc. I want everything will be straight.
unit Unit1;
interface
uses
...,
CommCtrl;
type
TPageControl = class(ComCtrls.TPageControl)
private
procedure TCMAdjustRect(var Msg: TMessage); message TCM_ADJUSTRECT;
end;
TForm1 = class(TForm)
...
end;
...
procedure TPageControl.TCMAdjustRect(var Msg: TMessage);
begin
inherited;
if Msg.WParam = 0 then
InflateRect(PRect(Msg.LParam)^, 4, 4)
else
InflateRect(PRect(Msg.LParam)^, -4, -4);
end;
...
end.
If you don't mind using third-party tools then the easiest solution would probably be to use TjvPageControl from JVCL. It has ClientBorderWidth property which you are looking for.
An alternative is to use a TTabSet with a TPageControl: In the onCreate event of the form, place this code to hide the tab.
procedure TMainForm.FormCreate(Sender: TObject);
var
I : Integer;
begin
for I := 0 to Pred(PageControl1.PageCount) do
PageControl1.Pages[I].TabVisible := False;
PageControl1.Style := tsFlatButtons;
PageControl1.ActivePageIndex := 0;
TabSet1.Style := tsModernPopout;
TabSet1.SelectedColor := clMoneyGreen;
TabSet1.UnselectedColor := clGradientActiveCaption;
TabSet1.SelectedColor := clGradientActiveCaption;
end;
procedure TMainForm.TabSet1Change(Sender: TObject; NewTab: Integer;
var AllowChange: Boolean);
begin
PageControl1.ActivePageIndex := NewTab;
end;
nowadays, that is the answer. No need any code hacks Probably you use themes, if not, you should use that technology:
Project Options > Application> Appearance
Check on one of them as Default Style) than :
Tools > Bitmap Style Designer > Open Style
Navigate your vsf style file
(probably right here
"C:\Users\Public\Documents\Embarcadero\Studio[VERSION]\Styles
Now In Bitmap Style Designer.. navigate to:
Objects > Tabs > Frame > Bitmap
Click [...] three dot button of Bitmap In Inspector
Zoom to 800%
Pan/Scroll and Focus on to bitmap rectangle range.
Right Mouse Click to change Upper-Left, Left Mouse Click to change Lower-Right
region.
(so select inner rectangle to eliminate border bitmap
now you have borderless page controls)