Delphi - Maximize a form to a particular screen - delphi

Quite a simple one i would think, but i need to be able to Maximize a form to a particular screen. Cant seem to find any Delphi specific info.
I can remember the forms position over subsequent application loads. However, when i restore the position, then call WindowState := wsMaximized, the form moves to the other screen! (i do have other forms also visible on that screen - it appears its maximizing to the 'active screen')
So i need a function like so:
procedure Maximize(const aScreenIndex : Integer);
begin
if aScreenIndex < Screen.MonitorCount then
//Maximize to that screen
end;

Intercept the WM_GETMINMAXINFO message and adjust the coordinates inside its MINMAXINFO structure as needed.

Set Form.Position to poDesigned at design time
In Form.FormShow or your Maximize procedure:
procedure Maximize(const aScreenIndex : Integer);
begin
if aScreenIndex < Screen.MonitorCount then
begin
//Maximize to that screen
Myform.Left := screen.Monitors[aScreenIndex ].Left;
Myform.WindowState := wsMaximized;
end;
end;

Related

Delphi : losing focus from parent when clicking on child

I am having a problem with an effect trigger (shadow effect). I did put the trigger to be ismouseover = true. So, when I put the mouse onto a panel (parent), the shadows activate, and it works fine until I start putting some buttons inside the panel (children).
The shadows effect goes off when the mouse is over the children.
So, is there anyway to keep focus on the parents while being focused on the children?
I did try to change the trigger of the effects (from ismouseover to isfocused), but it didn't give any different results.
As said, your design is wrong, then you can remove the trigger and do it manually :
// show or hide shadow effect
procedure TForm2.ShowShadowEffect(AValue: boolean);
begin
if ShadowEffect1.Enabled <> AValue then
ShadowEffect1.Enabled := AValue;
end;
// show when enter on panel
procedure TForm2.Panel1MouseEnter(Sender: TObject);
begin
ShowShadowEffect(True);
end;
// hide when leave the panel
procedure TForm2.Panel1MouseLeave(Sender: TObject);
begin
ShowShadowEffect(False);
end;
// keep visible when over button
procedure TForm2.Button1MouseEnter(Sender: TObject);
begin
ShowShadowEffect(True);
end;

How to stop Screen.Cursor affects all controls on the form?

I will try to simplify my problem. If for example you drop 2 TSpeedButton and do:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
SpeedButton2.Cursor := crHandPoint; // note I'm setting other cursor than crDefault
end;
The SpeedButton2.Cursor remains showing Screen.Cursor which was set to crHourGlass.
I have looked into the TScreen.SetCursor code, and realize it sets the cursor for the entire form.
My question: is it somehow possible to use the Screen.Cursor for the entire form, BUT without impacting some control(s) which I want to set other cursor.
The same happens with a TButton. I don't mind placing the SpeedButton on a windowed control if I can somehow control it's cursor while Screen.Cursor is set to crHourGlass.
Thanks.
This is intentional behavior as explained in the documentation for TScreen.Cursor:
... When Cursor is crDefault, the individual objects determine the
cursor image. Assigning any other value sets the mouse cursor image
for all windows belonging to the application. The global mouse cursor
image remains in effect until the screen's Cursor property is changed
back to crDefault. ..
Windowed controls handle their cursors in TWinControl.WMSetCursor procedure, handler of WM_SETCURSOR message, where they explicitly set the screen cursor if it is anything other than crDefault and disregard their own cursor.
So to change the behavior you can handle the mentioned message. For a TButton interposer, an example could be:
procedure TButton.WMSetCursor(var Message: TWMSetCursor);
begin
if (Cursor <> crDefault) and (Message.HitTest = HTCLIENT) then begin
Message.Result := 1;
Windows.SetCursor(Screen.Cursors[Cursor]);
end else
inherited;
end;
Graphic controls' cursors are handled by their parent TWinControl. So to change the behavior of a speed button, you would still need to handle the same message on its parent. This would likely be impractical since the parent class might not be known beforehand.
Still, a very non-generalized implementation, for example for a graphic control placed directly on the form, might look like the below:
procedure TForm1.WMSetCursor(var Message: TWMSetCursor);
var
SmPt: TSmallPoint;
Control: TControl;
begin
DWORD(SmPt) := GetMessagePos;
Control := ControlAtPos(ScreenToClient(SmallPointToPoint(SmPt)), True);
if Assigned(Control) and Boolean(Control.Tag) then begin
Message.Result := 1;
Windows.SetCursor(Screen.Cursors[Control.Cursor])
end else
inherited;
end;
Above example would require the graphic control to have a non zero tag value. E.g.:
procedure TForm1.Button1Click(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
SpeedButton1.Cursor := crHandPoint;
SpeedButton1.Tag := 1;
end;

Issue with controls alignment in dynamically created Frame

My program uses dynamically created frames and sometimes I get an issue that their controls are improperly aligned.
I use my own container control inherited from TPanel, but the same problem can be found when using the GridPanel as well.
Here is the source of a test program that reproduces the problem (with compiled exe).
The key code snippets:
in the main form:
//creating Frame from the main form
procedure TForm1.FormCreate(Sender: TObject);
begin
f := TFrame2.Create(Self);
f.Parent := Self;
end;
in the frame:
//constructor of the Frame
constructor TFrame2.Create(AOwner: TComponent);
begin
inherited;
Edit1.Clear;// the problem appears
end;
The frame and all its controls are aligned and must have the width of the main form, but Edit1 and ComboBox1 are visually not aligned until you resize the form manually (Sending WM_SIZE has no effect).
But if you comment the Edit1.Clear line everything will work fine from the program start. This code is not specific for the error and you can enter here e.g. ComboBox1.Items.Add('') etc.
If the frame is created statically or the GridPanel is changed to Panel the problem disappears.
I have made a new test2 version thanks to #quasoft, it works better - now the controls are horizontally aligned proper but vertically combobox is not in the right place that can be seen by changing the form size.
Quick fix:
The quick solution to your problem is to use Text property of TEdit, instead of the Clear method - as already said, replacing Edit1.Clear with Edit1.Text := ''.
Understanding the problem
But you need to understand this problem better if you plan to use frames in Delphi in the long term, or they will haunt you while you sleep (joking).
The real problem is that you are modifying the state of the frame before a Parent has been assigned to it.
procedure TForm1.FormCreate(Sender: TObject);
begin
f := TFrame2.Create(Self); // <--- Your text edit is cleared inside
f.Parent := Self; // <--- Your frame is attached to the form here
end;
Doing so does not allow the TGridPanel component to take in account the width, height and position of parent when calculating the size of its columns and rows.
Using Text property works, because the property setter does not change the text of the control directly, but sends a message for the purpose to the message queue:
Except from Controls.pas:
...
procedure TControl.SetTextBuf(Buffer: PChar);
begin
Perform(WM_SETTEXT, 0, Longint(Buffer));
Perform(CM_TEXTCHANGED, 0, 0);
end;
procedure TControl.SetText(const Value: TCaption);
begin
if GetText <> Value then SetTextBuf(PChar(Value));
end;
...
Which in effect, causes the text to actually change after you have assigned the Parent of the frame - as the message queue will be processed a little bit after the form create method completes.
Clear method on the other hand directly changes the text:
Excerpt from StdCtrls.pas:
...
procedure TCustomEdit.Clear;
begin
SetWindowText(Handle, '');
end;
...
A better solution
As you already learned, the quick fix works only in the specific example you provided.
A better solution is to create an Init method in your frame and call this method from the main form after Parent has been assigned:
Your frame:
procedure TFrame2.Init;
begin
Edit1.Clear;
ComboBox1.Items.Add('Foo Bar');
end;
Your main form:
procedure TForm1.FormCreate(Sender: TObject);
begin
f := TFrame2.Create(Self);
f.Parent := Self; // <--- Your frame is attached to the form here
f.Init; // <--- Calls initialization code of frame
end;

Forcing a Delphi form to draw when its not visible

I have a form that I scrape a bitmap to send to a small embedded TFT display. I then inject touch events from the display into the form to activate the controls. This all works very well unless the form is not visible. If its moved off the visible desktop, minimized or closed it will not get a paint event and never updates.
Is there a way to force the canvas to redraw itself visible or not?
All of the obvious things like called repaint does not work.
Yes you can use the PaintTo method on a form:
procedure TForm1.Button1Click(Sender: TObject);
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
Bitmap.Width := Form2.Width;
Bitmap.Height := Form2.Height;
Form2.PaintTo(Bitmap.Canvas, 0, 0);
Image1.Picture.Assign(Bitmap);
Bitmap.Free;
end;
Im my small example I made a project with two forms Form1 and Form2. On Form2 i placed a label and Timer.
Here's the code for Form2
procedure TForm2.Timer1Timer(Sender: TObject);
begin
Label1.Caption := FloatToStr(now);
end;
And i woks out well.

Delphi XE5 Firemonkey TTabItem and TEdit repaint coordination

I created a TTabControl with two TTabItems. On each TTabItem there is one (or more) TImageViewers and several TEdits. When I click on the TImageViewer, a modal screen pops up, I set some values, and I want to report those values to the user through the TEdits. So on returning from the Modal screen,
I execute
editn.text := whateveritis;
I then say
editn.repaint;
Nothing happens. I say TTabItem.repaint. Nothing happens. I click the other TTabIem and then come back to the first TabItem and, voila, the Edit control contains the right information. So my editn.text := whateveritis must be working (that's the only write to the TEdit), but I can't get the blinkin' control to show the result without going off-tab. How do I get it to redisplay as soon as I change the content? Do I need to write an OnChange routine that is one line, self.repaint? Seems ugly, and I'd hope there's a more global approach. Suggestions?
In light of initial comments, let me give more details. Setup: In main screen, drop in tabcontrol, and in tabcontrol drop in 2 tabitems. In tabitem1, drop in a timageviewer and 4 tedits (plus other stuff, probably irrelevant). Image gets dropped into the imageviewer (and displays correctly). The onclick event activates the following (ellipsis cuts out irrelevant code):
procedure TSCKMain.ImageViewer1Click(Sender: TObject);
var
lochold, scrollhold : tpoint;
backfromwavform : tmodalresult;
begin
lochold.X := mouseloccallback.x;
lochold.Y := mouseloccallback.y;
scrollhold.X := round(imageviewer1.ViewportPosition.X);
scrollhold.Y := round(imageviewer1.ViewportPosition.Y);
…
repeat backfromwavform := Wavform.Showmodal until backfromwavform<>mrnone;
case backfromwavform of
mrOK : begin {blue end}
Specsingle.BlueEnd.X := lochold.X;
Specsingle.BlueEnd.y := lochold.y;
edit13.Text := inttostr(Specsingle.BlueEnd.X);
Edit14.Text := inttostr(Specsingle.BlueEnd.y);
PublicWindowFlag := 'RePlot';
end;
mrContinue : begin {red end}
Specsingle.RedEnd.X := lochold.X;
Specsingle.RedEnd.y := lochold.y;
edit15.Text := inttostr(Specsingle.RedEnd.X);
Edit16.Text := inttostr(Specsingle.RedEnd.y);
PublicWindowFlag := 'RePlot';
end;
…
end;
if PublicWindowFlag<>'Cancel' then
if PublicWindowFlag='RePlot' then
begin
specsingle.RegenImage;
end
else
showmessage('Single image semaphore error. Debug.');
Imageviewer1.scrollto(scrollhold.X-Imageviewer1.viewportposition.X, scrollhold.y-Imageviewer1.ViewportPosition.Y);
end;
The modal screen sends back either mrContinue or mrOK correctly, and the appropriate case executes. However, edit13, edit14, edit15, and edit16 do not change their content. However, if I click over to Tabitem2 and back to Tabitem1, they DO repaint and DO contain the correct characters, which they could only have gotten from the above code. Conclusion: somehow, the edits aren’t repainting independently, but it’s not clear why.
Got it. The canvas for the imageviewer, the canvas for the bitmap in the imageviewer, and the canvas for the parent form are all in play. One must be sure that the canvas is the right one. As soon as scenes got untangled between imageviewer.bitmap and everything else, the edits worked as one would expect.

Resources