Changing a TMemo font size # runtime - firemonkey

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.

Related

Delphi: How to embed a VCL form into a panel

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:

Scrolling TreeView updated by Timer in Delphi 10.2 Tokyo form freezes the form in Windows 10

running the form below and continuously scrolling the treeview up and down will freeze the form on (my) Windows 10. The scrollbar, the form title bar and its buttons all become unresponsive, but will still update on timer events. Clicking on treeview still works normally.
On (my) Windows 7, the freezing does not happen.
On (my) Windows 10 When you click or Alt-tab away from the application and back, form becomes responsive again. Which means that whenever I switched away to Delphi IDE to pause and see what was going on, problem was gone. On one occasion I did manage to get form so stuck that switching away from it to debugger did not unfreeze it, and the Call Stack was deep inside UxTheme.dll.
As you can see in the code I have a sort of a workaround but it is not very satisfying. Can anybody explain what is going on here ?
I did my best to make the code sample below easy to run from scratch, that's why I included the .dpr The problem originates from a much more complex form updated from a background thread.
Update: just tried same sort of thing with TListView and I get no freezing.
Update: as nolaspeaker suggested, compiling without "enable runtime themes" fixes the problem.
Update: my original complex form was not fixed by compiling without "enable runtime themes" - as the app uses a custom manifest. However using DisableThemesApp from here How to switch an Application between Themed and not Themed at run-time? helped. In fact skipping STAP_ALLOW_NONCLIENT was enough.
The only downside being, on Windows 10 the app now looks like something from Deliverance - i.e. inbred :). So I will continue using my GetLastInputInfo bodge until someone suggests something better.
unit Win10Freezing;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.ComCtrls;
type
TWin10FreezingForm = class(TForm)
private
FTreeView: TTreeView;
procedure TimerTimer(Sender: TObject);
function HasASecondPassedSinceLastInput: boolean;
public
constructor Create(AOwner: TComponent);override;
end;
var
Win10FreezingForm: TWin10FreezingForm;
implementation
{$R *.dfm}
constructor TWin10FreezingForm.Create(AOwner: TComponent);
begin
inherited;
Width := 355;
Height := 355;
FTreeView := TTreeView.Create(self);
with FTreeView do begin
Parent := self;
Align := alClient
end;
with TTimer.Create(self) do begin
OnTimer := TimerTimer;
Interval := 2000;
Enabled := TRUE
end;
TimerTimer(self)
end;
procedure TWin10FreezingForm.TimerTimer(Sender: TObject);
var
i: Integer;
begin
//once the TreeView has been populated
//continuously scroling the listView up and down will freeze the form
//uncomment the following line as a not very good workaround
//if HasASecondPassedSinceLastInput then
with FTreeView.Items do begin
BeginUpdate;
try
Clear;
for i := 0 to 30 + Random(10) do
AddChild(nil, IntToStr(Random(100)))
finally
EndUpdate
end
end
end;
function TWin10FreezingForm.HasASecondPassedSinceLastInput: boolean;
var lii: TLastInputInfo;
begin
lii.cbSize := SizeOf(TLastInputInfo);
result := GetLastInputInfo(lii) and (GetTickCount - lii.dwTime > 1000)
end;
end.
program Win10Freeze;
uses
Vcl.Forms,
Win10Freezing in 'Win10Freezing.pas' {Win10FreezingForm};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TWin10FreezingForm, Win10FreezingForm);
Application.Run;
end.

Procedures on Free Pascal

Im relatively new to Pascal and, though i have a fair understanding of the language, there's still some stuff i cant figure out how to implement. I've ran into this problem and, after trying for like hours on my own and looking for similar cases on the internet, i have not found anything. I hope this question is a fair one because, honestly, i dont know how to figure this out.
Here's the thing.
I have an application which dynamically creates TextBoxes (TextEdits in this case) and adds them to a panel for displaying. Thing is, i need to execute some procedures on the newly created elements. I added a new procedure in my app (this is for explaining purposes only):
procedure Demo_Procedure(i: integer, a: String);
Then i proceeded to "develop" my procedure underneath the "implementation" part of the Form.
procedure Demo_Procedure(i: integer, a: String);
begin
ShowMessage(a, ' ' ,i);
end;
Now, for my dynamically created elements im trying to set the "OnKeyDow" event to run my new procedure (this is what i dont A- know if its possible to do or B- how to do it)
NewlyButton.OnClick:= Demo_Procedure(5, 'Hi');
Im getting different errors depending on how i call up my procedure. For example:
If i do it like this: Demo_Procedures(5, 'Hi'), it says:
Error: Incompatible types: got "untyped" expected "procedure variable type of procedure(TObject,var Word,TShiftState) of object;Register>"
Now, researching around i found out that some people that put an '#' before calling the method, the only difference is that this time instead of saying "untyped" it says that it got "procedure variable type of procedure(AnsiString,LongInt) of object" and that it was expecting the same as before (procedure(TObject,var> Word,Tshift...etc)
Can anyone help me out here? I really am lost so any help would be greatly appreciated. Thanks in advance :)
There are errors in your code:
procedure Demo_Procedure(i: integer, a: String); // Wrong
procedure Demo_Procedure(i: integer; a: String); // Right, use semicolon as parameters delimiter
ShowMessage(a, ' ' ,i); // Wrong, ShowMessage takes only one string parameter
ShowMessage(Format('%s %d', [a, i])); // Right, %s means string value and %d means decimal value, see help about Format function
Events is a procedural variables so they have its own types. For example, OnKeyDown event have a type
TKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of Object;
where of Object means that your event handler must be a class method.
So, you can not assign to the event any procedure but only class method with parameters provided in the type declaration.
Here is the simple code:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
e: TEdit;
begin
e := TEdit.Create(Self); // Create new TEdit control
e.Parent := Self; // Place control onto the form
e.Left := 10; // Set control coordinates
e.Top := 10;
e.OnKeyDown := #EditKeyDown; // Assign event handler
end;
procedure TForm1.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
ShowMessage(Format('Key code is %d', [Key]));
end;
end.

Pasting multiple lines into a TEdit

With respect to a TEdit component, would it be possible for the component to handle a multi-line paste from the Windows Clipboard by converting line breaks to spaces?
In other words, if the following data was on the Windows Clipboard:
Hello
world
!
...and the user placed their cursor in a TEdit then pressed CTRL+V, would it be possible to have the TEdit display the input as:
Hello world !
You'd need to subclass the TEdit using an interposer class, and add a handler for the WM_PASTE message:
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, adsdata, adsfunc, adstable;
type
TEdit= class(StdCtrls.TEdit)
procedure WMPaste(var Msg: TWMPaste); message WM_PASTE;
end;
type
TForm3 = class(TForm)
AdsTable1: TAdsTable;
Edit1: TEdit;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
uses
Clipbrd;
{ TEdit }
procedure TEdit.WMPaste(var Msg: TWMPaste);
var
TempTxt: string;
begin
TempTxt := Clipboard.AsText;
TempTxt := StringReplace(TempTxt, #13#10, #32, [rfReplaceAll]);
Text := TempTxt;
end;
end.

How to disable copy/paste in TEdit

I would like to prevent copy, cut and paste in my TEdit. How can I do this?
I tried setting the Key=NULL on KeyDown event when CTRL+V was pressed on the control, but it didn't work.
You'll need to prevent the WM_CUT, WM_COPY, and WM_PASTE messages from being sent to your TEdit. This answer describes how do to this using just the Windows API. For the VCL, it may be sufficient to subclass TEdit and change its DefWndProc property or override its WndProc method.
Assign this to TEdit.OnKeyPress :
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if (Key=#22) or (Key=#3) then Key:=#0; // 22 = [Ctrl+V] / 3 = [Ctrl+C]
end;
I know this is an old question but I'll add what I have found. The original poster almost had the solution. It works fine if you ignore cut/copy/paste in the key press event instead of the key down event. ie (c++ builder)
void __fastcall Form::OnKeyPress(TObject *Sender, System::WideChar &Key)
{
if( Key==0x03/*ctrl-c*/ || Key==0x16/*ctrl-v*/ || Key==0x018/*ctrl-x*/ )
Key = 0; //ignore key press
}
You can use some global programs that grab shortcuts and block C-V C-C C-X when TEdit window is active
Uses Clipbrd;
procedure TForm1.Edit1Enter(Sender: TObject);
begin
Clipboard.AsText := '';
end;
An old question, but the same bad answers are still floating around.
unit LockEdit;
// Version of TEdit with a property CBLocked that prevents copying, pasting,
// and cutting when the property is set.
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, StdCtrls, Windows;
type
TLockEdit = class(TEdit)
protected
procedure WndProc(var msg: TMessage); override;
private
FLocked: boolean;
public
property CBLocked: boolean read FLocked write FLocked default false;
end;
implementation
procedure TLockEdit.WndProc(Var msg: TMessage);
begin
if ((msg.msg = WM_PASTE) or (msg.msg = WM_COPY) or (msg.msg = WM_CUT))
and CBLocked
then msg.msg:=WM_NULL;
inherited;
end;
end.

Resources