디비그리드의 같은 값을 가진 셀병합(칼럼머지) 구현 예제
간만에 서울 올라갔다 내려오는길에 버스안에서 별로 할 짓도 없구해서 이것저것 생각하다가 문득 생각이 나길래 함 만들어 본 것입니다. 팁란에 올리려다 아래에 있는 임포스터 클래스 강좌와 같이 보는 게 나을 거 같아 여기에 올립니다.
아래에, 임포스터 클래스에 대해서 주정섭씨가 좋은 글을 써 주셨습니다.
주정섭님의 글에 대해서 김재철님이 좋은 댓글을 올려 주셨는데, 어차피 같은 내용으로 보여지므로 상황에 따라 편리한 방법을 선택하면 될 거 같습니다.
개발을 하다보면, DBGrid 콤포넌트의 기능이 약해서, 제3자 Grid 콤포넌트를 많이 사용하게 된다. 그런데, 그 덩치큰 삼자 Grid 콤포넌트의 전체 기능이 필요한 것이 아니고, 한두가지 기능만 필요한 경우가 많다.
더우기, 퀀텀그리드 같은 경우는 전체 레코드를 메모리에 로딩하기 때문에, 필요한 한두가지 기능때문에 쓰기는 참 사치스럽기까지 하다. 속도향상을 위해 메모리테이블을 써야 되는 경우도 있는데, 퀀텀에서 또 내부적으로 메모리테이블을 사용하므로 이런 경우 이중으로 메모리를 낭비하는 셈이 된다.
물론, 요즘 일반적 컴 사양에서 별무리가 아니라 하더라도, 한두가지 기능때문에 이런 식으로 퀀텀을 쓰기에는 소 잡는 칼로 닭 잡는 수가 될 수도 있다는 얘기다.
이 예제는, 인접하는 칼럼의 값이 같을 경우에, 이를 Merge하는 기능을 가진 그리드를 임포스터 방식으로 만든 것이다. 퀀텀 그리드의 경우 칼럼 머지 기능이라고 하는 것 같다. 혹은 그룹칼럼 머지 기능이라고도 하는 것 같다. 어떻게 동작하는지는 첨부한 예제를 실행해보면 알것이다. 예제를 실행하면, 이전 이후 레코드의 Category필드의 값이 같은 경우, 이를 병합해서 표시할 것이다.
주의) 컴파일시 kbmMemTable이 필요합니다.
kbmMemTable 주소: www.components4programmers.com
썩 좋은 예제는 아니지만 필요한 분들에게 참고가 되길 바랍니다.
이하 소스...
// 가져다 쓰는 폼의 소스
unit mainf;
interface
uses
..., dbgridext;
type
TDbGrid = class(TGroupDbGrid);
TForm1 = class(TForm)
Grid: TDBGrid;
....
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
// 그룹으로 보야줄 필드명
Grid.GroupFieldName:= 'Category';
// 라인은 깜박임을 줄이기 위해 내부에서 직접 그려준다.
// 오브젝트 인스펙터에서 설정해도 됨
// 임포스터 클래스에서 설정하면 오브젝트인스펙터 설정값이 우선함
Grid.Options:= Grid.Options - [dgColLines, dgRowLines];
end;
end.
// 그루핑디비그리드 임포스터 클래스
// 내부적으로 메모리 테이블을 사용한다.
unit dbgridext;
interface
uses
Windows, Messages, Classes, SysUtils, Types, Variants, Graphics, Db, Grids, DBGrids, KBMMemTable;
type
TGroupDbGrid = class(TDbGrid)
private
FPaintCol,
FPaintRow: Integer;
FGroupFieldName: String;
FDrawTable: TKbmMemtable;
procedure WMMouseWheel(var Message: TMessage); message WM_MOUSEWHEEL;
protected
procedure DrawCell(ACol, ARow: Integer; ARect: TRect;
AState: TGridDrawState); override;
procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
Column: TColumn; State: TGridDrawState); override;
procedure Scroll(Distance: Integer); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property PaintCol: Integer read FPaintCol;
property PaintRow: Integer read FPaintRow;
property GroupFieldName: string read FGroupFieldName write FGroupFieldName;
end;
implementation
constructor TGroupDbGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
inherited DefaultDrawing := False;
FPaintCol:= -1;
FPaintRow:= -1;
FDrawTable := TkbmMemTable.Create(Self);
with FDrawTable do
begin
FieldDefs.Add('Col', ftInteger);
FieldDefs.Add('Row', ftInteger);
FieldDefs.Add('Text', ftString, 30);
CreateTable;
Active := True;
end;
end;
destructor TGroupDbGrid.Destroy;
begin
FDrawTable.Free;
FDrawTable:= nil;
inherited Destroy;
end;
procedure TGroupDbGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);
begin
FPaintCol:= ACol;
FPaintRow:= ARow;
inherited DrawCell(ACol, ARow, ARect, AState);
if (gdFixed in AState) then
begin
DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOMRIGHT);
DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_TOPLEFT);
end;
end;
procedure TGroupDbGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;
Column: TColumn; State: TGridDrawState);
var
R: TRect;
i: Integer;
b: Boolean;
X, Y: Integer;
begin
if (gdFixed in State) then
Exit;
R:= Rect;
if AnsiCompareText(Column.FieldName, FGroupFieldName) <> 0 then
begin
x:= 2; y:= 2;
Canvas.TextRect(R, R.Left + x, R.Top + y, Column.Field.DisplayText);
R.Bottom:= R.Bottom - 1; R.Right:= R.Right - 1;
Canvas.Pen.Color:= clSilver;
Canvas.MoveTo(R.Right, R.Top);
Canvas.LineTo(R.Right, R.Bottom);
Canvas.LineTo(R.Left, R.Bottom);
Exit;
end;
Canvas.Brush.Color := Color;
Canvas.Font.Color := Column.Font.Color;
Canvas.FillRect(R);
with FDrawTable do
if Locate('Col;Row', VarArrayOf([PaintCol, PaintRow]), []) then
begin
Edit;
FieldByName('Text').AsString:= Column.Field.AsString;
Post;
end
else
begin
Insert;
FieldByName('Col').AsInteger:= PaintCol;
FieldByName('Row').AsInteger:= PaintRow;
FieldByName('Text').AsString:= Column.Field.AsString;
Post;
end;
i:= 0;
b:= True;
if PaintRow > 0 then
while b do begin
Inc(i);
b:= FDrawTable.Locate('Col;Row', VarArrayOf([PaintCol, PaintRow - i]), []) and
(Column.Field.AsString = FDrawTable.FieldByName('Text').AsString);
end;
R.Top:= R.Top - ((R.Bottom - R.Top) * (i - 1));
x:= R.Left + 2; y:= R.Top + ((R.Bottom - R.Top - Canvas.TextHeight('W')) div 2);
Canvas.TextRect(R, x, y, Column.Field.DisplayText);
R.Bottom:= R.Bottom - 1; R.Right:= R.Right - 1;
Canvas.Pen.Color:= clSilver;
Canvas.MoveTo(R.Right, R.Top);
Canvas.LineTo(R.Right, R.Bottom);
Canvas.LineTo(R.Left, R.Bottom);
end;
procedure TGroupDbGrid.Scroll(Distance: Integer);
begin
inherited;
Invalidate;
end;
procedure TGroupDbGrid.WMMouseWheel(var Message: TMessage);
var
i: SmallInt;
begin
i := HiWord(Message.wParam);
if i > 0 then
Perform(WM_KeyDown, VK_UP, 0)
else
Perform(WM_KeyDown, VK_DOWN, 0);
end;
end.
|