Avatar billede kinginkvong Nybegynder
03. maj 2006 - 21:18 Der er 16 kommentarer og
1 løsning

teks fra flere memoer gemt i en varabel, fra en klasse

Hej jeg har følgende problem:

Jeg ønsker skal kunne initialisere en variabel med flere Momoer’s tekst, og variablen skal være oprettet i en klasse i en anden unit.


Er der nogen der kan løse denne lille op gave for mig, jeg personligt ikke stærk i OOP og klasser i Delphi.

På forhånd tak

Morten Nielsen
Avatar billede kroning Nybegynder
03. maj 2006 - 22:30 #1
Kan du forklare det lidt nærmere?
Avatar billede kroning Nybegynder
03. maj 2006 - 23:09 #2
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.
Avatar billede kinginkvong Nybegynder
03. maj 2006 - 23:32 #3
Man skulde tror at du var tankelæser...

Det var faktisk det jeg leder efter, tror jeg. Jeg er som sagt ikke så kendt med klasser.

Har selv fundet en andre løsning hvor jeg ikke bruger klasser og array, men det er din løsning jeg ville have, så det er nok den jeg bruger.

tak, men jeg forstår dog ikke hvad du mener med "TMemoens CommaText property" og hvorfor skriver du "inherited create;" hvad gør den kode?

Morten
Avatar billede kroning Nybegynder
04. maj 2006 - 08:41 #4
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.
Avatar billede hrc Mester
08. maj 2006 - 16:06 #5
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.
Avatar billede kroning Nybegynder
08. maj 2006 - 16:25 #6
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.
Avatar billede kinginkvong Nybegynder
10. maj 2006 - 00:00 #7
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:-)

Morten
Avatar billede kroning Nybegynder
10. maj 2006 - 09:12 #8
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?
Avatar billede hrc Mester
10. maj 2006 - 09:13 #9
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
Avatar billede kroning Nybegynder
10. maj 2006 - 09:31 #10
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.
Avatar billede kroning Nybegynder
10. maj 2006 - 10:07 #11
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;
Avatar billede hrc Mester
10. maj 2006 - 11:33 #12
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.
Avatar billede kroning Nybegynder
10. maj 2006 - 14:11 #13
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.
Avatar billede kroning Nybegynder
10. maj 2006 - 14:15 #14
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.
Avatar billede hrc Mester
10. maj 2006 - 22:06 #15
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.Insert(const aIndex: integer; aMemo: TMemo);
begin
  fMemos.Insert(aIndex, format('%s=%s',[aMemo.Name, aMemo.Text]));
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):

  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Memo3: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button3Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    fMemoList : TMemoList;
  public
  end;

implementation

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.
Avatar billede kinginkvong Nybegynder
18. juli 2006 - 18:51 #16
Hmmm... ryder lig op i oprettet spørgsmål og vil gerne havde denne lukket.

så kroning gider du lige ligge et svar?
Avatar billede kroning Nybegynder
18. juli 2006 - 19:46 #17
ok
Avatar billede Ny bruger Nybegynder

Din løsning...

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester