How do I stop a form from exceeding the window height? - delphi

My form exceeds the window's height (when I use the splitter which changes a panel's height which in turn changes the form's size).
How can I stop it from resizing like that?

I assume you're changing the form size yourself, because I can't find a way to make the splitter do that automatically. You can get the Height of the screen using the Screen object in the Forms unit. You can simply test against Screen.Height or, if you want to better support multiple monitors, test against Screen.MonitorFromWindow(Handle).Height
Code sample, untested, should get you started:
var MaxFormHeight: Integer;
NewFormHeight: Integer;
M: TMonitor;
begin
// Get the monitor that's hosting the form
M := M := Screen.MonitorFromWindow(Handle);
MaxFormHeight := M.WorkAreaRect.Bottom - M.WorkAreaRect.Top - Top; // Take into account actual available monitor space and the Top of the window
// Do your stuff to calculate NewFormHeight
if NewFormHeight > MaxFormHeight then
NewFormHeight := MaxFormHeight;
Height := NewFormHeight;
end;

Related

How to create Tgrid at runtime

I am new to Delphi creating runtime Tgrid and want to add some rows and columns. I googled but not getting any thing. i am at level zero. what i have tried i mentioned it below.This code is showing nothing on my form.
procedure TForm1.Button1Click(Sender: TObject);
var
Grid : TGrid;
begin
Grid := TGrid.Create(Form1);
Grid.Visible := True;
Grid.Margins.Left := 10;
Grid.Margins.Right := 10;
Grid.Margins.Top := 10;
Grid.Margins.Bottom := 10;
Grid.RowCount := 5;
end;
The grid control must have a parent control. Indeed, naturally, the system must know where (on the screen) to put your control!
You probably want the grid to have the form as its parent. If so, just add Grid.Parent := Form1; after the construction of the grid.
Of course, when you add this missing line of code and get to see the grid, you'll very soon notice that your Margins assignments have no effect. That's because by default you control the position of the control (no pun intended) manually using its Top, Left, Height, and Width properties.
But should you start experimenting with the Align property, you'll discover the effect of the margins, if you also set AlignWithMargins to True. For instance, if you set Align to alClient, the control will occupy all of its parent's client area, save the margins.

Delphi XE2: Can a form cause all of its child controls to paint themselves offscreen?

I am working with this legacy app that has forms having a TScrollBox containing deeply nested TPanel descendents (up to 8 or so levels of nesting), each hosting controls that can cause the panel to be resized to make room for new child panels with their own controls. e.g. the user clicks a radio button, which resizes the panel hosting it to accommodate a new child panel with its own controls, including radio buttons that can cause the child to be resized and repopulated in the same manner.
The performance of this app was good until it was modified to use VCL styles. The group doing that work had to abandon the effort when it was found that the themed version might take over a minute to redraw itself after a single click.
It turned out that enabling double-buffering at the form level worked to make the repainting performance acceptable when the user clicks on a control. Scrolling is still a problem however. The controls on the form are not able to paint themselves fast enough to keep up with a user scrolling by rolling the mouse wheel. The scrollbar gets moved, a part of the client area gets redrawn, then the next mouse wheel message gets processed, a little bit of the client gets redrawn, and so on until the user quits rolling the mouse and there's time to redraw the whole thing. The code that does the scrolling looks like this:
procedure ScrollControl(Window: TScrollingWinControl; ScrollingUp: boolean; Amount: integer = 40);
var
Delta: integer;
// This is needed to tell the child components that they are moving,
// The TORCombo box, for example, needs to close a dropped down window when it moves.
// If Delphi had used standard scroll bars, instead of the customized flat ones, this
// code wouldn't be needed
procedure SendMoveMessage(Ctrl: TWinControl);
var
i: integer;
begin
for i := 0 to Ctrl.ControlCount - 1 do
begin
if Ctrl.Controls[i] is TWinControl then with TWinControl(Ctrl.Controls[i]) do
begin
SendMessage(Handle, WM_MOVE, 0, (Top * 65536) + Left);
SendMoveMessage(TWinControl(Ctrl.Controls[i]));
end;
end;
end;
begin
Delta := Amount;
if ScrollingUp then
begin
if Window.VertScrollBar.Position < Delta then
Delta := Window.VertScrollBar.Position;
Delta := - Delta;
end
else
begin
if (Window.VertScrollBar.Range - Window.VertScrollBar.Position) < Delta then
Delta := Window.VertScrollBar.Range - Window.VertScrollBar.Position;
end;
if Delta <> 0 then
begin
Window.VertScrollBar.Position := Window.VertScrollBar.Position + Delta;
SendMoveMessage(Window);
end;
end;
What I think I want to do is to (right before the outermost call to SendMoveMessage)
make a DC for Window
allocate an offscreen bitmap the size of Window's client area
arrange for all drawing operations on Window and its children to be drawn to the offscreen bitmap
Call SendMoveMessage and
bitblt the offscreen bitmap into Window's client area and free the bitmap and DC.
But I cannot see how to do step 3. Is it even possible?

Is it possible to set Form.Width to 10000 pixel?

When you compile blank form and you try to resize it's width with mouse, it will stop probably, when it's clientwidth is near screen resolution width.
It is not possible set wider form even in designer. (Strange enough, I would never suppose it happens). I also played with Constraints too, but it is no solution either.
Is it possible to set Form.Width to 10000 pixel?
Window size is limited by system - you can retrieve this value using function GetSystemMetrics(SM_CXMAXTRACK) - it is 1292 for my 1280x1024 display.
To allow your form be wider, you can treat message WM_GETMINMAXINFO providing desired max size:
procedure WMGETMINMAXINFO(var M: TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
...
procedure TForm1.WMGETMINMAXINFO(var M: TWMGetMinMaxInfo);
begin
M.MinMaxInfo.ptMaxTrackSize.X := 5000;
M.Result := 0;
inherited;
end;
With such message handler I am able to set Width := 5000; successfully in runtime.
Normally you should be able to use Constraints property of the form and set its MaxWidth to achieve this as in this answer but WM_GETMINMAXINFO of TCustomForm is defective in Delphi 7. Calling of ConstrainedResize method from the message handler depends on some FSizeChanging boolean field, which unfortunately is never set to true. This is corrected and the field is removed somewhere in between D2007 and DXE.

Positioning A Form From A SysTray Icon

I'd like to display a form off of a systray icon event, which just shows some information next to the taskbar and disappears itself after some time. The main issue I'm running into is positioning the form in a way that it is both in the correct position and visible. I found a couple of ways to determine where the icon is, but in testing I found them inconsistent based on OS (I attempted this in another app and ended up giving up and using a centered modal form). For example:
procedure GetWorkAreaRect(var outrect: TRect);
// returns the dimensions of the work area.
begin
Systemparametersinfo(SPI_GETWORKAREA, 0, #outrect, 0);
end;
The problem when that works is determining from there where to put the form (above, below, left, right). Any suggestions?
Edit: The problem is in positioning the form in relationship to the system tray icon, not necessarily determining where the system tray icon is. I made another attempt and got it working as long as some conditions are met. Most notably, it doesn't work if the taskbar is set to auto-hide, because the assumption is made that the click is made outside of the work area. This is not true when the form is set to auto-hide.
function PositionForm(X, Y, Width, Height: Integer): TPoint;
// receives mouse-click position in X and Y, form width and height in width and height
// returns Left and Top in TPoint.X and TPoint.Y.
var
workrect: TRect;
resrect: TPoint;
begin
GetWorkAreaRect(workrect);
if Y > WorkRect.Bottom then // taskbar is on bottom
begin
resRect.X := WorkRect.Right - Width;
resrect.Y := WorkRect.Bottom - Height;
end
else
if X > WorkRect.Right then // taskbar is on right
begin
resrect.X := WorkRect.Right - Width;
resrect.Y := WorkRect.Bottom - Height;
end
else
if X < WorkRect.Left then // taskbar is on left
begin
resrect.X := WorkRect.Left;
resrect.Y := WorkRect.Bottom - Height;
end
else
if Y < WorkRect.Top then // taskbar is on top
begin
resrect.X := WorkRect.Right - Width;
resrect.Y := WorkRect.Top;
end;
Result := ResRect;
end;
So on the surface, it seems the issue is to find an independent way to determine where the taskbar resides...or could the logic be extended above to take care of this?
When your notification icon receives the message corresponding to an action, you can query at that point to find out an associated point on the screen.
For example if you are handling WM_RBUTTONUP, WM_CONTEXTMENU etc. in your icon's message procedure you can call GetMessagePos to find out the position on the icon associated with the message.
I wrap this up with the following function so that I can decode the message into a TPoint:
function MessagePoint: TPoint;
begin
Result := TSmallPoint(GetMessagePos());
end;
So what you can do is, in your icon's message procedure, make a note of this point. When you need to show the form, use this point to determine where your icon lives. Since the point can be in the taskbar, you'll need to clip it into the work area.
After your question update it seems you want to know how to find out the location of the taskbar. Do that by calling SHAppBarMessage passing ABM_GETTASKBARPOS.
Windows does not expose a native way to query where system tray icons are positioned, or even if they are visible at all. But you can do it manually with some lower level API trickery, as demonstrated in the following article:
CTrayIconPosition - where is my tray icon?
That works up to XP, at least, maybe even Vista. Windows 7 drastically redesigned the way the system tray acts, so I do not know if these techniques still work anymore.
You can use TJvDesktopAlert to display simple notifications, if you have JCL and JVCL.
procedure TForm1.ShowDesktopAlert(const AHeader, AMessage: string);
begin
with TJvDesktopAlert.Create(nil) do
begin
StyleOptions.DisplayDuration := 5000;
HeaderText := AHeader;
MessageText := AMessage;
AutoFree := True;
Execute;
end;
end;

How to find the real size ("logical area") of a TScrollBox

I need to find the entire size (also called "logical area") of a TScrollBox - as opposite to visible area that you get via Width and Height property (or ClientWidth ClientHeight).
I want to create some controls inside that TScrollBox. One of them (called TViewer) needs to be as high as the TScrollBox itself. The thing is that during creation, the TScrollBox is scrolled down to show last created control. So, using Top=1 will not work because my control will have top=1 which is not the top of the logical area.
Delphi 7
Drop a component, like a TLabel, on the TScrollBox.
Set the component's Left and Top properties to 0.
Set the component's Visible property to False.
Now you always have the origin. The "logical height" is now:
myScrollBox.Height + (myOriginControl.Top * -1);
Maybe ScrollBox.HorzScrollBar.Range and ScrollBox.VertScrollBar.Range + the corresponding .Positions are what you need.
Look at Scrollbars:
ScrollBox1.VertScrollBar.Range
ScrollBox1.HorzScrollBar.Range
It can be less than height and width if the scrollbox logical area is not bigger than phisical area (scrollbars not visible in that case)
Or use this to get the max of both:
var
AHeight, AWidth: Integer;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, ScrollBox1.Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, ScrollBox1.Width);
ShowMessageFmt('%d,%d', [AHeight, AWidth]);
end;
Edit
From #Altar comments, I can add the logical height and/or width is not the problem. If you want to add any control which occupies all the height of the scrollbar, use the AHeight from the above calculation, but set the Top to the negative of VertScrollBar.Position, something like this:
procedure TForm2.Button3Click(Sender: TObject);
var
AHeight, AWidth: Integer;
Btn: TButton;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, Width);
Btn := TButton.Create(Self);
Btn.Parent := ScrollBox1;
Btn.Left := 15;
Btn.Top := -ScrollBox1.VertScrollBar.Position;
Btn.Height := AHeight;
end;
Im not sure i understand exactly what you want to do, but to get the complete area defined as "scrollable" you would have to write ScrollBox.HorScrollBar.Range + ScrollBox.Clientwidth (and the same thing for the vertical section). The scrollbox always deducts the visible "page" size from the total. So if you define a height of 1000 pixels, with 100 pixels showing - it will have a scrolling range of 900. You have to add the clientheight to get the rest.
Also, to get the correct "top" position you will have to read Canvas.Cliprect.Top, since a scrolling window does not change the top position of sub-controls. Windows handles this for you and only tells you what to re-draw once the scrollbars are initialized.
Since you want to create a control that is just as high as the complete scrollable area, i presume you are creating an editor of sorts?
If so you would probably get better results taking a look at SynEdit and extract the base-class that adds scrollbars to a normal TCustomControl (it's quite easy). That way you can control both the painting and the layout of your controls.
Here is one I wrote for Lazarus and FreePascal a while back. If you add messages to the uses clause and prefix the message handlers with WM rather than TLM it will compile under Delphi.
(code to long, had to use external link): http://delphimax.wordpress.com/2010/09/20/platform-independent-image-component-for-lazarus/
I have tried to do that, and believe me, I was not able to.
If you have the instances of the controls that are inside TScrollBox, you can use them to calculate (not precisely) the area.
Here is a complicated (but complete) solution:
I create my first child control during TScrollBox.Create (when TScrollBox does not have yet a scrollbar)
Set Child.Top:= 1
Create the rest of the child controls (this may force the TScrollBox to show the scrollbar)
Scroll the TScrollBox up
Use one of the above solutions to calculate the height of the TScrollBox

Resources