2012-06-10 15 views
12

Utworzono komponent, TGridPaintBox, oparty na TPaintBox. Jest to w zasadzie paintbox z dodaną "funkcjonalnością siatki". To nie jest siatka danych. Bardziej jak komponent szachownicy.Dlaczego mój składnik niestandardowy nie jest aktualizowany po zmianie właściwości?

W eksploratorze obiektów mogę ustawić określone właściwości. Co najważniejsze, mogę ustawić wymiary siatki (ile komórek w poprzek/w dół), ale także opcje związane z rysowaniem. Czy komórki mają być kwadratowe, kolor nieparzystych/równych komórek itd.

Moja pierwsza wersja tego komponentu miała właściwości bezpośrednio na klasie, a kiedy zmieniłem właściwość, rysunek designtime został natychmiast zaktualizowany. Wraz z rozwojem komponentu, chciałem trochę lepiej uporządkować moje właściwości i wprowadzić pewne "właściwości opcji", takie jak opcje rysowania, opcje zachowania itp. Po wprowadzeniu tego rysunku designtime nie jest już aktualizowany jak poprzednio. Po zmianie właściwości muszę kliknąć komponent, który ma zostać zaktualizowany. Czy ktoś może mi powiedzieć, dlaczego tak się dzieje?

Oto obnażona wersja kodu. Mam nadzieję, że to wytłumaczy zachowanie:

(PS: To jest mój pierwszy komponent, mimo że używam Delphi od 1997 roku, więc jeśli ktoś zauważy coś głupiego w sposób, w jaki to zrobiłem, proszę możesz mi powiedzieć)

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. 

Odpowiedz

13

Dzieje się tak, ponieważ nie masz ustawnika dla zestawu opcji, który unieważniłby twoją kontrolę, do której należy. Kliknięcie w projektanta formularzy powoduje, że kontrolka unieważnia się, ale powinieneś sobie z tym poradzić w takich ustawieniach opcji. Więc chciałbym przechowywać właściciela opcji dla lepszego dostępu do bezpośredniego instancji klasy właściciel i w opcjach seter wymusić to właściciel, kontrola przerysować:

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; 

Dalsze wskazówki:

jeśli nie trzeba wyraźnie mieć opublikowanych właściwości i zdarzeń, takich jak na przykład Color, Font lub OnPaint, wyprowadzić swoją kontrolę z TGraphicControl zamiast z TPaintBox. Możesz wybrać, które właściwości i wydarzenia będą publikowane przez Ciebie.

+1

Tak, literówka. W rzeczywistości jest to klasa TGridPaintBox, ale pomieszałem podczas edycji tego wpisu. Zaktualizuję moje pytanie. –

+1

Ponadto, dzięki za wskazówkę wynikającą z TGraphicControl zamiast z TPaintBox. I oczywiście dzięki za resztę odpowiedzi. To rozwiązało mój problem :-) –

Powiązane problemy