4-7. Master & Detail 예제
상속용 부모폼에는 DataSet이 하나밖에 없기때문에 Master/Detail 구조에 사용하려면 몇 가지 수정작업과 추가 코드가 필요하다. Master DataSet이 스크롤 될 때 서버로부터 Detail 정보를 가져오게 처리할 수도 있지만 데이터 건수가 한번에 가져오지 못할 정도가 아니라면 처리 속도나 서버 부하면에서 좋은 선택은 아닌것 같다. 본 예제에서는 전체 자료를 클라이언트에 모두 가져와서 Filter로 처리하게 만들었다. 만약 조회조건이 있는 경우라면 쓸데 없는 자료를 가져오지 않게 Master의 조건에 맞는 Detail 자료를 가져오게 SQL문을 구성해야한다. Master/Detail로 만들어야할 경우가 많다면 본 예제를 참고해서 Master/Detail용 부모폼을 만들어 상속하면 코딩 양을 상당히 줄일 수 있을 것이다.
unit code_frm;
:
:
const
DETAIL_TABLENAME = 'slminorm';
:
:
type
TfrmCode = class(TfrmSIMLazQ)
dbgMaster: TDBGrid;
dbgDetail: TDBGrid;
dsMaster: TDataSource;
dsDetail: TDataSource;
qrDetail: TBufDataset;
qrDetailMAJOR_CODE: TStringField;
qrDetailMINOR_CODE: TStringField;
qrDetailMINOR_NAME: TStringField;
qrDetailREMARK: TStringField;
qrDetailUSE_YN: TStringField;
qrMasterMAJOR_CODE: TStringField;
qrMasterMAJOR_NAME: TStringField;
qrMasterREMARK: TStringField;
qrMasterUSE_YN: TStringField;
procedure dbgDetailEnter(Sender: TObject);
procedure dbgMasterEnter(Sender: TObject);
procedure dbgMasterExit(Sender: TObject);
procedure dbgMasterKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure dsMasterStateChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure qrDetailAfterDelete(DataSet: TDataSet);
procedure qrDetailAfterInsert(DataSet: TDataSet);
procedure qrDetailBeforeDelete(DataSet: TDataSet);
procedure qrDetailBeforeEdit(DataSet: TDataSet);
procedure qrDetailBeforeInsert(DataSet: TDataSet);
procedure qrDetailBeforePost(DataSet: TDataSet);
procedure qrMasterAfterInsert(DataSet: TDataSet);
procedure qrMasterAfterOpen(DataSet: TDataSet);
procedure qrMasterMAJOR_CODESetText(Sender: TField; const aText: string);
private
u_DSIndex: Int32;
public
function UF_Modified: Boolean; override;
procedure UP_SetSQL; override;
procedure UP_OpenSQL; override;
procedure UP_Append; override;
procedure UP_Delete; override;
procedure UP_Save; override;
:
:
procedure TfrmCode.dbgDetailEnter(Sender: TObject);
begin
u_DSIndex := 2;
dbgMaster.TitleFont.Color := clSilver;
dbgDetail.TitleFont.Color := clBlack;
Application.MainForm.Tag := 1;
end;
procedure TfrmCode.dbgMasterEnter(Sender: TObject);
begin
u_DSIndex := 1;
dbgMaster.TitleFont.Color := clBlack;
dbgDetail.TitleFont.Color := clSilver;
Application.MainForm.Tag := 1;
end;
procedure TfrmCode.dbgMasterExit(Sender: TObject);
begin
Application.MainForm.Tag := 0;
end;
procedure TfrmCode.dbgMasterKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_RETURN then // uses LCLType
begin
with TDBGrid(Sender) do
begin
if (not (SelectedColumn.ButtonStyle in [cbsCheckboxColumn, cbsButtonColumn])) and (not SelectedColumn.ReadOnly) and (not EditorMode) then Exit; // uses Grids
if Shift = [] then
begin
if SelectedIndex < (Columns.Count - 1) then SelectedIndex := SelectedIndex + 1
else
begin
SelectedIndex := 0;
DataSource.DataSet.Next;
if DataSource.DataSet.Eof then DataSource.DataSet.Append;
end;
end
else if ssShift in Shift then
begin
if SelectedIndex > 0 then SelectedIndex := SelectedIndex - 1
else
begin
SelectedIndex := Columns.Count - 1;
DataSource.DataSet.Prior;
end;
end;
end;
end;
end;
procedure TfrmCode.dsMasterStateChange(Sender: TObject);
begin
qrMasterMAJOR_CODE.ReadOnly := not (qrMaster.State in [dsInsert]);
end;
procedure TfrmCode.FormCreate(Sender: TObject);
begin
inherited;
u_DSIndex := 1;
end;
procedure TfrmCode.FormShow(Sender: TObject);
begin
u_TableName := 'slmajorm';
UP_OpenSQL;
end;
procedure TfrmCode.qrDetailAfterDelete(DataSet: TDataSet);
begin
if (u_SQL <> '') and DataSet.Active then qrDetail.Tag := dmSIMLaz.UF_SIMLazExec(u_SQL);
end;
procedure TfrmCode.qrDetailAfterInsert(DataSet: TDataSet);
begin
with DataSet do
begin
FieldByName('major_code').AsString := qrMasterMAJOR_CODE.AsString;
FieldByName('use_yn').AsString := '1';
end;
dbgDetail.SelectedIndex := 0;
end;
procedure TfrmCode.qrDetailBeforeDelete(DataSet: TDataSet);
begin
if (not u_auth_d) and DataSet.Active then
begin
MessageDlg(Self.Caption, '삭제 권한이 없습니다!', mtError, [mbOK], 0);
Abort;
end
else
begin
if DataSet.State in [dsInsert] then u_SQL := ''
else u_SQL := dmSIMLaz.UF_SIMLazDML(qrDetail, DETAIL_TABLENAME, ukDelete);
end;
end;
procedure TfrmCode.qrDetailBeforeEdit(DataSet: TDataSet);
begin
if (not u_auth_m) and DataSet.Active then
begin
MessageDlg(Self.Caption, '수정 권한이 없습니다!', mtError, [mbOK], 0);
Abort;
end;
if DataSet.RecordCount < 1 then DataSet.Append;
end;
procedure TfrmCode.qrDetailBeforeInsert(DataSet: TDataSet);
begin
if (not u_auth_n) and DataSet.Active then
begin
MessageDlg(Self.Caption, '등록 권한이 없습니다!', mtError, [mbOK], 0);
Abort;
end
else if u_downkey_flag then Abort;
end;
procedure TfrmCode.qrDetailBeforePost(DataSet: TDataSet);
begin
u_SQL := '';
if DataSet.State in [dsInsert] then u_SQL := dmSIMLaz.UF_SIMLazDML(qrDetail, DETAIL_TABLENAME, ukInsert)
else u_SQL := dmSIMLaz.UF_SIMLazDML(qrDetail, DETAIL_TABLENAME, ukModify);
end;
procedure TfrmCode.qrMasterAfterInsert(DataSet: TDataSet);
begin
DataSet.FieldByName('use_yn').AsString := '1';
dbgMaster.SelectedIndex := 0;
end;
procedure TfrmCode.qrMasterAfterOpen(DataSet: TDataSet);
begin
if not DataSet.ControlsDisabled then
qrDetail.Filter := 'major_code = ' + QuotedStr(DataSet.FieldByName('major_code').AsString);
end;
procedure TfrmCode.qrMasterMAJOR_CODESetText(Sender: TField; const aText: string);
begin
TField(Sender).AsString := UpperCase(aText);
end;
function TfrmCode.UF_Modified: Boolean;
begin
Result := (qrMaster.Active and (qrMaster.State in [dsEdit, dsInsert])) or
(qrDetail.Active and (qrDetail.State in [dsEdit, dsInsert]));
end;
procedure TfrmCode.UP_SetSQL;
begin
u_SQL := 'SELECT major_code, major_name, use_yn, remark FROM slmajorm ORDER BY major_code';
end;
procedure TfrmCode.UP_OpenSQL;
begin
if qrDetail.Active then qrDetail.Close;
inherited UP_OpenSQL;
dbgMaster.SetFocus;
u_SQL := 'SELECT major_code, minor_code, minor_name, use_yn, remark FROM slminorm ORDER BY major_code, minor_code';
dmSIMLaz.UP_SIMLazOpen(qrDetail, u_SQL, False);
end;
procedure TfrmCode.UP_Append;
begin
if not u_auth_n then Exit;
case u_DSIndex of
1: if qrMaster.Active and (not qrMaster.ReadOnly) then qrMaster.Append;
2: if qrDetail.Active and (not qrDetail.ReadOnly) then qrDetail.Append;
end;
end;
procedure TfrmCode.UP_Delete;
begin
if not u_auth_d then Exit;
if ((u_DSIndex = 1) and ((not qrMaster.Active) or (qrMaster.RecordCount < 1) or qrMaster.ReadOnly)) or
((u_DSIndex = 2) and ((not qrDetail.Active) or (qrDetail.RecordCount < 1) or qrDetail.ReadOnly)) then Exit;
if MessageDlg(Self.Caption, '선택된 자료를 삭제 하시겠습니까?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
case u_DSIndex of
1: qrMaster.Delete;
2: qrDetail.Delete;
end;
end;
end;
procedure TfrmCode.UP_Save;
var
l_Flag: Boolean;
begin
if not (u_auth_n or u_auth_m or u_auth_d) then Exit;
l_Flag := False;
if qrDetail.Active and (not qrDetail.ReadOnly) and (qrDetail.State in [dsEdit, dsInsert]) then
begin
qrDetail.Post;
l_Flag := True;
end;
if qrMaster.Active and (not qrMaster.ReadOnly) and (qrMaster.State in [dsEdit, dsInsert]) then
begin
qrMaster.Post;
l_Flag := True;
end;
if l_Flag then ShowMessage('변경된 내용이 저장 되었습니다!');
end;