2013-05-29 15 views
7

Sto tentando di utilizzare una matrice di una classe personalizzata come proprietà per il mio componente, ma il problema è che i valori non sono stati salvati nel componente, il che significa che se imposto i valori, salvare tutto e aprire nuovamente il progetto, i valori per la componente scompare ... il mio codice è simile al seguente:Matrice di una classe personalizzata come proprietà

unit Unit1; 

interface 

uses Windows, ExtCtrls,Classes,Controls; 

type 

    TMyClass=class(TPersistent) 
    private 
    FName: string; 
    FValue: double; 
    public 
    property Name: string read FName write FName; 
    property Value: double read FValue write FValue; 
    end; 

    TMyComponent= class(TCustomPanel) 
    private 
    FMyArray: array[0..200] of TMyClass; 

    function GetmyArray(Index: Integer): TMyClass; 

    procedure SetMyArray(index: Integer; Value: TMyClass); 
    public 
    property myArray[index: Integer]: TMyClass read GetMyArray write SetMyArray; 
    end; 

implementation 

function TMyComponent.GetmyArray(Index: Integer): TMyClass; 
begin 
    result:= FmyArray[Index]; 
end; 

procedure TMyComponent.SetMyArray(index: Integer; Value: TMyClass); 
begin 
    FMyArray[index].FName:= Value.FName; 
    FMyArray[index].FValue:= Value.FValue; 
end; 

end. 

so che che le proprietà solo pubblicati possono essere trasmessi, ma il problema è che la mia proprietà è una matrice e non può essere pubblicata ... Un suggerimento che avevo era di usare DefineProperties() per fornire uno streaming personalizzato ma non vedo come farlo con un array. Un'altra possibilità che pensavo fosse di modificare TMyClass in un tipo di classe che TMyComponent potrebbe essere il genitore di esso, come è fatto in TChart, che è possibile aggiungere diverse classi di serie ad esso. Ma non so cosa classe questo dovrebbe essere

TMyClass=class(T???????????) 

Con che avrei potuto prendere il MyArray proprietà e creare TMyClass e aggiungere al TMyComponent come il seguente:

MyArray1.parent:= MyComponent1; 
MyArray2.parent:= MyComponent2; 
... 

. Qual è l'opzione migliore? O c'è qualche altra idea migliore?

risposta

14

la soluzione più semplice (e preferibilmente) è cambiare TMyClass derivare da TCollectionItem e cambiarne TMyComponent.FMyArray a TOwnedCollection. Successivamente, DFM invierà automaticamente gli articoli in streaming e otterrai un supporto nativo in fase di progettazione per la creazione e la manipolazione degli oggetti TMyClass e delle relative proprietà.

Prova questo:

unit Unit1; 

interface 

uses 
    Windows, ExtCtrls, Classes, Controls; 

type 
    TMyClass = class(TCollectionItem) 
    private 
    FName: string; 
    FValue: double; 

    procedure SetName(const AValue: string); 
    procedure SetValue(AValue: double); 
    public 
    procedure Assign(ASource: TPersistent); override; 
    published 
    property Name: string read FName write SetName; 
    property Value: double read FValue write SetValue; 
    end; 

    TMyArray = class(TOwnedCollection) 
    private 
    function GetItem(Index: Integer): TMyClass; 
    procedure SetItem(Index: Integer; const Value: TMyClass); 
    public 
    constructor Create(AOwner: TPersistent); 
    function Add: TMyClass; reintroduce; 
    function Insert(Index: Integer): TMyClass; reintroduce; 
    property Items[Index: Integer]: TMyClass read GetItem write SetItem; default; 
    end; 

    TMyComponent = class(TCustomPanel) 
    private 
    FMyArray: TMyArray; 

    procedure SetMyArray(Value: TMyArray); 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
    published 
    property myArray: TMyArray read FMyArray write SetMyArray; 
    end; 

implementation 

procedure TMyClass.Assign(ASource: TPersistent); 
begin 
    if ASource is TMyClass then 
    begin 
    with TMyClass(ASource) do 
    begin 
     Self.FName := Name; 
     Self.FValue := Value; 
    end; 
    Changed(False); 
    end else 
    inherited; 
end; 

procedure TMyClass.SetName(const AValue: string); 
begin 
    if FName <> AValue then 
    begin 
    FName := AValue; 
    Changed(False); 
    end; 
end; 

procedure TMyClass.SetValue(AValue: double); 
begin 
    if FValue <> AValue then 
    begin 
    FValue := AValue; 
    Changed(False); 
    end; 
end; 

constructor TMyArray.Create(AOwner: TPersistent); 
begin 
    inherited Create(AOwner, TMyClass); 
end; 

function TMyArray.GetItem(Index: Integer): TMyClass; 
begin 
    Result := TMyClass(inherited GetItem(Index)); 
end; 

procedure TMyArray.SetItem(Index: Integer; const Value: TMyClass); 
begin 
    inherited SetItem(Index, Value); 
end; 

function TMyArray.Add: TMyClass; 
begin 
    Result := TMyClass(inherited Add); 
end; 

function TMyArray.Insert(Index: Integer): TMyClass; 
begin 
    Result := TMyClass(inherited Insert(Index)); 
end; 

constructor TMyComponent.Create(AOwner: TComponent); 
begin 
    inherited; 
    FMyArray := TMyArray.Create(Self); 
end; 

destructor TMyComponent.Destroy; 
begin 
    FMyArray.Free; 
    inherited; 
end; 

procedure TMyComponent.SetMyArray(Value: TMyArray); 
begin 
    FMyArray.Assign(Value); 
end; 

end. 
+0

Ho provato questa versione e funziona bene, devo solo testare il mio codice reale che è un po 'più complesso, grazie mille – Felipe

+0

Cercavo la stessa cosa. Ottima risposta da Remy, grazie mille. –

5

Vorrei votare per DefineProperties! Il codice necessario dovrebbe assomigliare a questo (assumendo nessuna delle istanze nella matrice è nullo):

procedure TMyComponent.DefineProperties(Filer: TFiler); 
begin 
    inherited; 
    Filer.DefineProperty('MyArray', ReadMyArray, WriteMyArray, true); 
end; 

procedure TMyComponent.ReadMyArray(Reader: TReader); 
var 
    N: Integer; 
begin 
    N := 0; 
    Reader.ReadListBegin; 
    while not Reader.EndOfList do begin 
    Reader.ReadListBegin; 
    FMyArray[N].Name := Reader.ReadString; 
    FMyArray[N].Value := Reader.ReadFloat; 
    Reader.ReadListEnd; 
    Inc(N); 
    end; 
    Reader.ReadListEnd; 
end; 

procedure TMyComponent.WriteMyArray(Writer: TWriter); 
var 
    I: Integer; 
begin 
    Writer.WriteListBegin; 
    for I := 0 to High(FMyArray) do begin 
    Writer.WriteListBegin; 
    Writer.WriteString(FMyArray[I].Name); 
    Writer.WriteFloat(FMyArray[I].Value); 
    Writer.WriteListEnd; 
    end; 
    Writer.WriteListEnd; 
end; 
+1

ho ottenuto un errore: [DCC errore] MyComponentTest1.pas (155): E2362 Impossibile accedere simbolo protetta TReader.ReadProperty E la stessa cosa per WriteProperties – Felipe

+1

davvero! Ho dimenticato di avere un aiutante di classe in ambito che ha fatto la cosa. Aggiornata la risposta. –

+0

Ancora non ha ottenuto il mio obiettivo .. Posso controllare il modulo come testo e ho ottenuto che: 'MyComponent1 oggetto: TMyComponent sinistra = 160 Top = 181 Width = 185 Altezza = 41 MyArray = ( ()) ' – Felipe

Problemi correlati