The form "Form1" contains a panel.
On this panel we want to embed a second form "Form2".
But not only the components should be embedded, but also the functionality.
I am using a VCL form.
I have already tried this tutorial on this page.
How to put a form in panel
unit parent;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, child;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Form2 := TForm2.Create(Panel1);
with Form2 do
Name := 'MyForm';
Parent := Panel1;
Width := 500;
Height := 500;
Top := 10;
Left := 10;
Show;
end;
end.
unit child;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
btnCalc: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Label1: TLabel;
procedure btnCalcClick(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.btnCalcClick(Sender: TObject);
begin
edit3.Text:=edit1.Text+edit2.Text;
end;
end.
The form is not displayed at startup and Form2 is not embedded in Form1.
For future reference, when you post a question about code, copy - paste actual code and inform possible shown errors.
In the first edition of your question, your code recreated Form1 within the constructor of the same form. After some time the program fails with an out of resources error.
In your second edition, where you corrected erroneous references of Form1 to Form2 your code is still missing a begin .. end pair around the properties you want to set after with Form2 do. This error leads to a stack overflow because you are setting Form1 parent to Panel1 which is a child of Form1, iow a chicken - egg dilemma.
Because you did not mention either of these errors, one can only conclude that your code is different from what you have posted, or, you think that error messages are not important. WRONG! So, please take the lesson and pay attention to post actual code and inform about any error messages.
Also, if your question is about appearance of a form, post the .dfm file content.
With the following code Form2 is shown embedded in the Panel1 of Form1 (I removed the width and height settings to keep the image smaller):
procedure TForm1.FormCreate(Sender: TObject);
begin
Form2 := TForm2.Create(Panel1);
with Form2 do
begin
Name := 'MyForm';
Parent := Panel1;
Top := 10;
Left := 10;
Show;
end;
end;
The Top and Left properties don't look as expected. That is because the default value of the TForm.Position property is poDefaultPosOnly which means that the form bypasses the Top and Left settings and leaves it to the OS to decide on the location. However since the form is given a parent that doesn't apply either and the form is just placed at position 0,0. Add the line regarding position property below (or set it in Form2 designer).
...
Parent := Panel1;
Position := poDesigned; // add this line
BorderStyle := bsNone; // and this for a better embedded appearance
Top := 10;
...
The result is now:
Related
How does one change a FMX.TMemo font size # runtime? In the following app the Spinbox1.Change method does not change the Memo.Font.Size. I have tried the BeginUpdate() and EndUpdate() methods of the TMemo. I have also tried Memo1.Repaint() and nothing seems to work. I have looked at every property, function and procedure for TMemo, but, I can't find what I need. Later versions of Delphi have TTextSettings for TMemo, but, XE5 does not. I also tried a class helper for TMemo to add a TTextSettings property, but, to no avail.
unit Unit13;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
FMX.Edit, FMX.Layouts, FMX.Memo;
type
TForm13 = class(TForm)
Memo1: TMemo;
ToolBar1: TToolBar;
SpinBox1: TSpinBox;
procedure FormCreate( Sender : TObject );
procedure SpinBox1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form13: TForm13;
implementation
{$R *.fmx}
procedure TForm13.FormCreate( Sender : TObject );
begin
Memo1.Font.Size := SpinBox1.Value;
end;
procedure TForm13.SpinBox1Change(Sender: TObject);
begin
Memo1.Font.Size := SpinBox1.Value;
end;
end.
working with FMX is certainly not like the VCL.
The TMemo as many other controls has a property StyledSettings which has a few subfields, Family, Size, Style, FontColor and Other.
All except Other are True by default, so the control would automatically follow any used style setting. Set StyledSettings.Size = False in the Object Inspector and the TMemo will follow your own font size settings.
As I don't have any older XE-version than XE7, the above is tested with XE7. If XE5 doesn't have the corresponding settings, I will remove this answer as it doesn't answer your question.
Alternatively, in code you can write:
with Memo1 do
StyledSettings := StyledSettings - [TStyledSetting.ssSize];
This should work in XE5.
I made the following code but something does not work.
unit Unit9;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, ADODB, Grids, DBGrids, ExtCtrls;
type
TForm9 = class(TForm)
edt1: TEdit;
procedure FormCreate(Sender: TObject);
end;
var
Form9: TForm9;
const
EM_SETCUEBANNER = $1501;
implementation
uses
Unit2;
{$R *.dfm}
procedure TForm9.FormCreate(Sender: TObject);
var
Banner: String;
buf: array [0..$ff] of Char;
begin
Banner := UTF8Encode('Введите логин');
Utf8ToUnicode(PWideChar(#buf), PAnsiChar(Banner), Length(Banner));
SendMessage(edt1.Handle, EM_SETCUEBANNER, 0, Integer(#buf));
end;
end.
For example, as on some websites, an edit control displays "Login" until something is written.
My application uses mouse wheel scrolling in a number of places.
Thus I’ve written a mouse wheel handler, and this handler works out where the mouse is located before calling the appropriate object method.
On most PCs this works fine, but I have one laptop here where it does not. Despite the handler receiving the correct mouse co-ordinates from Windows, calls to FindVCLWindow are returning nil. This is however only happening when I use the laptop’s internal touch pad. External USB mice work fine.
I’ve updated the laptop’s touch pad driver to the latest available from the manufacturer's web site, but to no avail.
How else can I fix this?
Here’s the code:
unit Mouse_Wheel_Testing;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Grids;
type
TForm1 = class(TForm)
Panel1: TPanel;
StringGrid1: TStringGrid;
Mouse_Coordinates: TEdit;
Control_Name: TEdit;
Button1: TButton;
procedure MouseWheelHandler(var Message: TMessage); override;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.MouseWheelHandler(var Message: TMessage);
var
Target_Control: TWinControl;
begin
with TWMMouseWheel(Message) do
begin
Mouse_Coordinates.Text := IntToStr(XPos) + ', ' + IntToStr(YPos);
Target_Control := FindVCLWindow(Point(XPos, YPos));
if Target_Control <> nil then
Control_Name.Text := Target_Control.Name
else
Control_Name.Text := 'nil';
end;
end;
end.
The reason why FindVCLWindow was returning nil was that WindowFromPoint was returning an incorrect handle. This in turn was the result of a setting in the laptop relating to the behavior of its touch pad when in scrolling mode. This option needed to be set correctly for the correct handle to be returned.
Since my application cannot rely on the user having their laptop set correctly, I have now written a new FindComponent function which is based upon ChildWindowFromPointEx. The following function now resides within the mouse wheel handler:
function Find_Control: TWinControl;
var
Parent: HWND;
Child: HWND;
Position: TPoint;
begin { Find_Control }
Result := nil;
Parent := Self.Handle;
with TWMMouseWheel(Message) do
Position := ScreenToClient(Point(XPos, YPos));
Child := ChildWindowFromPointEx(Parent, Position, CWP_SKIPINVISIBLE);
while (Child <> 0) and (Child <> Parent) do
begin
Result := FindControl(Child);
Position := Point(Position.X - Result.Left, Position.Y - Result.Top);
Parent := Child;
Child := ChildWindowFromPointEx(Parent, Position, CWP_SKIPINVISIBLE);
end; { while (Child <> 0) and (Child <> Parent) }
end; { Find_Control }
I am creating a program that moves through an array of records and save these student records to a file.
However I now wish to reload the data (StudentName,Class,Grade) back into the array and subsequently display them in a list box on another form.
I have tried a few methods but with no success.
This is the code that wrote the file:
unit NewStudent;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, studentdata;
{ TFormNewStudent }
Type
TFormNewStudent = class(TForm)
Button1: TButton;
ButtonAddStudent: TButton;
Button3: TButton;
ComboBoxPredictedGrade: TComboBox;
EditClass: TEdit;
EditName: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure ButtonAddStudentClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
Type
TPupil = Record
Name:String[30];
ClassGroup:String;
ComboBoxPredictedGrade:Integer;
end;
var
FormNewStudent: TFormNewStudent;
StudentRecArray : Array[1..30] of TPupil;
StudentNo:integer;
studentFile:TextFile;
implementation
{$R *.lfm}
{ TFormNewStudent }
procedure TFormNewStudent.Button1Click(Sender: TObject);
begin
FormStudentData.visible:=true;
FormNewStudent.visible:=false;
end;
procedure TFormNewStudent.Button3Click(Sender: TObject);
begin
FormStudentData.visible:=False;
FormNewStudent.visible:=True;
end;
procedure TFormNewStudent.ButtonAddStudentClick(Sender: TObject);
var
newStudent:string;
Begin
assignfile(studentFile,'G:\ExamGen\studentfile.txt');
StudentRecArray[StudentNo].Name:=EditName.text;
StudentRecArray[StudentNo].ClassGroup:=EditClass.text;
StudentRecArray[StudentNo].ComboBoxPredictedGrade:=ComboBoxPredictedGrade.ItemIndex;
append(studentFile);
newStudent:=(StudentRecArray[StudentNo].Name)+','+(StudentRecArray[StudentNo].ClassGroup)+','+(IntToStr(StudentRecArray[StudentNo].ComboBoxPredictedGrade));
writeln(studentFile,newStudent);
closefile(StudentFile);
StudentNo := StudentNo + 1;
end;
procedure TFormNewStudent.FormCreate(Sender: TObject);
begin
ComboBoxPredictedGrade.Items.Add('A');
ComboBoxPredictedGrade.Items.Add('B');
ComboBoxPredictedGrade.Items.Add('C');
ComboBoxPredictedGrade.Items.Add('D');
ComboBoxPredictedGrade.Items.Add('E');
ComboBoxPredictedGrade.Items.Add('U');
end;
end.
ScreenShot 1: StudentFile
ScreenShot 2: AddStudent Form
Answer given by Zamrony P. Juhara is correct, but your approach here may be not the most convenient. You define record which contains information about each student, then you write procedures to write this record to file and another one to read it. If you'll eventually change format of your record, you'll have to rewrite this code also. There are better ways, in my opinion.
You can define record containing only simplest members, like Ken White suggested:
TPupil = Record
Name:String[30];
ClassGroup:String[20]; //some convenient value
ComboBoxPredictedGrade:Integer;
end;
Such a record have fixed size and contains all needed information in itself (version with ClassGroup:String actually stores pointer to another area in memory where your string is), and then you can save and load it extremely easy:
var
myFile : File of TPupil;
procedure TFormNewStudent.ButtonAddStudentClick(Sender: TObject);
Begin
assignfile(studentFile,'G:\ExamGen\studentfile.txt');
StudentRecArray[StudentNo].Name:=EditName.text;
StudentRecArray[StudentNo].ClassGroup:=EditClass.text;
StudentRecArray[StudentNo].ComboBoxPredictedGrade:=ComboBoxPredictedGrade.ItemIndex;
append(studentFile);
Write(studentFile,StudentRecArray[StudentNo]); //THAT'S IT!
closefile(StudentFile);
inc(StudentNo);
end;
procedure TFormNewStudent.ReadFromFile;
begin
AssignFile(myFile,'G:\ExamGen\studentfile.txt');
Reset(studentFile);
StudentNo:=1;
while not Eof(studentFile) do begin
Read(studentFile,StudentRecArray[i]);
inc(StudentNo);
end;
end;
There is little drawback: file is not so readable as it was before, because Integer is saved exactly as 4-byte value, not its decimal representation.
There is much more interesting possibilities if you move from record to class, in that case you can use streaming system in a way as IDE saves forms to disc, in .dfm or .lfm files, so you'll be able to automatically save complex ierarchies of objects and load them back.
var
myFile : TextFile;
text : string;
lines : TStringList;
i : integer;
...
lines := TStringList.Create();
AssignFile(studentFile,'G:\ExamGen\studentfile.txt');
Reset(studentFile);
i:=1;
while not Eof(studentFile) do
begin
ReadLn(studentFile, text);
lines.CommaText := text;
studentRecArray[i].Name := lines[0];
studentRecArray[i].ClassGroup := lines[1];
studentRecArray[i].ComboBoxPredictedGrade := StrToInt(lines[2]);
inc(i);
end;
CloseFile(studentFile);
lines.Free();
In my extended TComboBox class, I overrided ComboWndProc() procedure handler, but I was not able to detect neither CN_VSCROLL nor WM_VSCROLL messages from the scroll bar of the List (FListHandle).
I basically want to implement an infinite scroll using winapi.
I imagine that, to do what I want, I basically would need to know the track bar position of the scroll so when the track bar touch the line down button I would add more data to strings.
The idea is simple and maybe naive, but I could start from there and see what problems I would have.
Is it possible to do such a thing?
Is there a way to track scroll bar messages from TComboBox?
More importantly:
If yes, How?
If no, Why?
You can use WM_VSCROLL, to do so you have to subclass the listbox control of the combobox. CN_VSCROLL will not work because the listbox part of the combobox is not a VCL control.
Below example is essentially from this answer of Kobik, included here for the sake of completeness.
type
TForm1 = class(TForm)
ComboBox1: TComboBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FComboListWnd: HWND;
FComboListWndProc, FSaveComboListWndProc: Pointer;
procedure ComboListWndProc(var Message: TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
Info: TComboBoxInfo;
begin
ZeroMemory(#Info, SizeOf(Info));
Info.cbSize := SizeOf(Info);
GetComboBoxInfo(ComboBox1.Handle, Info);
FComboListWnd := Info.hwndList;
FComboListWndProc := classes.MakeObjectInstance(ComboListWndProc);
FSaveComboListWndProc := Pointer(GetWindowLong(FComboListWnd, GWL_WNDPROC));
SetWindowLong(FComboListWnd, GWL_WNDPROC, Longint(FComboListWndProc));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
SetWindowLong(FComboListWnd, GWL_WNDPROC, Longint(FSaveComboListWndProc));
classes.FreeObjectInstance(FComboListWndProc);
end;
procedure TForm1.ComboListWndProc(var Message: TMessage);
begin
case Message.Msg of
WM_VSCROLL: OutputDebugString('scrolling');
end;
Message.Result := CallWindowProc(FSaveComboListWndProc,
FComboListWnd, Message.Msg, Message.WParam, Message.LParam);
end;