4-2. 메인 화면 만들기 메인 화면의 메뉴는 TMainMenu가 아닌 TTreeView를 사용한 Tree 형태의 메뉴를 사용하고, Popup이 아닌 폼에서 공통으로 사용할 조회/등록/삭제/저장 버튼을 상단에 배치한다. 하단에는 상태바에 현재 날짜와 접속한 사용자명을 보여준다. 나머지 부분은 공백이 아니라 "TabSheet"가 없는 "TPageControl"이다. 처음 시도는 MDI 형태로 만들려고 했었는데 라자루스에서 아직 윈도우즈용 MDI를 지원하지 않는 관계로 PageControl을 이용한 Docking 폼 형태로 만들었다. 물론 컴퍼넌트를 설치하면 MDI 형태를 사용할 수도 있지만 왠만하면 추가 컴퍼넌트 수를 늘리지않는게 나중을 생각하면 좋다. TTreeView의 Node는 Caption 외에 데이터 처리를 위한 속성이 없기때문에 각 Node의 Data에 필요한 정보를 담은 Pointer를 할당해서 사용해야한다. 우리가 필요한건 Caption과 메뉴ID, 프로그램ID이기때문에 메뉴ID와 프로그램ID를 레코드 Pointer로 만들어서 사용한다.
    unit main_frm;
    :
    :
    type
      // TreeMenu Information
      PSIMLazNodeInfo = ^SIMLazNodeInfo;
      SIMLazNodeInfo = record
        menu_id: string; // Menu ID
        prg_id: string; // Program ID
      end;

      TfrmMain = class(TForm)
      :
      :
      private
        procedure UP_Hint(Sender: TObject); // 메인폼 상태바에 힌트 표시하기
        procedure UP_CreatePRG(AID, ACaption: string); // 폼생성
상단에 있는 공통 버튼의 Enabled는 DataModule에 선언한 버튼상태 변수를 이용해서 활성화 또는 비활성화 시킨다. 폼 생성시 버전 정보를 Application의 Title에 추가하고 메인 화면의 Caption을 Application의 Title로 대체한다.
    procedure TfrmMain.FormActivate(Sender: TObject);
    begin
      with dmSIMLaz do
      begin
        btnOpen.Enabled := u_btn_v;
        btnAppend.Enabled := u_btn_n;
        btnDelete.Enabled := u_btn_d;
        btnSave.Enabled := u_btn_s;
      end;
    end;

    procedure TfrmMain.FormCreate(Sender: TObject);
    begin
      Application.Title := Application.Title + ' Version ' + GF_SIMLazFVer(ParamStr(0));
      Application.OnHint := @UP_Hint;
    end;

    procedure TfrmMain.UP_Hint(Sender: TObject);
    begin
      stbMain.Panels[2].Text := ' ' + Application.Hint;
    end;
닫기 버튼 클릭시 Docking 폼이 있으면 해당 폼을 닫고 아무것도 없을 때에는 프로그램을 종료 한다. 프로그램 종료확인은 메인폼의 CloseQuery에서 처리하기때문에 닫기 버튼에서는 하지않는다. 델파이의 경우 PageControl에 Docking 폼을 생성하면 TabSheet에서 Docking 폼의 정보를 확인할 수 있는데 윈도우즈용 라자루스에서는 그 정보를 확인 할 수가 없다. 라자루스가 델파이에 비해 불편하거나 구현이 안 된 부분이 많지만 충분히 피해갈 방법이 있다. Docking 폼의 정보를 알아내기위해 편법으로 Docking 시 폼의 이름을 TabSheet의 이름에 추가해서 해당 폼을 찾기위한 명칭으로 활용하면 된다.
    procedure TfrmMain.btnCloseClick(Sender: TObject);
    var
      l_Name: string;
      l_Form: TCustomForm;
    begin
      if pgcMain.PageCount = 0 then Close
      else
      begin
        l_Name := Copy(pgcMain.ActivePage.Name, 4, Length(pgcMain.ActivePage.Name));
        l_Form := Screen.FindForm(l_Name);
        if l_Form <> nil then l_Form.Hide;
        pgcMain.ActivePage.Free;
        if l_Form <> nil then l_Form.Close;

        if pgcMain.PageCount = 0 then
        begin
          btnOpen.Enabled := False;
          btnAppend.Enabled := False;
          btnDelete.Enabled := False;
          btnSave.Enabled := False;
          trvMenu.Visible := True;
        end;
      end;
    end;

    procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: boolean);
    begin
      if MessageDlg(Self.Caption,
        '프로그램을 종료하시겠습니까?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then
        CanClose := False;
    end;
참고로 Docking 폼을 닫기 전에 Hide 하지않으면 화면에 번쩍하고 나타났다가 사라지는 현상이 생긴다. Docking 폼 형태에서는 Enter Key로 Control간 이동 처리는 메인 폼에서 해야한다.
    procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    begin
      if (ssAlt in Shift) and (UpCase(Char(Key)) = 'X') then btnCloseClick(btnClose)
      else if Shift = [] then
      begin
        if Key in [VK_RETURN, VK_DOWN, VK_UP] then
        begin
          if Self.Tag = 1 then Exit;
          case Key of
            VK_RETURN, VK_DOWN: SelectNext(ActiveControl, True, True);
                         VK_UP: SelectNext(ActiveControl, False, True);
          end;
          Key := 0;
        end;
      end;
    end;
공통 버튼의 클릭처리는 약간의 꼼수로 Docking 폼의 Key Event와 연동해서 처리한다. 각 버튼별로 설정된 Key 값을 Docking된 폼이 있을 경우 현재 선택된 폼으로 Key Event를 보낸다. 예제에 사용한 TSpeedButton에는 Key 값을 담을만한 속성이 없기때문에 각 버튼의 Tag값을 활용한다. 예를 들면 VK_F2의 값은 113 이니까 Tag에 2를 넣고 111을 더하는 형태로 처리한다. 조회 버튼에 Key값을 Docking 폼에 보내는 처리를 범용적으로 처리한 후 다른 공용 버튼의 Click Event에 할당한다.
    procedure TfrmMain.btnOpenClick(Sender: TObject);
    var
      l_Key: Word;
      l_Name: string;
      l_Form: TCustomForm;
      l_ShiftState: TShiftState;
    begin
      if pgcMain.PageCount <= 0 then Exit;
      l_Key := 111 + TSpeedButton(Sender).Tag; // VK_F2, VK_F3, VK_F8, VK_F9
      l_ShiftState := [];
      l_Name := Copy(pgcMain.ActivePage.Name, 4, Length(pgcMain.ActivePage.Name));
      l_Form := Screen.FindForm(l_name);
      if l_Form <> nil then l_Form.OnKeyDown(l_Form, l_Key, l_ShiftState);
    end;