[ komplikator @ 04.02.2011. 11:09 ] @
Pozdrav svima!

Napravio sam custom kontrolu koja naslijeđuje TDBGrid. Ova moja bi trebala imati svojstva svijetlije, tamnije boje kolone i prikazivanja (boolean) za dbgrid obojan u "zebru".

No prema ovome jednostavno neće. Instaliram kontrolu, ona uredno radi kao običan dbgrid, vide se svojstva u inspectoru, ali ne farba grid. Jesam li negdje promašio u radu s eventima, vjerojatno jesam?

Code:

unit B8DBGrid;

interface

uses
  SysUtils, Classes, Controls, Grids, DBGrids, Types, Graphics;

type
  TZebra = Array [ boolean ] of TColor;

type
  TB8DBGrid = class( TDBGrid )
    private
      fZebra: TZebra;
      fZebraDark: TColor;
      fZebraLight: TColor;
      fShowZebra: boolean;
      fOnDrawColumnCell: TDrawColumnCellEvent;
      function getShowZebra: boolean;
      procedure setShowZebra( const Value: boolean );
      function getZebraDark: TColor;
      function getZebraLight: TColor;
      procedure setZebraDark( const Value: TColor );
      procedure setZebraLight( const Value: TColor );
    protected
      procedure DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState );

    public

    published
      property ColorZebraLight: TColor read getZebraLight write setZebraLight;
      property ColorZebraDark: TColor read getZebraDark write setZebraDark;
      property ShowZebra: boolean read getShowZebra write setShowZebra Default True;
      property ondrawcolumncell  : TDrawColumnCellEvent read fOnDrawColumnCell write fOnDrawColumnCell;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents( '8Bita', [ TB8DBGrid ] );
end;

{ T8BDBGrid }

function TB8DBGrid.getShowZebra: boolean;
begin
  Result := fShowZebra;
end;

function TB8DBGrid.getZebraDark: TColor;
begin
  Result := fZebraDark;
end;

function TB8DBGrid.getZebraLight: TColor;
begin
  Result := fZebraLight;
end;

procedure TB8DBGrid.DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState );

VAR
  ColOdd: boolean;

BEGIN

  TRY
    ColOdd := ( ( Sender AS TDBGrid ).DataSource.DataSet.RecNo MOD 2 <> 0 );
    fZebra[ False ] := fZebraLight;
    fZebra[ True ] := fZebraDark;

    IF ( gdSelected IN State ) THEN

      BEGIN 

        ( Sender AS TDBGrid ).Canvas.Brush.Color := clHighlight;
        ( Sender AS TDBGrid ).Canvas.Font.Color := clHighlightText;
      END

    ELSE

      IF fShowZebra then
       ( Sender AS TDBGrid ).Canvas.Brush.Color := fZebra[ ColOdd ];
       ( Sender AS TDBGrid ).DefaultDrawColumnCell( Rect, DataCol, Column, State );

  EXCEPT
  END;

end;

procedure TB8DBGrid.setShowZebra( const Value: boolean );
begin
  fShowZebra := Value;

end;

procedure TB8DBGrid.setZebraDark( const Value: TColor );
begin
  fZebraDark := Value;
end;

procedure TB8DBGrid.setZebraLight( const Value: TColor );
begin
  fZebraLight := Value;
end;

end.
[ Milos D @ 04.02.2011. 12:04 ] @
Fali override:

Code:

procedure DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState ); Override;
[ komplikator @ 04.02.2011. 12:17 ] @
Znam, no override direktiva mi uzrokuje pogrešku: [DCC Error] B8DBGrid.pas(26): E2037 Declaration of 'DrawColumnCell' differs from previous declaration. Ne shvaćam zašto, procedura tj. metoda je identična po tipovima originalnoj za TDbGrid.

Edit: nisam shvatio da zbilja pozivam pogrešne parametre, nema "Sendera" što je logično jer se klasa obraća sama sebi tj. Self, zar ne?

Prepravio sam kod u dolje prikazani kod. Sad se dešava jedna zanimnjiva pojava: u designtime-u. kontrola oboji redove (nakon run-a.) no runtime tj. u samom programu neće. Očigledno još negdje nešto krivo inicializiram, ili griješim s ovim Self-om ?

Code:

unit B8DBGrid;

interface

uses
  SysUtils, Classes, Controls, Grids, DBGrids, Types, Graphics;

type
  TZebra = Array [ boolean ] of TColor;

type
  TB8DBGrid = class( TDBGrid )
    private
      fZebra: TZebra;
      fZebraDark: TColor;
      fZebraLight: TColor;
      fShowZebra: boolean;
      fOnDrawColumnCell: TDrawColumnCellEvent;
      function getShowZebra: boolean;
      procedure setShowZebra( const Value: boolean );
      function getZebraDark: TColor;
      function getZebraLight: TColor;
      procedure setZebraDark( const Value: TColor );
      procedure setZebraLight( const Value: TColor );
    protected
      procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
      Column: TColumn; State: TGridDrawState);   override;

    public

    published
      property ColorZebraLight: TColor read getZebraLight write setZebraLight;
      property ColorZebraDark: TColor read getZebraDark write setZebraDark;
      property ShowZebra: boolean read getShowZebra write setShowZebra Default True;
      property ondrawcolumncell
        : TDrawColumnCellEvent read fOnDrawColumnCell write fOnDrawColumnCell;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents( '8Bita', [ TB8DBGrid ] );
end;

{ T8BDBGrid }

function TB8DBGrid.getShowZebra: boolean;
begin
  Result := fShowZebra;
end;

function TB8DBGrid.getZebraDark: TColor;
begin
  Result := fZebraDark;
end;

function TB8DBGrid.getZebraLight: TColor;
begin
  Result := fZebraLight;
end;

procedure TB8DBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;
      Column: TColumn; State: TGridDrawState);
VAR
  ColOdd: boolean;

BEGIN

  TRY
    ColOdd := Self.DataSource.DataSet.RecNo MOD 2 <> 0 ;
    fZebra[ False ] := fZebraLight;
    fZebra[ True  ] := fZebraDark;

    IF ( gdSelected IN State ) THEN

      With self do BEGIN
        Canvas.Brush.Color := clHighlight;
        Canvas.Font.Color := clHighlightText;
      END

    ELSE
      if fShowZebra then
    with self do begin
     Canvas.Brush.Color := fZebra[ ColOdd ];
     DefaultDrawColumnCell( Rect, DataCol, Column, State );
    end;
  EXCEPT
  END;

end;

procedure TB8DBGrid.setShowZebra( const Value: boolean );
begin
  fShowZebra := Value;

end;

procedure TB8DBGrid.setZebraDark( const Value: TColor );
begin
  fZebraDark := Value;
end;

procedure TB8DBGrid.setZebraLight( const Value: TColor );
begin
  fZebraLight := Value;
end;

end.


[Ovu poruku je menjao komplikator dana 04.02.2011. u 13:37 GMT+1]
[ Milos D @ 04.02.2011. 14:21 ] @
Self ti ne treba jer se podrazumeva. Na primer, kad kažeš DataSource.nešto podrazumeva se da misliš na self.DataSource.nešto. Jedini izuzetak bi bio ako bi u proceduri definisao lokalnu promenljivu sa imenom "DataSource", e tad bi ona imala prioritet i samo tada bi morao da uradiš self.DataSource da bi, umesto lokalnoj, pristupio promenljivi koja pripada objektu.

Procedure koje postavljaju vrednosti property-ja bi trebalo da urade i Invalidate da bi naterao grid da se ponovo iscrta.

[ rambo @ 04.02.2011. 17:33 ] @
Evo ti nešto jednostavnije rešenje
Code (delphi):

unit dpDBGrid;

interface

uses
  SysUtils, Classes, Controls, Graphics, Types, Grids, DBGrids;

type
  TdpDBGrid = class(TDBGrid)
  private
    fZebraColorLight: TColor;
    fZebraColorDark: TColor;
    fZebraEnabled: Boolean;
    procedure SetZebraColorLight(Value: TColor);
    procedure SetZebraColorDark(Value: TColor);
    procedure SetZebraEnabled(Value: Boolean);
    function GetZebraColorLight: TColor;
    function GetZebraColorDark: TColor;
  protected
    procedure DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property ZebraColorLight: TColor read GetZebraColorLight write SetZebraColorLight;
    property ZebraColorDark: TColor read GetZebraColorDark write SetZebraColorDark;
    property ZebraEnabled: Boolean read fZebraEnabled write SetZebraEnabled;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Moje komponente', [TdpDBGrid]);
end;

{ TdpDBGrid }

constructor TdpDBGrid.Create(AOwner: TComponent);
begin
  inherited;
  fZebraColorLight := $00F0F0F0;
  fZebraColorDark := $00E0E0E0;
  fZebraEnabled := False;
end;

procedure TdpDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  fOddItem: Boolean;
begin
  inherited;
  fOddItem := DataSource.DataSet.RecNo mod 2 <> 0;
  if gdSelected in State then
  begin
    Canvas.Brush.Color := clHighlight;
    Canvas.Font.Color := clHighlightText;
  end
  else
  begin
    if fZebraEnabled then
    begin
      if fOddItem then
        Canvas.Brush.Color := fZebraColorDark
      else
        Canvas.Brush.Color := fZebraColorLight;
      DefaultDrawColumnCell(Rect, DataCol, Column, State);
    end;
  end;
end;

function TdpDBGrid.GetZebraColorDark: TColor;
begin
  Result := fZebraColorDark;
end;

function TdpDBGrid.GetZebraColorLight: TColor;
begin
  Result := fZebraColorLight;
end;

procedure TdpDBGrid.SetZebraColorDark(Value: TColor);
begin
  if Value <> fZebraColorDark then
  begin
    fZebraColorDark := Value;
    Invalidate;
  end;
end;

procedure TdpDBGrid.SetZebraColorLight(Value: TColor);
begin
  if Value <> fZebraColorLight then
  begin
    fZebraColorLight := Value;
    Invalidate;
  end;
end;

procedure TdpDBGrid.SetZebraEnabled(Value: Boolean);
begin
  if Value <> fZebraEnabled then
  begin
    fZebraEnabled := Value;
    Invalidate;
  end;
end;

end.
 

[ komplikator @ 04.02.2011. 18:02 ] @
Hvala, uspio sam pročistiti i ispraviti kod. Naletio sam na bug koji me mučio i mučio (senilnost, skroz sam zaboravio na taj "feature"). Dešavalo mi se da u designtime mijenjam svojstvo showZebra i sve radi, no runtime ne radi, osim ako svojstvo runtime postavim kroz kod. Na kraju sam se sjetio da se property ne pamti u dfm-u. ako je po defaultu true, nego se za defaulte boolean property postavlja false, a kao true ga postavim kroz konstruktor. Evo, vidim da si to isto napravio i ti Rambo. Sad idem korak dalje pa ću napraviti klasu/komponentu sa color shemama za razne situacije i razne custom komponente poput ove (lookup polja jedne boje, read-only druge itd, itd) i povezati je s ovom komponentom. Naravno, moram joj dodati još funkcionalnosti. Nije ni čudo, prije samo 3 dana sam se vratio u Clipper ispraviti neke stvari u prastarim programima, u međuvremenu odradio nešto u php-u.
[ Milos D @ 04.02.2011. 18:45 ] @
Možeš da još pojednostaviš, ne treba ti GetZebraColorLight (i druga dva get___) ako treba samo da vrati fZebraColorLight stavi

property ... read fZebraColorLight write ...
[ Boris B. @ 05.02.2011. 00:06 ] @
I lepo bi bilo da se doda default za boje, tako ce property inspector znati da prikaze default boju, i boje ce se pravilno serijalizovati u DFM:
Code (pascal):

    property ZebraColorLight: TColor read GetZebraColorLight write SetZebraColorLight; default $00F0F0F0;
    property ZebraColorDark: TColor read GetZebraColorDark write SetZebraColorDark; default $00E0E0E0;
    property ZebraEnabled: Boolean read fZebraEnabled write SetZebraEnabled; default false;