Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Delphi] Caption Form elementen aanpassen in andere Unit

Pagina: 1
Acties:
  • 125 views sinds 30-01-2008
  • Reageer

  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Voor een Delphi projectje maak ik gebruik van een .ini bestand waar wat language instellingen in staan. Door het selecteren van een taal in het menu worden de Captions van verschillende Form elementen (Labels, Groupboxes, etc) aangepast. Zolang ik de code hiervoor gewoon in mijn mainunit hou is er niks aan de hand en werkt het perfect.

Echter, ik wil een aparte Unit voor alle I/O code dus ook om de taal instellingen te wijzigen.
Ik dacht, gewoon een procedure in de nieuwe Unit aanmaken en deze procedure in mijn mainunit aanroepen met een pointer naar mijn Form. Maar helaas dat werkt dus niet.

Even wat code ter verduidelijking:

Uit mainunit:

code:
1
LoadLanguageFile(Form1: TForm);


Uit IO.pas:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unit IO;

interface

uses  inifiles, Forms, Classes, SysUtils, StdCtrls;

implementation

procedure LoadLanguageFile(Form1: TForm);
var
  IniFile : TIniFile;
  LastLang : String;
begin
  IniFile := TIniFile.Create(extractfilepath (application.ExeName) + 'language.ini');
  
  // Get the language
  LastLang := IniFile.ReadString('Language','Last','NL');
  
  Form1.GroupBox1.Caption := IniFile.ReadString(LastLang,'Box1','Oplosser');

  // Set Labels, Radiobuttons, Checkboxes and Buttons to correct language
  Form1.Label1.Caption := IniFile.ReadString(LastLang,'Label1','Taal bestand niet gevonden.');

  IniFile.Free;
end;

end.


Errors:
[Error] IO.pas(19): Undeclared identifier: 'GroupBox1'
[Error] IO.pas(22): Undeclared identifier: 'Label1'

Maak vast en zeker ergens een achterlijke fout, maar zie het zelf niet.

[ Voor 0% gewijzigd door een moderator op 30-10-2007 22:19 . Reden: code tags aangepast. sytax highlighting ftw \0/ ]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
edit:
anders ben ik even te lang uit Delphi :X

[ Voor 110% gewijzigd door RobIII op 30-10-2007 22:20 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 15:10

Creepy

Tactical Espionage Splatterer

Een TForm is de superclass van je daadwerkelijke form, die bevat geen andere elementen. Je echte Form1 is zeer waarschijnlijk van het type TForm1, en dat type moet je dan ook meegeven als je direct items op het meegegeven form wilt kunnen benaderen.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Je hebt gelijk:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
end;

var
  Form1: TForm1;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadLanguageFile(Form1: TForm1 );
end;


Maar als ik hem zoals hierboven meegeef, dan geeft hij aan dat hij TForm1 niet kent (wat vrij logisch is).

[ Voor 0% gewijzigd door een moderator op 30-10-2007 22:19 . Reden: code tags aangepast. sytax highlighting ftw \0/ ]


  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 17:21

Reptile209

- gers -

Pieth schreef op dinsdag 30 oktober 2007 @ 22:09:
Je hebt gelijk:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
end;

var
  Form1: TForm1;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadLanguageFile(Form1: TForm1 );
end;


Maar als ik hem zoals hierboven meegeef, dan geeft hij aan dat hij TForm1 niet kent (wat vrij logisch is).
Delphi:
1
2
3
begin
  LoadLanguageFile(Form1);  // Zonder ": TForm1" dus
end;

Of geeft hij de fout in een andere regel :?

Zo scherp als een voetbal!


  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Mja, was misschien wel handig geweest om te zeggen waar:

[Error] IO.pas(7): Undeclared identifier: 'TForm1'

Dat is dus in mijn tweede unit, bij de declaratie van de procedure:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
unit IO;

interface

uses  inifiles, Forms, Classes, SysUtils, StdCtrls;

procedure LoadLanguageFile(Form2: TForm1);

implementation

procedure LoadLanguageFile(Form2: TForm1);
var
  IniFile : TIniFile;
  LastLang : String;
begin
  IniFile := TIniFile.Create(extractfilepath (application.ExeName) + 'language.ini');

  // Get the language
  LastLang := IniFile.ReadString('Language','Last','NL');

  Form2.GroupBox1.Caption := IniFile.ReadString(LastLang,'Box1','Oplosser');

  // Set Labels, Radiobuttons, Checkboxes and Buttons to correct language
  Form2.Label1.Caption := IniFile.ReadString(LastLang,'Label1','Taal bestand niet gevonden.');

  IniFile.Free;
end;

end.

  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 28-11 12:23

Bergen

Spellingscontroleur

Delphi:
1
procedure LoadLanguageFile(Form1: TForm);

Hmm, geef je op die manier wel een echte pointer mee? Dit zou wel moeten werken denk ik:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
unit IO;

interface

uses  inifiles, Forms, Classes, SysUtils, StdCtrls;

type
  PForm = ^TForm; // Pointer naar een TForm

implementation

procedure LoadLanguageFile(Form1: PForm); // PForm ipv een TForm
(etcetera)

En dan bij de aanroep een pointer meegeven:
Delphi:
1
2
3
4
procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadLanguageFile(@Form1); // Let op de @
end;


[edit]
Hmm even getest maar dat werkt natuurlijk niet. De reference wel maar het is een pointer naar het type TForm en daar zitten niet die objecten in die jij wilt gebruiken... Een oplossing zou iig zijn om een pointer naar die specifieke GroupBox en Label mee te geven en dan in IO.pas een PGroupBox en PLabel aan te maken. Dat werkt sowieso, maarja stel dat je 20 objecten zou willen aanspreken dan is 't niet echt handig.

In dat geval zou je de declaratie van TForm1 in een aparte unit moeten stoppen, inclusief een PForm1 = ^TForm1 en dan vanuit Unit1.pas en IO.pas die unit bij de uses moeten zetten. Dan kennen zowel Unit1.pas als IO.pas die TForm1 en kun je alles aanspreken. Zo zou ik het denk ik oplossen.

[edit2]
Ohja, Form1 is al een pointer natuurlijk. 8)7 Er hoeft helemaal geen PForm aangemaakt te worden, dat wordt een dubbele pointer. Never mind. Oh well.

[edit3]
Ook even getest: de hele TForm1 declaratie in een losse unit zetten en in Unit1.pas alleen uses Unit2; maar dat wil niet echt werken: "Error in module Unit1: Declaration of class TForm1 is missing or incorrect." Waarom moet de declaratie van een formulier in dezelfde unit staan? Als je de class in een andere unit definieert en in de uses-clause zet zou dat toch ook moeten werken? Wat een prutser voel ik me op deze woensdagochtend... :O

[ Voor 57% gewijzigd door Bergen op 31-10-2007 08:05 ]


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 15:10

Creepy

Tactical Espionage Splatterer

Een object variabele in Delphi is altijd een pointer. Dus een TForm1 als type voor je parameter kan prima, je geeft dan echt een pointer mee.

In de unit waar je de TForm1 wilt gebruiken moet je de unit waar je TForm1 staat gedeclareerd opnemen d.m.v. een uses form1.pas o.i.d. Lijkt me niet zo moeilijk en dat mist in elk geval in de laatste voorbeeld code.

Pieth: no offence maar kennnis over classes en het gebruik van uses is redelijke basis kennis van Delphi wat in een beetje Delphi boek wordt uitgelegd. Daarnaast is het hier egenlijk de bedoeling dat je meer doet dan alleen een foutmelding geven en je code te dumpen zoals je in je laatste post doet. Ook dan verwachten we dat je (net als in een topic start) minstens aangeeft wat je zelf hebt geprobeerd.

[ Voor 19% gewijzigd door Creepy op 31-10-2007 08:56 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 28-11 12:23

Bergen

Spellingscontroleur

Creepy schreef op woensdag 31 oktober 2007 @ 08:51:
In de unit waar je de TForm1 wilt gebruiken moet je de unit waar je TForm1 staat gedeclareerd opnemen d.m.v. een uses form1.pas o.i.d. Lijkt me niet zo moeilijk en dat mist in elk geval in de laatste voorbeeld code.
Maar dan heb je een circular reference toch?

  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Creepy schreef op woensdag 31 oktober 2007 @ 08:51:
Pieth: no offence maar kennnis over classes en het gebruik van uses is redelijke basis kennis van Delphi wat in een beetje Delphi boek wordt uitgelegd. Daarnaast is het hier egenlijk de bedoeling dat je meer doet dan alleen een foutmelding geven en je code te dumpen zoals je in je laatste post doet. Ook dan verwachten we dat je (net als in een topic start) minstens aangeeft wat je zelf hebt geprobeerd.
I know, maar was zo druk bezig met klooien dat ik nog geen tijd heb gehad om mijn probeersellen neer te planten. O-)

Anyways, TForm1 is gedeclareerd in de mainunit en in die mainunit wordt IO 'geused'. Op het moment dat ik mijn mainunit weer in de uses van IO zet krijg ik een error m.b.t. circular unit reference. Heb ook al gelezen over het usen in de implementation, maar daar heeft mijn declaratie van de procedure niks aan.

Normaal werk ik trouwens in C/ C++ waar je wel een hoop 'vieze' trucs uit mag halen m.b.t. pointers. Het is dat dit project in Delphi gemaakt moest worden, anders was ik er niet aan begonnen :X

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 15:10

Creepy

Tactical Espionage Splatterer

Bergen schreef op woensdag 31 oktober 2007 @ 09:10:
[...]
Maar dan heb je een circular reference toch?
Ik zat al op deze opmerking te wachten :P

Nee, want mocht je iets in form1.pas nodig hebben van die andere unit dan zet je de uses in je implementation en niet in je interface. Alles wat in je interface deel staat is publiekelijk beschikbaar en alles wat in je implementation staat is dat niet. Je kan dus prima in unitA unitB usen en tegelijk ook andersom zolang het in minstens 1 geval maar in de implementation gebeurd en niet in de interface.

Dit is helaas een veel gemaakte fout omdat de Delphi IDE zelf altijd alles in de interface deel zet.

Pieth: haal dan die IO uit de unit waar je de error krijgt omdat je die unit al meekrijgt via een andere unit. Overigens is dit soort kennis in C++ vergelijkbaar dus ik snap je opmerking over het gebruik van C++ niet helemaal. Simpel gezegd kan je het interface deel van je unit zien als een header file en het implementation deel als je .c of .cpp file. In C++ kan je ook rare zaken krijgen als je twee keer dezelfde header file include, tenzij je #IFDEF guards gebruikt.

[ Voor 37% gewijzigd door Creepy op 31-10-2007 09:18 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Creepy schreef op woensdag 31 oktober 2007 @ 09:14:
Ik zat al op deze opmerking te wachten :P

Nee, want mocht je iets in form1.pas nodig hebben van die andere unit dan zet je de uses in je implementation en niet in je interface. Alles wat in je interface deel staat is publiekelijk beschikbaar en alles wat in je implementation staat is dat niet. Je kan dus prima in unitA unitB uses en tegelijk ook andersom zolang het in minstens 1 geval maar in de implementation gebeurd en niet in de interface.

Dit is helaas een veel gemaakte fout omdat de Delphi IDE zelf altijd alles in de interface deel zet.
_/-\o_

Uiteraard... ik maar proberen om mainunit in de implementation van IO te usen. Waarom heb ik er niet aan gedacht om IO te usen in de implentation van de mainunit... 8)7

  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 28-11 12:23

Bergen

Spellingscontroleur

Aha, ik had me al eens afgevraagd wat het verschil was. Dank voor deze verduidelijking.

  • Pieth
  • Registratie: Oktober 2004
  • Laatst online: 19-08 11:30

Pieth

Beginnend prutser..

Topicstarter
Creepy schreef op woensdag 31 oktober 2007 @ 09:14:
[...]
Pieth: haal dan die IO uit de unit waar je de error krijgt omdat je die unit al meekrijgt via een andere unit. Overigens is dit soort kennis in C++ vergelijkbaar dus ik snap je opmerking over het gebruik van C++ niet helemaal. Simpel gezegd kan je het interface deel van je unit zien als een header file en het implementation deel als je .c of .cpp file. In C++ kan je ook rare zaken krijgen als je twee keer dezelfde header file include, tenzij je #IFDEF guards gebruikt.
Kwestie van door de bomen het bos niet meer zien. Te vaak de code bekeken om de simpele dingen niet meer te zien. Denk dat ik nu maar eens ga slapen... :O

/slotje en bedankt

  • SysRq
  • Registratie: December 2001
  • Laatst online: 29-11 20:35
Heb je verder wel in de gaten dat je op deze manier niet echt goed aan het programmeren bent? :P

Wat denk je dat er gebeurt als je het taal bestand nog in een ander form wilt gebruiken? Je bent dan direct verplicht om ook op dat form een groupbox1 en label1 te gebruiken. Probeer dat te voorkomen.

Ik zou in dit geval gebruik maken van de volgende code:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
  private
    procedure LoadLanguage();
end;

implementation;

uses io;

var
  Form1: TForm1;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadLanguage();
end;

procedure TForm1.LoadLanguage;
begin
  GroupBox1.Caption := io.GetTranslation('box1');
  Label1.caption := io.GetTranslation('label');  
end;


In de IO unit kun je dan bijvoorbeeld een singleton implementatie maken, zodat je maar één keer het ini bestand hoeft in te lezen. :)

-


  • Paul
  • Registratie: September 2000
  • Laatst online: 11:06
Kun je niet veel beter iets als het volgende doen:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
unit Language;

interface

type
  TInternationalization = class
  private
    data: TList; // Of net waar je het in opslaat en een beetje performt met zoeken
  public
    constructor Create;  // als ik het goed heb moeten hier nog wat
    destructor Destroy; // keywords achter om de compiler tevreden te houden
    function GetLanguageString(Resource: String): String;
  end;

var Internationalization: TInternationalization;

implementation

constructor TInternationalization.Create()
begin
  // instantieer je data-gedoe
  // Bepaal welke taal je weer wilt geven
  // vul je data-dinges
  inherited;
end;
  
destructor Destroy()
begin
  // Geef je data weer vrij
end;

function GetLanguageString(Resource: String): String;
begin
  Result := Data.Find(Resource);
end;

end.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
program Project1;

uses
  Language in "Language.pas";

begin
  Internationalization := TInternationalization.Create;
  try
    ...
    ...
  finally
    Internationalization.Free;
  end;
end;

end.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
unit Form1;

uses Language;

interface

implementation
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckBox1.Caption := Internationalization.GetLanguageString("Form1_CheckBox1");
  Label1.Caption := Internationalization.GetLanguageString("Form1_Label1");
end;


Op die manier hoeft je Internationalization-class (jouw IO) geen weet te hebben van de rest van het programma, en dus geen honderden forms in zijn uses te hebben etc.

Edit: Wat mijn bovenbuurman dus ook zegt :P

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


  • SysRq
  • Registratie: December 2001
  • Laatst online: 29-11 20:35
Paul Nieuwkamp \o/

En een singleton implementatie ziet er dan zo uit:

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
unit Language;

interface

type
  TInternationalization = class
  private
    data: TList; // Of net waar je het in opslaat en een beetje performt met zoeken
  public
    constructor Create;  // als ik het goed heb moeten hier nog wat
    destructor Destroy; // keywords achter om de compiler tevreden te houden
    function GetLanguageString(Resource: String): String;
  end;

implementation

var
  internalInternationalization: TInternationalization

constructor TInternationalization.Create()
begin
  // instantieer je data-gedoe
  // Bepaal welke taal je weer wilt geven
  // vul je data-dinges
  inherited;
end;
  
destructor Destroy()
begin
  // Geef je data weer vrij
end;

function GetLanguageString(Resource: String): String;
begin
  if not (assigned(internalInternationalization)) then
  begin
    internalInternationalization := TInternationalization.create();
    // zet eventuele properties
  end;
  Result := internalInternationalization.Data.Find(Resource);
end;

finalization

if assigned(internalInternationalization) then
  internalInternationalization.free

end.


En nog een

-


  • Paul
  • Registratie: September 2000
  • Laatst online: 11:06
Mis je daar niet nog een stuk?

Volgens mij compiled dit ook niet clean: data is private en TInternationalization.GetLanguageString wordt nergens ingevuld ;)

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock


  • SysRq
  • Registratie: December 2001
  • Laatst online: 29-11 20:35
Hmm, daar had ik inderdaad wel wat tekst neergezet. :)

Er moeten natuurlijk nog een aantal aanpassingen gemaakt worden. Je kunt inderdaad niet bij data (volgens de codingguidelines moet dat eigenlijk fData heten ;) ) maar daarvoor zou je een getData functie kunnen hebben. GetLanguageString moet dan een losse functie worden, niet ingebed in de TInternationalization classe.

-


  • GabberKooij
  • Registratie: April 2002
  • Laatst online: 22-11 20:09
Ik gebruik in mijn prive projecten vaak gnugettext voor Delphi. Dat werkt zo ongeveer op de hier eerder beschreven manier.

GNU gettext for Delphi

is wel wat ingewikkelder om op te zetten, dus voor een klein projectje kan ik me voorstellen dat je het liever zelf implementeerd.

Maak dagritmekaarten met mijn Picto-Selector op www.pictoselector.eu


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Delphi zelf heeft ook ingebouwde ondersteuning voor i18n. Multilizer is ook een goed product die hetzelfde effect bereikt.

We adore chaos because we like to restore order - M.C. Escher


Verwijderd

Wij doen het met 2 componenten.
(we hebben eigen tool gemaakt voor vertalingen).

Een hoofd component (1x per applicatie).
Een form component (1x per form).

Hoofdcomponent gooi je op bv een datamodule of je mainform. Werkt idd via singleton.
Form component gooi je op iedere form, en registereert zich via de aanwezige singleton, bij het hoofdcomponent.

Zodra je hje taal veranderd instelling in hoofdcomponent, wordt de interne messagelist vertaald en alle gekoppelde forms krijgen een signaal van hey, je moet gaan vertalen.

Met messages bedoel ik zaken die niet als standaard component property beschikbaar zin,dus losse strings etc.
Pagina: 1