Nå jeg gætter på at det du vil er at kunne oprette en variable som du kan gemme teksten fra x antal TMemo´er. Det lyder som noget en TStringList kan bruges til så det kan bla. laves sådan:
// Dette kan du putte ind i en unit for sig selv:
interface
uses Classes, StdCtrls;
type TMemoGem = class(TStringList) private { Private declarations } public { Public declarations } constructor create(Memos : array of TMemo); end;
implementation
constructor TMemoGem.create(Memos : array of TMemo); var i : integer; begin inherited create; for i:=0 to Length(Memos)-1 do Self.Add(Memos[i].Lines.CommaText); end;
--------------------------------
Jeg har her smidt 3 TMemo´er og en TButton på en form og putter følgende kode ind i Button´ens OnClik event:
procedure TForm1.Button1Click(Sender: TObject); var MemoGem : TMemoGem; i : integer; begin MemoGem:=TMemoGem.create([Memo1,Memo2,Memo3]);
for i:=0 to MemoGem.Count-1 do ShowMessage(MemoGem[i]); end;
I første linie oprettes der en TMemoGem med de 3 Memoer som parametre. I for-next lykken vises så indholdet af de enkelte memos, taget fra en nye MemoGem klasse, teksten fra de enkelte memos vises som kommasepereret tekst.
Hvis du skal have en tekst ud fra MemoGem klassen og ind i en anden TMemo bruger du blot TMemoens CommaText property.
Hvis man tager mit eks. og du f.eks. gerne vil have teksten fra Memo2 ud af MemoGem og ind i en helt anden memo på din form som f.eks. hedder MemoNy så kan du gøre det således:
MemoNy.Lines.CommaText:=MemoGem[1];
Teksten fra de enkelte memoer (Memo1, Memo2 og Memo3) er jo blevet gemt i MemoGem klassen som CommaText.
MemoGem er baseret på en TStringList, TStringList har selv en Create metode som derfor kaldes med inherited create for at sikre at klassen bliver initialiseret korrekt. Lige i dette tilfælde hvor der bruges TStringList er det dog ikke nødvendig da TStringLists create stammer helt oppe fra TObject klassen og derfor ikke indeholder nogen kode. Men jeg kalder nu altid inherited create alligevel.
Man kan også bruge de to funktioner Low og High i Kronings eksempel
constructor TMemoGem.create(Memos : array of TMemo); var i : integer; begin inherited create; for i:=low(Memos) to high(Memos) do Self.Add(Memos[i].Lines.CommaText); end;
I stedet for CommaText kan man vel bare bruge Memos[i].Text, kan man ikke?
constructor TMemoGem.create(Memos : array of TMemo); var i : integer; begin inherited create; for i:=low(Memos) to high(Memos) do Self.Add(Memos[i].Text); end;
I øvrigt bør du komme igang med klasser og objekter. Det er ikke så svært som man gør det til og tingene er meget lettere bagefter ... syns' jeg.
Ja til det første og jow til det næste, jeg plejer at bruge commatext når jeg laver noget i denne stil da man så har mulighed for let at gemme det til en fil med SaveToFile proceduren.
hrc du nævner at ikke er så svært som man gør det til. hmm... kan du (eller en anden) svare mig på, hvordan jeg laver en klasse der indeholder en funktion, som kan skal havde en memos tekst som input og returnere en memos tekst. Således at jeg bagefter kan lægge funktionens returnerede memos tekst over i en anden memo tekst?
Håber du kan forstå hvad jeg skriver jeg finder det lidt inviklet:-)
Hvis du gemmer en Memos text i en klasse hvordan vil du så senere hente denne text tilbage, du skal jo på en eller anden måde kunne fortælle klassen hvilken text du vil have. Så skal det være ved at angive et tal eller skal det være ved at angive memoens navn?
Du vil gerne have flere memo'er gemt i klassen, ikke? Medmindre du vil over i TRichMemo (eller hvad den lige hedder) kan du gemme data fra en TMemo som en simpel tekststreng.
var i : integer; StringList : TStringList; Index1, Index2 : integer; begin StringList := TStringList.Create; // Opret objekt af typen TStringList; try // Tilføj Memo'ernes tekster. Husk Index Index1 := StringList.Add(Memo1.Text); Index2 := StringList.Add(Memo2.Text);
// Byt rundt på de to tekster Memo1.Text := StringList[Index2]; Memo2.Text := StringList[Index1];
// Læg Memo1 og Memo2 over i Memo3 Memo3.Text := StringList.Text;
// Gennemløb StringList'en (Index starter altid i 0, derfor "Count - 1") for i := 0 to StringList.Count - 1 do StringList[i] := StringList[i] + 'Hej'; finally StringList.Free; // Husk at frigive hukommelsen for StringList'en igen end;
Du kan prøve det af på en form der indeholder 3 TMemoer. Lav en knap og sæt ovenstående (utestede) kode ind.
Jeg ved ikke om det er hvad du søger. Eksemplet er en fortsættelse af Kronings løsning og hvis det ikke er hvad du leder efter ... så skriv og vi skal prøve at hitte på noget andet
Jeg syntes selv det vil være smartest at benytte navne så her komme en sådan løsning, den bygger på min tidligere klasse men med 2 extra procedurer og en lidt ændret create.
type TMemoGem = class(TStringList) private { Private declarations } public { Public declarations } constructor create(Memos : array of TMemo); procedure AddMemoText(Memo : TMemo); function GetMemotext(Memo : TMemo; MemoNavn : string) : boolean; end; . . var MemoGem : TMemoGem; // skal nu være global
. . implementation . . function TMemoGem.GetMemotext(Memo : TMemo; MemoNavn : string) : boolean; begin if Self.IndexOfName(MemoNavn)=-1 then begin Result:=false; exit; end; Memo.Lines.CommaText:=Self.Values[MemoNavn]; Result:=true; end;
procedure TMemoGem.AddMemoText(Memo: TMemo); begin Self.Add(Memo.Name+'='+Memo.Lines.CommaText); end;
constructor TMemoGem.create(Memos : array of TMemo); var i : integer; begin inherited create; for i:=0 to Length(Memos)-1 do Self.Add(Memos[i].Name+'='+Memos[i].Lines.CommaText); end;
//-----------------------
Husk at create MemoGem som det første, f.eks. i din forms OnCreate:
procedure TForm1.FormCreate(Sender: TObject); begin MemoGem:=TMemoGem.create([Memo1,Memo2,Memo3]); end;
//-------------------------------
Hvis du har en memo på din form der f.eks. hedder Memo4 kan du gemme den således: MemoGem.AddMemoText(Memo4);
Hvis du har en memo på din form der f.eks. hedder Memo5 og vil hente teksten fra Memo4 ind i din Memo5 gøres det således: MemoGem.GetMemotext(Memo5,'Memo4');
1. parameter er den memo som teksten skal ind i, og 2. parameter er navnet på den Memos tekst som du tidligere gemte med AddMemoText Bemærk GemMemoText vil returnere false hvis det angivet Memo navn ikke findes.
Her er lige en ny GetMemotext function, den gør det samme men jeg er bange for at jeg ikke kan få mit golf sving til at fungere ordentlig hvis jeg ikke lige kommer med den :-)
function TMemoGem.GetMemotext(Memo : TMemo; MemoNavn : string) : boolean; begin Result:=Self.IndexOfName(MemoNavn)>-1; if Result then Memo.Lines.CommaText:=Self.Values[MemoNavn]; end;
Det er altså ikke smart at have en "array of TMemo" på en liste. Der findes fine metoder til at tilføje bagefter: Add og Insert. Du lægger op til at man ikke skal kunne tilføje flere memo'er når du angiver dem construktoren - og det er vildledende.
Synes generelt dine klasser er lidt mudrede og ikke særligt hensigtsmæssige, undskyld, men det synes jeg. Hvorfor lave en AddMemo, hvis der findes en Add som du kan override? Hvorfor ikke placere selve Memo-komponenten på Items.Object? Brug TItemList eller evt. TObjectList i stedet (har nok tabt kinginkvong her :-))
Hvis du vil have en liste af TMemo'er (der ikke er manuelt oprettet, men sidder på en form), så bør du bruge TItemList (det er dog smart at man kan gemme memo'ens navn i stringlisten, her må man søge gennem listen):
En simpel implementering af en liste af TMemo'er. Modsat TObjectList vil TItemList ikke frigive TMemo-objekterne i listen, da de "ejes" og frigives af formen hvorpå de er placeret.
TMemoList = class(TItemList) private function GetMemo(const aIndex : integer) : TMemo; public property Items[const aIndex : integer] : TMemo read GetMemo; default; function IndexOfName(const aName : string): integer; // indeks på søgte memo end;
function TMemoList.GetMemo(const aIndex : integer) : TMemo; begin result := TMemo(inherited Items[aIndex]); end;
function TMemoList.IndexOfName(const aName : string): integer; var i : integer; Found : boolean; begin result := -1; i := -1; Found := false; while (i < Count - 1) and not Found do begin inc(i); Found := SameStr(Items[i].Name, aName); end; if Found then result := i; end;
// ---------
var ml : TMemoList; i, Index : integer; begin ml := TMemoList.Create; try ml.Add(Memo1); ml.Add(Memo2); ml.Add(Memo3); for i := 0 to ml.Count - 1 do MessageBox(ml[i].Name,mtInformation,[mbOK],0); // Vil skrive Memo1, Memo2...
// Søg memo vha. navn: Index := ml.IndexByName('memo2'); if Index > -1 then MessageBox(ml[Index].Name,mtInformation,[mbOK],0); // Vil skrive Memo2 finally ml.Free; // Bemærk, frigiver ikke Memo1, Memo2, Memo3 end; end;
Lige et afsluttende ord om hvad der egentligt foregår: En klasse (TMemoList) er en definition som ikke eksisterer i hukommelsen. Et objekt er definitionen udført og er allokeret i hukommelsen. Et objekt er groft sagt en pointer til adressen i hukommelsen.
Når der proppes en TMemo ind i en TMemoList (ml.Add() eller ml.Insert()), så gemmer man kun denne pointer. Derved undgår man at skulle lave en kopi da man blot gemmer memo'ens 32-bits adresse. Ulempen er, at hvis du retter i en TMemo som du har gemt i TMemoListen, så retter du altså også indholdet der. Fordi det er et og samme objekt.
Jeg er så slet ikke enig, jeg giver jo et eks. på det der bliver spurgt om:
King skriver: "teks fra flere memoer gemt i en varabel.." Dvs. han vil have teksten fra memoerne og ikke selve memoerne.
King skriver: "Jeg ønsker skal kunne initialisere en variabel med flere Momoer’s tekst" Han vil have at man kan initialisere klassen med teksten fra flere memoer, og det er lige hvad jeg har lavet.
Jeg kan ikke se et problem i at lave en AddMemo selvom der findes en Add, jeg syntes faktisk det er bedre end som at override Add.
Jeg tror ikke det er smart at gør som du forslår nemlig at bruge TItemList hvor kun en pointer til de enkelte memos gemmes, hvad nu hvis King vil gemme en memo tekst fra en form som lige derefter free´es, så mister man sin memo tekst. Eller hvis nu han vil gemme en memo tekst og så have mulighed for at ændre i memoen uden at den gemte tekst ændres.
Nå ja så er det jo også lige det med at kunne gemme/læse alle memo tekster, med min klasse forslag er det jo ganske enkelt at gemme til en fil ved at bruge klassens SaveToFile funktion. Og LoadFromFile for at indlæse alle memo tekster igen.
Jeg påpeger også selv fordelene og ulemperne ved TStringList og TListItem. Hvis TMemo'erne er placeret på en form skal den nok sørge for at frigive det igen. Alternativt må man bruge TObjectList. Problemet med din Memo-klasse (og jeg gentager mig selv lidt) er, at du offentliggør for meget af TStringList-klassen og at du bruger constructoren forkert. Du lader Add og Insert være tilgængelige, men indsætter via constructoren. Det er ikke sikker OOP! Den bedste (subjektivt vurderet) løsning vil være en svag (= indirekte) nedarving af TStringList, noget lignende nedenstående:
type TMemoList = class private fMemos : TStringList; function GetString(const aIndex: integer): string; public constructor Create; destructor Destroy; override; property Items[const aIndex : integer] : string read GetString; default; function Count : integer; procedure Clear; function Add(aMemo : TMemo) : integer; procedure Insert(const aIndex: integer; aMemo: TMemo); function IndexOfName(const aName : string) : integer;
// Herefter går jeg lidt amok for jeg kunne bare bruge fMemos metoder til det // at gemme og hente - jeg ved det godt - men det er da pænt lavet, ikke? procedure SaveToFile(const aFilename : string); procedure SaveToStream(const aStream : TStream); procedure LoadFromFile(const aFilename : string); procedure LoadFromStream(aStream : TStream); end;
implementation
{ TMemoList }
constructor TMemoList.Create; begin inherited Create; fMemos := TStringList.Create; end;
destructor TMemoList.Destroy; begin fMemos.Free; inherited; end;
function TMemoList.GetString(const aIndex : integer) : string; begin result := fMemos.ValueFromIndex[aIndex]; end;
function TMemoList.Count : integer; begin result := fMemos.Count; end;
procedure TMemoList.Clear; begin fMemos.Clear; end;
function TMemoList.Add(aMemo : TMemo) : integer; begin // Eller kommatext om du synes. result := fMemos.Add(format('%s=%s',[aMemo.Name, aMemo.Text])); end;
function TMemoList.IndexOfName(const aName : string) : integer; begin result := fMemos.IndexOfName(aName); end;
procedure TMemoList.LoadFromFile(const aFilename: string); var fs : TFileStream; begin fs := TFileStream.Create(aFilename,fmOpenRead or fmShareDenyWrite); try LoadFromStream(fs); finally fs.Free; end; end;
procedure TMemoList.LoadFromStream(aStream: TStream); var Reader : TReader; MemoName, MemoValue : string; begin Reader := TReader.Create(aStream, 1024); try fMemos.Clear; while aStream.Position < aStream.Size do begin MemoName := Reader.ReadString; MemoValue := Reader.ReadString; fMemos.Add(format('%s=%s',[MemoName, MemoValue])); end; finally Reader.Free; end; end;
procedure TMemoList.SaveToFile(const aFilename: string); var fs : TFileStream; begin fs := TFileStream.Create(aFilename,fmCreate or fmShareExclusive); try SaveToStream(fs); finally fs.Free; end; end;
procedure TMemoList.SaveToStream(const aStream: TStream); var i : integer; Writer : TWriter; begin Writer := TWriter.Create(aStream,1024); try for i := 0 to fMemos.Count - 1 do begin Writer.WriteString(fMemos.Names[i]); Writer.WriteString(fMemos.ValueFromIndex[i]); end; finally Writer.Free; end; end;
Her er tale om svag nedarving hvor der er lavet ordentlig indkapsling. Jeg gik lidt amok mht. det at gemme og hente data; er lidt facineret af TReader og TWriter for tiden. Der offentliggøres ikke noget der ikke virker. Jeg foretrækker selv en direkte nedarving da det er så let, men man risikerer tit at man får metoder og attributter med som ikke virker - det er noget man opdater et stykke udi fremtiden hvor man sætter sig ned og skal debugge sin kode. Til den tid har man glemt at klassen var "usikker".
I min testform for jeg kan godt se at jeg skal være velformuleret og at tingene skal være ordentligt testede har jeg lavet 3 memoer som jeg gemmer som strenge (altså ikke som TMemo'er):
procedure TForm1.FormCreate(Sender: TObject); begin fMemoList := TMemoList.Create; end;
procedure TForm1.FormDestroy(Sender: TObject); begin fMemoList.Free; end;
procedure TForm1.Button1Click(Sender: TObject); begin fMemoList.Add(Memo1); fMemoList.Add(Memo2); fMemoList.Add(Memo3); end;
procedure TForm1.Button2Click(Sender: TObject); begin fMemoList.SaveToFile('c:\test.dat'); end;
procedure TForm1.Button3Click(Sender: TObject); begin fMemoList.LoadFromFile('c:\test.dat'); end;
I øvrigt vil jeg gerne undskylde overfor kinginkvong at det nu har risikoen for at udvikle sig til en stædig og temmelig volumeniøs objektorienteret religionskrig :-) - håber du kan få lidt ud af det alligevel.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.