unit X.ChildForm;
 
// for y&f 2009-06-30
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StrUtils, RTTI, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Grids, X.AppEditions;
 
type
  { TxForm }
 
  TxChildFormClass = class of TxChildForm;
 
  TxDosMoveOptions = set of (moEnter, moUpDn);
  /// 
  ///   窗体穿透事件, 当不同窗体互相调用bpl, 时, 可以传入参数进行互相调用
  /// 
 
  TxShowQueryEvent = procedure(Sender: TObject; var CanShow: Boolean) of object;
 
  TxChildForm = class(TForm)
  private
    { Private declarations }
    FAsChild: Boolean;
    FTempParent: TWinControl;
    FEditionType: TxAppEditionType;
    FDosMove: boolean;
    FDosMoveOptions: TxDosMoveOptions;
    FOnShowQuery: TxShowQueryEvent;
    FLastWasEdit: Boolean;
    FIsTabGrid: Boolean;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure Loaded; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    procedure DowMoveKeyDown(var Key: Word; Shift: TShiftState);
    procedure DowMoveKeyPress(var Key: char);
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); overload; override;
    constructor Create(AOwner: TComponent; AParent: TWinControl); reintroduce; overload;
    /// 
    ///   如果有窗体调用包, 窗体打开状态也会调用此过程.
    /// 
    function ShowQuery: Boolean; virtual;
  public
    /// 
    ///   显示对话框
    /// 
    class function ShowModalEx(): Integer; overload;
    /// 
    ///   显示对话框
    /// 
    ///
    ///   回调函数,当模式对话框关闭前,调用此函数.
    ///
    ///  demo1:
    ///  var
    ///    Callback: TProc;
    ///  begin
    ///     callback := procedure(ADlg: TzxForm; mr: TModalResult)
    ///                begin
    ///                  showmessage('dialog close callback proc');
    ///                end;
    ///
    ///     Tdlg.ShowModalEx(callback);
    ///  end;
    ///
    ///  =======================================================================
    ///
    ///  demo2:
    ///  begin
    ///     Tdlg.ShowModalEx(
    ///                 procedure(ADlg: TzxForm; mr: TModalResult)
    ///                 begin
    ///                   showmessage('dialog close callback proc');
    ///                 end);
    ///  end;
    ///
    /// 
    class function ShowModalEx(ACallback: TProc): Integer; overload;
 
    /// 
    ///   显示对话框
    /// 
    class function ExecuteEx(): Boolean; overload;
    /// 
    ///   显示对话框
    /// 
    ///   回调函数,当模式对话框关闭前,调用此函数.
    /// 
    class function ExecuteEx(ACallback: TProc): Boolean; overload;
  published
    /// 
    ///   当前模块可应用的最低版本类别,
    /// 
    property EditionType: TxAppEditionType read FEditionType write FEditionType;
    property DosMove: boolean read FDosMove write FDosMove default false;
    property DosMoveOptions: TxDosMoveOptions read FDosMoveOptions write FDosMoveOptions;
    /// 窗体互调事件
    ///   窗体互调过程, 默认是在打开bpl是执行,
    ///   如果有窗体调用包, 窗体打开状态也会调用此过程.
    ///
    property OnShowQuery: TxShowQueryEvent read FOnShowQuery write FOnShowQuery;
  published
    //这几个属性, 很重要
    property IsControl;
    property OnActivate;
    property OnDeactivate;
    property OnHide;
  end;
 
implementation
 
{ TxChildForm }
 
constructor TxChildForm.Create(AOwner: TComponent);
begin
  //***begin 这段不可以更改**************************
  FAsChild := False; //这句必须在inherited的前
  FDosMoveOptions := [moEnter, moUpDn];
  inherited Create(AOwner); //这句必须使用inherited.
 
end;
 
constructor TxChildForm.Create(AOwner: TComponent; AParent: TWinControl);
begin
  //***begin 这段不可以更改**************************
  FAsChild := True; //这个必须在inherited的前
  FTempParent := AParent; //这个必须在inherited前
  FDosMoveOptions := [moEnter, moUpDn];
  inherited Create(AOwner); //这个必须使用inherited
 
end;
 
procedure TxChildForm.Loaded;
begin
  inherited;
  if FAsChild then
  begin
    Align := alClient;
    BorderStyle := bsNone;
    BorderIcons := [];
    Parent := FTempParent;
    Position := poDefault;
    //BoundsRect:=FTempParent.BoundsRect;
    BoundsRect := FTempParent.ClientRect;
  end;
end;
 
procedure TxChildForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if FAsChild then
    Params.Style := Params.Style or WS_CHILD;
end;
 
procedure TxChildForm.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
  sc_DragMove = $F012;
begin
  inherited;
 
  ReleaseCapture;
  Perform(wm_SysCommand, sc_DragMove, 0);
end;
 
class function TxChildForm.ShowModalEx(): Integer;
begin
  Result := ShowModalEx(nil);
end;
 
class function TxChildForm.ShowModalEx(ACallback: TProc): Integer;
var
  LContext: TRttiContext;
  LClass: TRttiInstanceType;
  LForm: TxChildForm;
begin
  LContext := TRttiContext.Create;
  try
    LClass := LContext.FindType(Self.QualifiedClassName) as TRttiInstanceType;
    if LClass <> nil then
    begin
      LForm := LClass.MetaclassType.Create as TxChildForm;
      LForm.Create(Application.MainForm);
      try
        Result := LForm.ShowModal;
 
        //回调函数
        if Assigned(ACallback) then
          ACallback(LForm, Result);
      finally
        LForm.Destroy;
        LForm := nil;
      end;
    end;
  finally
    LContext.Free;
  end;
end;
 
class function TxChildForm.ExecuteEx(): Boolean;
begin
  Result := ExecuteEx(nil);
end;
 
class function TxChildForm.ExecuteEx(ACallback: TProc): Boolean;
begin
  Result := (ShowModalEx(ACallback) = mrOK);
end;
 
procedure TxChildForm.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
 
  DowMoveKeyDown(Key, Shift);
end;
 
procedure TxChildForm.KeyPress(var Key: Char);
begin
  inherited;
 
  DowMoveKeyPress(Key);
end;
 
procedure TxChildForm.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
 
end;
 
function TxChildForm.ShowQuery: Boolean;
begin
  Result := True;
  if Assigned(FOnShowQuery) then
    FOnShowQuery(Self, Result);
end;
 
procedure TxChildForm.DowMoveKeyDown(var Key: Word; Shift: TShiftState);
begin
  if FDosMove then
  begin
    // true if last active control is TCustomEdit and above
    FLastWasEdit := Self.ActiveControl is TCustomEdit;
 
    if (FDosMoveOptions <> []) then
    begin
      if Self.ActiveControl is TCustomGrid then
      begin
        if (Key = VK_RETURN) then
        begin
          Key := VK_TAB;
          FIsTabGrid := True;
        end;
      end
      else if Self.ActiveControl is TCustomMemo then //2006-02-25 Add by Qwang for TCustomMemo
      begin
        with (Self.ActiveControl as TCustomMemo) do
          if (Key = VK_UP) and (moUpDn in FDosMoveOptions) and (SelStart <
            Length(Lines.Strings[0])) then  //光标在第一列时按VK_UP键
            Self.Perform(WM_NEXTDLGCTL, 1, 0)
          else if (Key = VK_DOWN) and (moUpDn in FDosMoveOptions) and (SelStart
            > (Length(Text) - Length(Lines.Strings[Lines.Count - 1]))) then //光标在最后一列时按VK_DOWN键
            Self.Perform(WM_NEXTDLGCTL, 0, 0);
      end
      else
      begin
        // Handle the specials keys
        if ((Key = VK_DOWN) and (moUpDn in FDosMoveOptions)) or ((Key =
          VK_RETURN) and (moEnter in FDosMoveOptions)) then
          Self.Perform(WM_NEXTDLGCTL, 0, 0)
        else if (Key = VK_UP) and (moUpDn in FDosMoveOptions) then
          Self.Perform(WM_NEXTDLGCTL, 1, 0);
      end;
    end; // if Options<>[] ...
 
  end; // if FActive ...
 
end;
 
procedure TxChildForm.DowMoveKeyPress(var Key: char);
begin
  // Handle 'Enter' key that makes Edits beep
  if FDosMove then
  begin
    if FIsTabGrid then
    begin
      FIsTabGrid := False;
      Key := #0;
    end;
  end; // if FActive ...
end;
 
end.
 
 
unit X.ChildFormDisplay;
 
// create for y&f 2009-06-30
 
interface
 
uses
  Windows, Classes, Forms, SysUtils, Messages, Controls, ExtCtrls, ComCtrls,
  Generics.Collections, Dialogs, X.ChildForm;
 
type
  TxChildFormDisplayChangeEvent = procedure(Sender: TObject; NewForm: TxChildForm; var Cancel: Boolean) of object;
 
  { TxChildFormDisplay }
 
  TxChildFormDisplay = class(TPanel)
  private
    FList: TList;
    FActiveFormIndex: Integer;
    FOnChange: TNotifyEvent;
    FOnChanging: TxChildFormDisplayChangeEvent;
    FTabControl: TTabControl;
    procedure SetActiveForm(aValue: TxChildForm);
    procedure SetActiveFormIndex(aValue: Integer);
    function GetFormCount: Integer;
    function GetActiveForm: TxChildForm;
    function GetForms(Index: Integer): TxChildForm;
    function GetFormIndex(Index: TxChildForm): Integer;
    procedure SetTabControl(const Value: TTabControl);
    procedure WMSetFocus(var Message: TWMSetFocus); message WM_SetFocus;
    procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;
  protected
    //
    procedure Resize; override;
    //TabControl新的OnChange事件
    procedure TabControlChange(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    //加载窗体
    function AddForm(aValue: TxChildForm; aDisplay: Boolean; aCaption: string = ''): Integer;
    function AddFormClass(aValue: TxChildFormClass; aDisplay: Boolean; aCaption: string = ''): Integer;
    function FindNextForm(CurForm: TxChildForm; GoForward: Boolean): TxChildForm;
    procedure HideForm(aValue: TxChildForm);
    procedure MoveForm(aValue: TxChildForm; NewIndex: Integer);
    procedure RemoveForm(aValue: TxChildForm; FreeForm: Boolean);
    procedure RemoveFormsAll(FreeForm: boolean);
    procedure ActiveNextForm(GoForward: Boolean);
    function ActiveFormByName(aValue: string): Boolean;
  public
    property ActiveFormIndex: Integer read FActiveFormIndex write SetActiveFormIndex;
    property ActiveForm: TxChildForm read GetActiveForm write SetActiveForm;
    property FormCount: Integer read GetFormCount;
    property Forms[index: Integer]: TxChildForm read GetForms;
    property FormIndex[index: TxChildForm]: Integer read GetFormIndex;
  published
    property TabControl: TTabControl read FTabControl write SetTabControl;
    property OnChanging: TxChildFormDisplayChangeEvent read FOnChanging write FOnChanging;
   // new introduced properties
    property Align;
    property BevelInner;
    property BevelKind;
    property BevelOuter;
    property BevelWidth;
    property Ctl3D;
    property Caption;
    property Font;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property ShowHint;
    property TabOrder;
  end;
 
implementation
 
{ TxChildFormDisplay }
 
procedure TxChildFormDisplay.SetActiveForm(aValue: TxChildForm);
var
  tmp: Integer;
begin
  tmp := FList.IndexOf(aValue);
  if tmp <> -1 then
    SetActiveFormIndex(tmp);
end;
 
procedure TxChildFormDisplay.SetActiveFormIndex(aValue: Integer);
var
  tmp: TxChildForm;
  cancel: Boolean;
  pf: TCustomForm;
begin
  if aValue <> FActiveFormIndex then
  begin
    pf := GetParentForm(Self);
      // hide current page
    cancel := false;
    if Assigned(FOnChanging) then
      FOnChanging(self, Forms[aValue], cancel);
    if cancel then
      exit;
 
    if FActiveFormIndex <> -1 then
    begin
      if Assigned(pf) and Assigned(pf.Menu) and Assigned(TxChildForm(Forms[FActiveFormIndex]).Menu) then
        pf.Menu.Unmerge(TxChildForm(Forms[FActiveFormIndex]).Menu);
      HideForm(Forms[FActiveFormIndex]);
    end;
 
    FActiveFormIndex := aValue;
    if FActiveFormIndex = -1 then
      exit;
 
    // show new page
    tmp := Forms[FActiveFormIndex];
    if Assigned(pf) and Assigned(pf.Menu) and Assigned(TForm(tmp).Menu) then
      pf.Menu.Merge(TForm(tmp).Menu);
 
    if not tmp.HandleAllocated then
      tmp.HandleNeeded;
    try
      SetWindowLong(tmp.Handle, GWL_STYLE, WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN);
      SetWindowLong(tmp.Handle, GWL_EXSTYLE, 0);
      Windows.SetParent(tmp.Handle, Handle);
 
      // positionieren
      tmp.Align := alClient;
      tmp.BringToFront;
    finally
      tmp.Visible := True;
      tmp.SetFocus;
      if Assigned(TxChildForm(tmp).OnActivate) then
        TxChildForm(tmp).OnActivate(tmp); //JH, July 2001
      if Assigned(FOnChange) then
        FOnChange(self);
    end;
 
    if Assigned(FTabControl) then
    begin
      FTabControl.TabIndex := FActiveFormIndex;
      if Assigned(FTabControl.OnChange) then
        FTabControl.OnChange(FTabControl);
    end;
  end;
end;
 
procedure TxChildFormDisplay.SetTabControl(const Value: TTabControl);
begin
  FTabControl := Value;
  if Assigned(FTabControl) then
  begin
    FTabControl.OnChange := Self.TabControlChange;
  end;
end;
 
function TxChildFormDisplay.GetFormCount: Integer;
begin
  Result := FList.Count;
end;
 
function TxChildFormDisplay.GetForms(Index: Integer): TxChildForm;
begin
  Result := nil;
  if Index < FList.Count then
    Result := TxChildForm(FList[index]);
end;
 
function TxChildFormDisplay.GetFormIndex(Index: TxChildForm): Integer;
begin
  Result := FList.IndexOf(Index);
end;
 
function TxChildFormDisplay.GetActiveForm: TxChildForm;
begin
  Result := nil;
  if (FActiveFormIndex <> -1) then
    Result := TxChildForm(FList[FActiveFormIndex]);
end;
 
procedure TxChildFormDisplay.WMSetFocus(var Message: TWMSetFocus);
begin
  inherited;
  if TabStop and (ActiveForm <> nil) then
    if ActiveForm.ActiveControl <> nil then
      ActiveForm.SetFocus;
end;
 
procedure TxChildFormDisplay.CMDialogKey(var Message: TCMDialogKey);
begin
  if (Message.CharCode = VK_TAB) and (GetKeyState(VK_CONTROL) < 0) then
  begin
    ActiveNextForm(GetKeyState(VK_SHIFT) >= 0);
    Message.Result := 1;
  end
  else
    inherited;
end;
 
procedure TxChildFormDisplay.Resize;
begin
  inherited Resize;
  if csDesigning in ComponentState then
    exit;
  if (FList.Count > 0) and Assigned(ActiveForm) then
    ReAlign;
end;
 
constructor TxChildFormDisplay.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);
  FList := TList.Create;
  FActiveFormIndex := -1;
  FTabControl := nil;
end;
 
destructor TxChildFormDisplay.Destroy;
begin
  FList.Free;
  FActiveFormIndex := -1;
  inherited Destroy;
end;
 
function TxChildFormDisplay.AddForm(aValue: TxChildForm; aDisplay: Boolean; aCaption: string): Integer;
begin
  Result := FList.Add(aValue);
  if Result <> -1 then
  begin
    if aCaption <> '' then
      aValue.Caption := aCaption;
 
    // prepare Form for use in TxChildFormDisplay
    aValue.Parent := Self;
    aValue.Visible := False;
    //TxChildForm(aValue).IsControl := True;
 
    if Assigned(FTabControl) then
      FTabControl.Tabs.Add(aValue.Caption);
 
    //边框等属性
    aValue.BorderStyle := bsNone;
    aValue.BorderIcons := [];
    aValue.Position := poDefault;
    aValue.TabStop := False;
    aValue.Align := alClient;
 
    if aDisplay then
      SetActiveFormIndex(Result);
  end;
end;
 
function TxChildFormDisplay.AddFormClass(aValue: TxChildFormClass; aDisplay: Boolean; aCaption: string): Integer;
begin
  Result := AddForm(aValue.Create(self), aDisplay);
end;
 
function TxChildFormDisplay.FindNextForm(CurForm: TxChildForm; GoForward: Boolean): TxChildForm;
var
  index: integer;
begin
  Result := nil;
 
  Index := FList.IndexOf(CurForm);
  if (CurForm = nil) or (Index = -1) then
    exit;
 
  if GoForward then
  begin
    Inc(Index);
    while (Index < FList.Count) do
      if Assigned(FList[Index]) then
      begin
        Result := TxChildForm(FList[Index]);
        Exit;
      end
      else
        Inc(Index)
  end
  else
  begin
    Dec(Index);
    while (Index >= 0) do
      if Assigned(FList[Index]) then
      begin
        Result := TxChildForm(FList[Index]);
        Exit;
      end
      else
        Dec(index);
  end;
end;
 
procedure TxChildFormDisplay.HideForm(aValue: TxChildForm);
var
  index: Integer;
begin
  index := FList.IndexOf(aValue);
  if FActiveFormIndex <> index then
    exit;
  if Assigned(TxChildForm(aValue).OnDeactivate) then
    TxChildForm(aValue).OnDeactivate(aValue); //JH July 2001
  if Assigned(TxChildForm(aValue).OnHide) then
    TxChildForm(aValue).OnHide(aValue); //JH July 2001
 
  aValue.Align := alNone;
  aValue.Visible := False;
  FActiveFormIndex := -1;
end;
 
procedure TxChildFormDisplay.MoveForm(aValue: TxChildForm; NewIndex: Integer);
var
  index, oldActiveIndex: Integer;
begin
  index := FList.IndexOf(aValue);
  if index = -1 then
    exit;
  oldActiveIndex := FActiveFormIndex;
  if FActiveFormIndex <> -1 then
    HideForm(ActiveForm);
  FList.Move(index, NewIndex);
  if Assigned(FTabControl) then
    FTabControl.Tabs.Move(index, NewIndex);
  SetActiveFormIndex(oldActiveIndex);
end;
 
procedure TxChildFormDisplay.TabControlChange(Sender: TObject);
begin
  if Assigned(FTabControl) then
  begin
    if FTabControl.TabIndex <> FActiveFormIndex then
    begin
      FTabControl.TabIndex := FActiveFormIndex;
      SetActiveFormIndex(FTabControl.TabIndex);
    end;
  end;
end;
 
procedure TxChildFormDisplay.RemoveForm(aValue: TxChildForm; FreeForm: Boolean);
var
  index: Integer;
begin
  index := FList.IndexOf(aValue);
  // 5.0, user report
  if index = -1 then
    exit;
 
  if FActiveFormIndex = index then
    HideForm(aValue);
 
  if Assigned(FTabControl) then
    FTabControl.Tabs.Delete(Index);
 
  FList.Remove(aValue);
  if index >= FList.Count then
    index := Pred(FList.Count);
  SetActiveFormIndex(index);
 
  if FreeForm then
  begin
    aValue.Free;
    aValue := nil;
  end;
end;
 
procedure TxChildFormDisplay.RemoveFormsAll(FreeForm: boolean);
var
  i: Integer;
begin
  for i := FList.Count - 1 downto 0 do
    RemoveForm(TxChildForm(FList.Items[i]), FreeForm);
end;
 
procedure TxChildFormDisplay.ActiveNextForm(GoForward: Boolean);
begin
  SetActiveForm(FindNextForm(ActiveForm, GoForward));
end;
 
function TxChildFormDisplay.ActiveFormByName(aValue: string): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 0 to Pred(FList.Count) do
  begin
    if TxChildForm(FList[i]).Caption = aValue then
    begin
      SetActiveFormIndex(i);
      Result := true;
      break;
    end
  end;
end;
 
end.