2012-06-10 7 views
12

Ich habe eine Komponente, TGridPaintBox, basierend auf TPaintBox erstellt. Es ist im Grunde ein Paintbox mit "Grid-Funktionalität" hinzugefügt. Es ist kein Datenraster. Mehr wie eine Schachbrettkomponente.Warum wird meine benutzerdefinierte Komponente nicht aktualisiert, wenn ich Eigenschaften ändere?

Im Objekt-Explorer kann ich bestimmte Eigenschaften festlegen. Am wichtigsten ist, dass ich die Rastermaße festlegen kann (wie viele Zellen über/unter), aber auch Optionen bezüglich des Zeichnens. Ob die Zellen quadratisch sein sollten, die Farbe von ungeraden/geraden Zellen usw.

Meine erste Version dieser Komponente hatte Eigenschaften direkt auf der Klasse, und wenn ich eine Eigenschaft änderte, wurde die Zeichnungszeitzeichnung sofort aktualisiert. Als die Komponente wuchs, wollte ich meine Eigenschaften ein wenig besser organisieren und führte einige "Optioneneigenschaften" ein, wie Zeichenoptionen, Verhaltensoptionen usw. Nach dieser Einführung wird die Zeichnungszeitzeichnung nicht mehr wie zuvor aktualisiert. Nach dem Ändern einer Eigenschaft muss ich auf die Komponente klicken, um sie zu aktualisieren. Kann mir jemand sagen, warum das passiert?

Hier ist eine abgespeckte Version des Codes. Ich hoffe, es wird das Verhalten erklären:

(PS: Dies ist meine erste Komponente, obwohl ich Delphi seit 1997 verwendet habe, also wenn jemand etwas dummes in der Art finde ich habe es getan, bitte fühlen frei, mir zu sagen)

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. 

Antwort

13

Es geschieht, weil Sie keinen Setter für die Optionen festgelegt haben, die Ihre Steuer ungültig machen würde, das gehört. Der Klick im Formular-Designer ruft das Steuerelement auf, um ungültig zu machen, aber Sie sollten dies in einem solchen Optionen-Setter selbst behandeln. Also ich würde speichern Sie die Optionen Eigentümer für einen besseren Zugang zum direkten Eigentümer Klasseninstanz und in den Optionen Setter dieser Besitzer zwingen, an die Steuerung neu zu zeichnen:

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; 

Weitere Hinweise:

Wenn Sie müssen nicht explizit die veröffentlichten Eigenschaften und Ereignisse des Malkastens wie zum Beispiel Color, Font oder OnPaint event haben, leiten Sie Ihre Kontrolle von TGraphicControl statt von TPaintBox ab. Sie können auswählen, welche Eigenschaften und Ereignisse Sie selbst veröffentlichen möchten.

+1

Ja, Tippfehler. Es ist tatsächlich in der TGridPaintBox-Klasse, aber ich habe beim Bearbeiten für diesen Beitrag versagt. Ich werde meine Frage aktualisieren. –

+1

Danke auch für den Tipp über TGraphicControl statt TPaintBox. Und natürlich, danke für den Rest der Antwort. Es hat mein Problem gelöst :-) –

Verwandte Themen