asked on
Does using a font dialog to set the font of a TFont variable, also reset all other TFont variables ?
Hi, I have been having troubles with Fonts. At first it seemed it was not possible to programmatically set the font of a Font Dialog. This may be true, but, when I wrote a simple program to duplicate the problem and look for solutions, I found a more fundamental problem with Fonts that could cause the first problem. Basically what I have found is that apparently, if you have three Tfont variables, and use a Font Dialog to set the values of one of these, it seems to set the value of all existing Tfont variables to the same font. Can this be true ? More likely I am doing something wrong. So I wrote another even more simple program to demonstrate the problem.
First I declare three Tfont variables, FontOne, FontTwo and FontThree. I add a Font Dialog to the form. Now I add six panels, in three rows of two, that I will use as buttons. I prefer panels for buttons because I can control the color of the button and text, as well as if it appears raised or lowered. The three rows represent the three fonts. I add three labels to show the font that row affects. Each row has two buttons. The first button in each row opens the Font Dialog and sets font for the appropriate TFont Variable. I caption this button “Set Font” Now I add one more panel, at the bottom of the form. I caption this “Font Display” The second button in each row displays the font of that row’s TFont in the panel we added at the bottom of the form. This second button is labeled “Display Font”. Note that this program has only six procedures, one for each button.
Now we run the program. First we set the font of each TFont variable by pressing the first button of each row. You can see the font is now displayed in that button. Now we check the new fonts we have put into the TFont variables by pressing one of the “Display Font” buttons. The font display should now display the name of the font, in the same font as that you see in the “Set Font” button for that row. The problem is that although the text is correct, the font is always the one last selected by the Font Dialog.
I will attach a picture of the program running. In this picture I have selected a font for FontOne, then FontTwo, then Font Three. I then pressed the Font 2 “Display Font” Button. You see in the picture that the window at the bottom is showing the correct text, Font Two, but is using FontThree. If we press any of the “Display Font” buttons the text is correct, but the font is the last one chosen with the Font Dialog, FontThree in this case.
What am I doing wrong ? Here is my code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Panel4: TPanel;
Panel5: TPanel;
Panel6: TPanel;
Panel7: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
FontDialog1: TFontDialog;
procedure Panel1Click(Sender: TObject);
procedure Panel2Click(Sender: TObject);
procedure Panel3Click(Sender: TObject);
procedure Panel4Click(Sender: TObject);
procedure Panel5Click(Sender: TObject);
procedure Panel6Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FontOne: TFont;
FontTwo: TFont;
FontThree: TFont;
implementation
{$R *.dfm}
procedure TForm1.Panel1Click(Sender: TObject); //Set Font One Button
begin
if FontDialog1.Execute then
begin
FontOne := FontDialog1.Font;
Panel1.Font := FontOne;
end;
end;
procedure TForm1.Panel2Click(Sender: TObject); //Set Font Two Button
begin
if FontDialog1.Execute then
begin
FontTwo := FontDialog1.Font;
Panel2.Font := FontTwo;
end;
end;
procedure TForm1.Panel3Click(Sender: TObject); //Set Font Three Button
begin
if FontDialog1.Execute then
begin
FontThree := FontDialog1.Font;
Panel3.Font := FontThree;
end;
end;
procedure TForm1.Panel4Click(Sender: TObject); //Display Font One Button
begin
Panel7.Font := FontOne;
Panel7.Caption := 'Font One';
end;
procedure TForm1.Panel5Click(Sender: TObject); //Display Font Two Button
begin
Panel7.Font := FontTwo;
Panel7.Caption := 'Font Two';
end;
procedure TForm1.Panel6Click(Sender: TObject); //Display Font Three Button
begin
Panel7.Font := FontThree;
Panel7.Caption := 'Font Three';
end;
end.
Note that I first discovered this problem working with Delphi 10.2 on a Windows 7 computer. I wrote this small program using Delphi 11 on a Windows 10 computer.
ASKER
Don't use the TFontDialog as component on the form, but create and destroy it during runtime.
E.g. your font select methods should look like
procedure TForm1.Panel1Click(Sender: TObject); //Set Font One Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create;
if FontDialog.Execute then
begin
FontOne := FontDialog.Font;
Panel1.Font := FontOne;
end;
FontDialog.Destroy;
end;
ASKER
Thank you ste5an for your quick reply. Unfortunately it does not seem to work. Here is my new code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Panel4: TPanel;
Panel5: TPanel;
Panel6: TPanel;
Panel7: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure Panel1Click(Sender: TObject);
procedure Panel2Click(Sender: TObject);
procedure Panel3Click(Sender: TObject);
procedure Panel4Click(Sender: TObject);
procedure Panel5Click(Sender: TObject);
procedure Panel6Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FontOne: TFont;
FontTwo: TFont;
FontThree: TFont;
implementation
{$R *.dfm}
procedure TForm1.Panel1Click(Sender: TObject); //Set Font One Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel1);
if FontDialog.Execute then
begin
FontOne := FontDialog.Font;
Panel1.Font := FontOne;
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel2Click(Sender: TObject); //Set Font Two Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel2);
if FontDialog.Execute then
begin
FontTwo := FontDialog.Font;
Panel2.Font := FontTwo;
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel3Click(Sender: TObject); //Set Font Three Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel3);
if FontDialog.Execute then
begin
FontThree := FontDialog.Font;
Panel3.Font := FontThree;
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel4Click(Sender: TObject); //Display Font One Button
begin
Panel7.Font := FontOne;
Panel7.Caption := 'Font One';
end;
procedure TForm1.Panel5Click(Sender: TObject); //Display Font Two Button
begin
Panel7.Font := FontTwo;
Panel7.Caption := 'Font Two';
end;
procedure TForm1.Panel6Click(Sender: TObject); //Display Font Three Button
begin
Panel7.Font := FontThree;
Panel7.Caption := 'Font Three';
end;
end.
I found I had to add a parameter to Create. First I tried "Form1" for all three. When I press a second display font button, I get an access violation error.
Then I tried "Panel1" for all three, and also Panel1, Panel2, and Panel3. i didn't get the error now, but still, the result is the same. The message in the "display font" window changes for each "Display Font" Button, but the Font remains the same, always the last one selected.
You are doing wrong. Each TFont variable should be created on forms Create event and release on Destroy.
Put those variables under Forms class. And after all you'll need to assign Font like this:
if FontDialog.Execute then
begin
FontThree.Assign(FontDialog.Font);
...
Panel1.Font.Assign(FontThree);
TFont is as class pointer and "strange" things can happen if you set Font like you do.
ASKER
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Panel4: TPanel;
Panel5: TPanel;
Panel6: TPanel;
Panel7: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure Panel1Click(Sender: TObject);
procedure Panel2Click(Sender: TObject);
procedure Panel3Click(Sender: TObject);
procedure Panel4Click(Sender: TObject);
procedure Panel5Click(Sender: TObject);
procedure Panel6Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
// FontOne: TFont;
// FontTwo: TFont;
// FontThree: TFont;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
FontOne: TFont;
FontTwo: TFont;
FontThree: TFont;
begin
end;
procedure TForm1.Panel1Click(Sender: TObject); //Set Font One Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel1);
if FontDialog.Execute then
begin
FontOne.Assign(FontDialog.Font);
Panel1.Font.Assign(FontOne);
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel2Click(Sender: TObject); //Set Font Two Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel1);
if FontDialog.Execute then
begin
FontTwo.Assign(FontDialog.Font);
Panel2.Font.Assign(FontTwo);
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel3Click(Sender: TObject); //Set Font Three Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel1);
if FontDialog.Execute then
begin
FontThree.Assign(FontDialog.Font);
Panel3.Font.Assign(FontThree);
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel4Click(Sender: TObject); //Display Font One Button
begin
Panel7.Font := FontOne;
Panel7.Caption := 'Font One';
end;
procedure TForm1.Panel5Click(Sender: TObject); //Display Font Two Button
begin
Panel7.Font := FontTwo;
Panel7.Caption := 'Font Two';
end;
procedure TForm1.Panel6Click(Sender: TObject); //Display Font Three Button
begin
Panel7.Font := FontThree;
Panel7.Caption := 'Font Three';
end;
end.
If I declare the variables as I did here, in the form1.create event, Delphi sees the variables as undeclared and I can't compile.
I tried to put them at the top under type after the declaraton TForm1 = class(TForm). But then it says there is no corresponding component. Before i tried to move the TFont declarations, I got an access violation error whenever a font assignment line, like: "FontOne.Assign(FontDialog.Font);" is executed.
ASKER
Because this is all working perfectly now I will add the latest .pas text for the use of anyone who is looking for an answer to the same problem.
unit Unit1; //a simple program to demonstrate how to set and use TFont variables and a FontDialog.
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
Panel4: TPanel;
Panel5: TPanel;
Panel6: TPanel;
Panel7: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure Panel1Click(Sender: TObject);
procedure Panel2Click(Sender: TObject);
procedure Panel3Click(Sender: TObject);
procedure Panel4Click(Sender: TObject);
procedure Panel5Click(Sender: TObject);
procedure Panel6Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FontOne: TFont;
FontTwo: TFont;
FontThree: TFont;
public
{ Public declarations }
end;
var
Form1: TForm1;
Font1Visits: Integer = 0; //three variables to indicate when a font has been assigned to the variable
Font2Visits: Integer = 0; //should probably be boolean
Font3Visits: Integer = 0;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FontOne := TFont.Create;
FontTwo := TFont.Create;
FontThree := TFont.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FontOne.Free;
FontTwo.Free;
FontThree.Free;
end;
procedure TForm1.Panel1Click(Sender: TObject); //Set Font One Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel1);
If Font1Visits > 0 then FontDialog.Font.Assign(FontOne); //sets dialog to existing font variable if one has been applied
if FontDialog.Execute then
begin
FontOne.Assign(FontDialog.Font);
Panel1.Font.Assign(FontOne);
Inc(Font1Visits); //indicate when font has been assigned to variable
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel2Click(Sender: TObject); //Set Font Two Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel2);
If Font2Visits > 0 then FontDialog.Font.Assign(FontTwo);
if FontDialog.Execute then
begin
FontTwo.Assign(FontDialog.Font);
Panel2.Font.Assign(FontTwo);
Inc(Font2Visits);
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel3Click(Sender: TObject); //Set Font Three Button
var
FontDialog: TFontDialog;
begin
FontDialog := TFontDialog.Create(Panel3);
If Font3Visits > 0 then FontDialog.Font.Assign(FontThree);
if FontDialog.Execute then
begin
FontThree.Assign(FontDialog.Font);
Panel3.Font.Assign(FontThree);
Inc(Font3Visits);
end;
FontDialog.Destroy;
end;
procedure TForm1.Panel4Click(Sender: TObject); //Display Font One Button
begin
Panel7.Font := FontOne;
Panel7.Caption := 'Font One';
end;
procedure TForm1.Panel5Click(Sender: TObject); //Display Font Two Button
begin
Panel7.Font := FontTwo;
Panel7.Caption := 'Font Two';
end;
procedure TForm1.Panel6Click(Sender: TObject); //Display Font Three Button
begin
Panel7.Font := FontThree;
Panel7.Caption := 'Font Three';
end;
end.
ASKER