unit Dbpanel;

interface
uses
	Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs,
  	ExtCtrls, dbctrls, stdctrls, db, ADODB, ComCtrls;

type
   TEditorstyle = (TsDbMemo,TsDBcombox,Tsdbedit);

type
  TDBPanel = class(TPanel)
  private
    { Private declarations }
    FEditorstyle:Teditorstyle;
    FLeft: Integer;
    FTop: Integer;
    maxTextLen: Integer;
    maxLabelLen: Integer;
    FTitleVisible :Boolean;
    FScrollBox: TScrollBox; {滚动控件}
    FLineHeight: Integer;

    //数据数组控件，动态生成
    MemoEditors: array of TDBMemo;
    comEditors: array of TDBCombobox;
    edEditors: array of TDBedit;
    ProgressEditor :array of TDBedit;
    Labels: array of TDBText;  //字段标题，动态生成
    ProgressBars: array of TProgressBar;

    FDataSource: TDataSource; // 数据源
    FDataField_A: String;  // DataField
    FDataField_B: String;  // DataField
    FDataField_C: String;  // DataField
    FColumns: Integer; //显示列数
    procedure FreeEditors; //释放数据输入控件的内存
    procedure AKeyDown(Sender:TObject; var Key: Word; Shift:TShiftState);
    procedure AKeyPress(Sender:TObject; var Key: Char);
    procedure AProgressEditorChange(Sender: TObject);
    function comEditor(Index: Integer):TDBComboBox;
    function edEditor(Index: Integer):TDBedit;
    function MemoEditor(Index: Integer): TDBMemo;
  protected
    { Protected declarations }

  public
      constructor Create(AOwner:TComponent); override;
      destructor Destroy; override;
  		function Get_TitleVisible() :Boolean;
    	procedure Set_TitleVisible(Value : Boolean);
    	procedure Setedit(Value : TEditorstyle);
    	procedure CreateEditors(DS: TDataSource; ColCount: Integer);  //创建各字段的数据输入控件
    	procedure ClearHits(ItemIndex: Integer);
    	procedure AddHits(ItemIndex:Integer; Hits: array of string);

    { Public declarations } 
    published

    	property LimitLeft: Integer read FLeft write FLeft default 10;
    	property LimitTop: Integer read FTop write FTop default 10;
    	property Editorstyle : TEditorstyle read FEditorstyle write Setedit default tsdbMemo;
    	property EditorWidth: Integer read maxTextLen write maxTextLen  default 100;
    	property TitleWidth: Integer read maxLabelLen write maxLabelLen  default 100;
    	property TitleVisible : Boolean read Get_TitleVisible write Set_TitleVisible default True;
    	property LineHeight: Integer read FLineHeight write FLineHeight  default 15;
    	//property OnOkClick: TNotifyEvent read FClick write FClick;
    	property DataSource: TDataSource read FDataSource write FDataSource; //数据源
      property DataField_Editor: String read FDataField_A write FDataField_A;
      property DataField_Title: String read FDataField_B write FDataField_B;
      property DataField_Progress: String read FDataField_C write FDataField_C;
      property Columns: Integer read FColumns write FColumns default 4;//表列数

    { Published declarations }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Data Controls', [TDBPanel]);
end;

function TDBPanel.Get_TitleVisible() :Boolean;
begin
  	Result := FTitleVisible;
end;

procedure TDBPanel.Set_TitleVisible(Value : Boolean);
begin
	FTitleVisible := Value;
end;

procedure TDBPanel.Setedit(Value : TEditorstyle);
begin
  	if FEditorstyle <> Value then
   begin
   	FEditorstyle := Value;
   	Invalidate;
   end;
end;

{ 为第I字段增加提示信息的方法}
procedure TDBPanel.AddHits(ItemIndex:
Integer; Hits: array of string);
var
  m,n,i: Integer;
begin
	if FEditorstyle = TsDBcombox then
 	begin
   	n := Length(comEditors);
    	m := Length(Hits);
   	if ItemIndex< n then
      for i:=0 to m-1 do
        	comEditors[ItemIndex].Items.Add(Hits[i]);
	end
  	else if FEditorstyle = TsDBEdit then
  	begin
   	n := Length(edEditors);
    	m := Length(Hits);
   	if ItemIndex< n then
      for i:=0 to m-1 do
      	edEditors[ItemIndex].Hint:= Hits[i];
   end
  	else if FEditorstyle = TsDBMemo then
  	begin
   	n := Length(memoEditors);
    	m := Length(Hits);
   	if ItemIndex< n then
      for i:=0 to m-1 do
      	memoEditors[ItemIndex].Hint:= Hits[i];
   end;
end;

procedure TDBPanel.AKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
	if (Sender is TDBComboBox) then
   begin
    	case Key of
      	VK_Next: (Sender as TDBComboBox).DataSource.DataSet.Next;
       	VK_PRIOR: (Sender as TDBComboBox).DataSource.DataSet.Prior;
      end;
  	end
  	else if (Sender is TDBedit) then
   begin
   	case Key of
      	VK_Next: (Sender as TDBedit).DataSource.DataSet.Next;
       	VK_PRIOR: (Sender as TDBedit).DataSource.DataSet.Prior;
      end;
   end
  	else if (Sender is TDBMemo) then
   begin
   	case Key of
      	VK_Next: (Sender as TDBMemo).DataSource.DataSet.Next;
       	VK_PRIOR: (Sender as TDBMemo).DataSource.DataSet.Prior;
      end;
   end;
end;

procedure TDBPanel.AProgressEditorChange(Sender: TObject);
begin
     //
end;

procedure TDBPanel.AKeyPress(Sender: TObject; var Key: Char);
begin
   if (Sender is TDBComboBox) or (Sender is TDBedit) or (Sender is TDBMemo) then
   if Key=#13 then
   	(Owner as TForm).Perform(WM_NEXTDLGCTL, 0, 0);
end;

procedure TDBPanel.ClearHits(ItemIndex: Integer);
var
  n: Integer;
begin
 	if FEditorstyle = TsDBcombox then
  	begin
   	n := Length(comEditors);
   	if ItemIndex< n then comEditors[ItemIndex].Items.Clear;
   end
   else if FEditorstyle = TsDBEdit then
   begin
      n := Length(edEditors);
      if ItemIndex< n then edEditors[ItemIndex].Hint:='';;
   end
   else if FEditorstyle = TsDBMemo then
   begin
      n := Length(MemoEditors);
      if ItemIndex< n then MemoEditors[ItemIndex].Hint:='';;
   end;

end;

constructor TDBPanel.Create(AOwner: TComponent);
begin
  Inherited Create(AOWner);
  FLeft :=10;
  FTop := 10;
  maxTextLen := 100;
  maxLabelLen := 100;
  FLineHeight := 15;
end;

{ 创建各字段的数据输入控件的方法}
procedure TDBPanel.CreateEditors(DS: TDataSource; ColCount: Integer);
var
  i, n, RowCount: Integer;
  TextHeight: Integer;
begin
   if DataSource = nil then exit;
   if not DataSource.DataSet.Active then exit;
   n := DataSource.DataSet.RecordCount;
   if n > 0 then
  	begin
      DataSource.DataSet.DisableControls;
      if maxLabelLen < maxTextLen then
      	maxTextLen := maxLabelLen;
    	
    	{ 计算最大的标题长度及显示长度}
    	DataSource.DataSet.First;
    	{ 计算高度}
    	TextHeight := Canvas.TextHeight(DataSource.DataSet.Fields[0].DisplayLabel) + FLineHeight; //10;
    	{ 计算行列数}
      if (FColumns = 0) or (FColumns <> ColCount) then FColumns := ColCount;
    	RowCount := n div Columns;
    	if n mod Columns <> 0 then inc(RowCount);
    	{ 分配内存}
    	FreeEditors;
    	SetLength(Labels,n);
      SetLength(ProgressBars,n);
      SetLength(ProgressEditor,n);
    	if FEditorstyle = TsDBcombox then
    		SetLength(comEditors,n)
    	else if FEditorstyle = TsDBEdit then
    		SetLength(edEditors,n)
    	else
    		SetLength(MemoEditors,n);

    	{ 创建滚动盒}
    	FScrollBox := TScrollBox.Create(Owner);
      FScrollBox.Visible := False;
    	FScrollBox.Parent := Self;
    	FScrollBox.Align := alClient;
    	{ 创建编辑}
    	for i := 0 to n - 1 do
    	begin
      	{ 创建标题}
      	Labels[i] := TDBText.Create(Owner);
         Labels[i].visible := FTitleVisible;
      	Labels[i].Parent := FScrollBox;
      	Labels[i].DataField := DataField_Title;
         Labels[i].DataSource := DataSource;
      	Labels[i].Left := FLeft + (maxLabelLen + 36) * (i div RowCount)+16;  //+maxTextLen
         if FEditorstyle = TsDBMemo then
         begin
         	Labels[i].Width := maxLabelLen;
      		Labels[i].Top := FTop + (i mod RowCount) * (TextHeight*4+10) + 2;
         end
         else
         begin
         	Labels[i].Top := FTop + (i mod RowCount) * (TextHeight*2+10) + 2;
            Labels[i].Width := maxLabelLen;
         end;
         { 创建进度条数据对象}
      	ProgressEditor[i] := TDBedit.Create(Owner);
         ProgressEditor[i].visible := False;
      	ProgressEditor[i].Parent := FScrollBox;
         ProgressEditor[i].DataField := DataField_Progress;
         ProgressEditor[i].DataSource := DataSource;
         ProgressEditor[i].OnChange := AProgressEditorChange;
         
      	{ 创建信息显示数据对象}
	 		if FEditorstyle = TsDBcombox then
    		begin
      		comEditors[i] := TDBComboBox.Create(Owner);
      		comEditors[i].Parent := FScrollBox; //Self;
      		comEditors[i].Left := Labels[i].Left; //+ Labels[i].Width;
      		comEditors[i].Width := maxTextLen;
      		comEditors[i].Top := Labels[i].Top+20;
      		comEditors[i].DataSource := DataSource;
      		comEditors[i].DataField := DataField_Editor;
      		comEditors[i].OnKeyPress := AKeyPress;
      		comEditors[i].OnKeyDown := AKeyDown;
         	ProgressBars[i] := TProgressBar.Create(Owner);
         	ProgressBars[i].Parent := FScrollBox;
            ProgressBars[i].Orientation := pbHorizontal;
         	ProgressBars[i].Left := comEditors[i].Left;
         	ProgressBars[i].Width := comEditors[i].Width;
         	ProgressBars[i].Height := 10;
         	ProgressBars[i].Top := comEditors[i].Top+comEditors[i].height+2;
         	ProgressBars[i].Position := i*4
    		end
    		else if FEditorstyle = TsDBEdit then
    		begin
      		edEditors[i] := TDBedit.Create(Owner);
      		edEditors[i].Parent := FScrollBox;
      		edEditors[i].Left := Labels[i].Left; //+ Labels[i].Width;
      		edEditors[i].Width := maxTextLen;
      		edEditors[i].Top := Labels[i].Top+20;
      		edEditors[i].DataSource := DataSource;
      		edEditors[i].DataField := DataField_Editor;
      		edEditors[i].OnKeyPress := AKeyPress;
      		edEditors[i].OnKeyDown := AKeyDown;
         	ProgressBars[i] := TProgressBar.Create(Owner);
         	ProgressBars[i].Parent := FScrollBox;
            ProgressBars[i].Orientation := pbHorizontal;
         	ProgressBars[i].Left := edEditors[i].Left;
         	ProgressBars[i].Width := edEditors[i].Width;
         	ProgressBars[i].Height := 10;
         	ProgressBars[i].Top := edEditors[i].Top+edEditors[i].height+2;;
         	ProgressBars[i].Position := i*4
    		end
    		else
    		begin
      		MemoEditors[i] := TDBmemo.Create(Owner);
      		MemoEditors[i].Parent := FScrollBox;
      		MemoEditors[i].Left := Labels[i].Left;
      		MemoEditors[i].Width := maxTextLen;
      		MemoEditors[i].Top := Labels[i].Top+20;
      		MemoEditors[i].DataSource := DataSource;
      		MemoEditors[i].DataField := DataField_Editor;
      		MemoEditors[i].OnKeyPress := AKeyPress;
      		MemoEditors[i].OnKeyDown := AKeyDown;
         	ProgressBars[i] := TProgressBar.Create(Owner);
         	ProgressBars[i].Parent := FScrollBox;
            ProgressBars[i].Orientation := pbVertical;
         	ProgressBars[i].Left := MemoEditors[i].Left + MemoEditors[i].Width +1;
         	ProgressBars[i].Width := 10;
         	ProgressBars[i].Height := MemoEditors[i].Height;
         	ProgressBars[i].Top := MemoEditors[i].Top;
         	ProgressBars[i].Position := i*4
    		end;
         if not DataSource.DataSet.Eof then
         	DataSource.DataSet.next;
  		end;
      DataSource.DataSet.EnableControls;
  		FScrollBox.Visible := True;
  end;
end;

destructor TDBPanel.Destroy;
begin
  	FreeEditors;
  	Inherited Destroy;
end;

function TDBPanel.comEditor(Index: Integer): TDBComboBox;
begin
  	if Index< Length(comEditors) then Result := comEditors[Index]
  	else Result := nil;
end;

function TDBPanel.edEditor(Index: Integer): TDBedit;
begin
  	if Index < Length(edEditors) then Result := edEditors[Index]
  	else Result := nil;
end;
function TDBPanel.MemoEditor(Index: Integer): TDBMemo;
begin
  	if Index< Length(MemoEditors) then Result := MemoEditors[Index]
  	else Result := nil;
end;

// 内存的释放是要有顺序的！必须以创建的相反的顺序进行！尤其是当组件之间有父子关系时
procedure TDBPanel.FreeEditors;
begin
  	if FScrollBox <> nil then
   begin
   	comEditors := nil;
   	edEditors := nil;
   	MemoEditors := nil;
    	FScrollBox.Free;
    	FScrollBox := nil;
  	end;
end;

end.
 