2012-06-10 11 views
12

Ho creato un componente, TGridPaintBox, basato su TPaintBox. È fondamentalmente un paintbox con "funzionalità di griglia" aggiunta. Non è una griglia di dati. Più simile a un componente della scacchiera.Perché il componente personalizzato non viene aggiornato quando cambio le proprietà?

Nell'esploratore di oggetti è possibile impostare determinate proprietà. Soprattutto, posso impostare le dimensioni della griglia (quante celle sono aperte/discese), ma anche le opzioni relative al disegno. Se le celle devono essere quadrate, il colore delle celle pari/dispari ecc.

La mia prima versione di questo componente aveva proprietà direttamente sulla classe, e quando ho cambiato una proprietà, il disegno è stato aggiornato immediatamente. Mentre il componente cresceva, volevo organizzare le mie proprietà un po 'meglio e ho introdotto alcune "proprietà di opzioni", come opzioni di disegno, opzioni di comportamento, ecc. Dopo averlo introdotto, il disegno non si aggiornava più come prima. Dopo aver modificato una proprietà, devo fare clic sul componente da aggiornare. Qualcuno può dirmi perché questo succede?

Ecco una versione ridotta del codice. Spero che spiegherà il comportamento:

(PS: Questo è il mio primo componente, anche se uso Delphi dal 1997, quindi se qualcuno può individuare qualcosa di stupido nel modo in cui l'ho fatto, per favore sentiti libero di dirmi)

unit GridPaintBox; 

interface 

type 
    TGridDrawOption = (gdoSquareCells,gdoCenterCells,gdoDrawCellEdges,gdoDrawFocus); 
    TGridDrawOptions = set of TGridDrawOption; 

    TGridOptions = class(TPersistent) 
    private 
    FCellsX : integer; 
    FCellsY : integer; 
    FDrawOptions : TGridDrawOptions; 
    public 
    constructor Create(aGridPaintBox : TGridPaintBox); 
    procedure Assign(Source : TPersistent); override; 
    published 
    property CellsX : integer read FCellsX write FCellsX; 
    property CellsY : integer read FCellsY write FCellsY; 
    property DrawOptions : TGridDrawOptions read FDrawOptions write FDrawOptions; 
    end; 

    TGridPaintBox = class(TPaintBox) 
    private 
    FGridOptions : TGridOptions; 
    FFocusedX, 
    FFocusedY : integer; 
    FOnFocusChanged: TNotifyEvent; 
    procedure CalculateSizeAndPosition; 
    procedure DrawCell(X,Y : integer); 
    procedure DrawCells; 
    procedure SetGridOptions(const Value: TGridOptions); 
    protected 
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; 
    public 
    constructor Create(aOwner : TComponent); override; 
    destructor Destroy; override; 
    procedure Paint; override; 
    procedure SetFocus(X,Y : integer); 
    published 
    property OnFocusChanged : TNotifyEvent read FOnFocusChanged write FOnFocusChanged; 
    property Options : TGridOptions read FGridOptions write SetGridOptions; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterComponents('Samples', [TGridPaintBox]); 
end; 

procedure TGridPaintBox.CalculateSizeAndPosition; 
begin 
    <...> 
end; 

constructor TGridPaintBox.Create(aOwner: TComponent); 
begin 
    inherited; 
    FGridOptions := TGridOptions.Create(self); 
end; 

procedure TGridPaintBox.DrawCell(X, Y: integer); 
begin 
    <...> 
end; 

procedure TGridPaintBox.DrawCells; 
var 
    X,Y : integer; 
begin 
    CalculateSizeAndPosition; 

    for Y := 0 to FGridOptions.CellsY-1 do 
    for X := 0 to FGridOptions.CellsX-1 do 
     DrawCell(X,Y); 
end; 

procedure TGridPaintBox.Paint; 
begin 
    Canvas.Font := Font; 
    Canvas.Brush.Color := Color; 
    Canvas.Brush.Style := bsSolid; 
    Canvas.FillRect(Rect(0,0,Width,Height)); 
    DrawCells; 
    if Assigned(OnPaint) then 
    OnPaint(Self); 
end; 

procedure TGridPaintBox.SetGridOptions(const Value: TGridOptions); 
begin 
    FGridOptions.Assign(Value); 
    invalidate; 
end; 

procedure TGridPaintBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
begin 
    SetFocus(PixelToCellX(X),PixelToCellY(Y)); 
    inherited; 
end; 

procedure TGridPaintBox.SetFocus(X, Y: integer); 
begin 
    if (FocusedX=X) and (FocusedY=Y) then 
    exit; 

    FFocusedX := X; 
    FFocusedY := Y; 

    if assigned(OnFocusChanged) then 
    OnFocusChanged(self); 

    invalidate; 
end; 

constructor TGridOptions.Create(aGridPaintBox : TGridPaintBox); 
begin 
    FCellsX := 20; 
    FCellsY := 8; 
    FDrawOptions := [gdoSquareCells,gdoCenterCells,gdoDrawCellEdges]; 
end; 

procedure TGridOptions.Assign(Source : TPersistent); 
begin 
    if Source is TGridOptions then 
    begin 
    FCellsX := TGridOptions(Source).CellsX; 
    FCellsY := TGridOptions(Source).CellsY; 
    FDrawOptions := TGridOptions(Source).DrawOptions; 
    end 
    else 
    inherited; 
end; 

end. 

risposta

13

succede perché non si dispone di un setter per le opzioni impostate tali da inficiare il vostro controllo, che appartiene a. Il clic nel progettista del modulo invoca il controllo per invalidare, tuttavia, ma dovresti gestirlo da solo in tale setter di opzioni. Quindi mi sento di memorizzare il proprietario opzioni per un migliore accesso alla diretta istanza di classe proprietario e nelle opzioni di Setter forzare questo proprietario, il controllo per ridisegnare:

type 
    TGridPaintBox = class; 
    TGridDrawOption = (gdoSquareCells, gdoCenterCells, gdoDrawCellEdges, gdoDrawFocus); 
    TGridDrawOptions = set of TGridDrawOption; 
    TGridOptions = class(TPersistent) 
    private 
    FOwner: TGridPaintBox; 
    FCellsX: Integer; 
    FCellsY: Integer; 
    FDrawOptions: TGridDrawOptions; 
    procedure SetCellsX(AValue: Integer); 
    procedure SetCellsY(AValue: Integer); 
    procedure SetDrawOptions(const AValue: TGridDrawOptions); 
    public 
    constructor Create(AOwner: TGridPaintBox); 
    procedure Assign(ASource: TPersistent); override; 
    published 
    property CellsX: Integer read FCellsX write SetCellsX; 
    property CellsY: Integer read FCellsY write SetCellsY; 
    property DrawOptions: TGridDrawOptions read FDrawOptions write SetDrawOptions; 
    end; 

implementation 

constructor TGridOptions.Create(AOwner: TGridPaintBox); 
begin 
    FOwner := AOwner; 
    FCellsX := 20; 
    FCellsY := 8; 
    FDrawOptions := [gdoSquareCells, gdoCenterCells, gdoDrawCellEdges]; 
end; 

procedure TGridOptions.SetCellsX(AValue: Integer); 
begin 
    if FCellsX <> AValue then 
    begin 
    FCellsX := AValue; 
    FOwner.Invalidate; 
    end; 
end; 

procedure TGridOptions.SetCellsY(AValue: Integer); 
begin 
    if FCellsY <> AValue then 
    begin 
    FCellsY := AValue; 
    FOwner.Invalidate; 
    end; 
end; 

procedure TGridOptions.SetDrawOptions(const AValue: TGridDrawOptions); 
begin 
    if FDrawOptions <> AValue then 
    begin 
    FDrawOptions := AValue; 
    FOwner.Invalidate; 
    end; 
end; 

Ulteriori note:

se si non è necessario disporre esplicitamente di proprietà ed eventi di Paint box come ad esempio , Font o OnPaint, derivare il controllo da TGraphicControl anziché da TPaintBox. Puoi scegliere quali proprietà ed eventi pubblicherete da soli.

+1

Sì, errore di battitura. In realtà è nella classe TGridPaintBox, ma mi sono incasinato durante la modifica per questo post. Aggiornerò la mia domanda –

+1

Inoltre, grazie per il suggerimento derivante da TGraphicControl piuttosto che da TPaintBox. E, naturalmente, grazie per il resto della risposta. Ha risolto il mio problema :-) –

Problemi correlati