Link to home
Start Free TrialLog in
Avatar of ChLa
ChLaFlag for United States of America

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.

Avatar of ChLa
ChLa
Flag of United States of America image

ASKER

Oops, I forgot to add the picture for the question above. Here it is.
User generated image
Avatar of ChLa

ASKER

The other thing that could be happening is, when i use a TFont variable to set the font of something like a panel caption, the variable is reset to the last font set with a font dialog. That a TFont variable can only be used once, and is then reset.
Avatar of ste5an
To test and understand what happens:

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;

Open in new window

Avatar of ChLa

ASKER

This last comment box is always pre-filled with the last message i sent !

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.
Hi!
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);

Open in new window


TFont is as class pointer and "strange" things can happen if you set Font like you do.
Yeah, that's why he should play with the advised modifications. So that he can learn the difference between value and reference types.
Avatar of ChLa

ASKER

I must not be understanding how to declare the TFont variables under forms class. Here is what I have now:
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 CERTIFIED SOLUTION
Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ChLa

ASKER

That was it. It is all working perfectly now. Thank you very much. If you had said to put the TFont variables in the private declarations, I would have understood. I added a couple of lines to the program. I first discovered the problem because I was trying to set the font shown in the dialog before it opened. I added lines to set the dialog font and that is working perfectly too. Now if you reset one of the three fonts, the dialog opens with the current font showing. First I tried it this way "fontDialog.Font := FontOne;" That worked perfectly, but I changed it to "FontDialog.Font.Assign(FontOne);", and this works perfectly too. Part of what I added assures that the FontDialog.Font is not assigned a TFont, until a font has been assigned to the TFont variable, because this would lead to an error because the values in the font before setting it are nil.
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.