一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

『Delphi園地』

 容心居 2020-03-22
                         看了以上這么多的技巧和方法,想必大家未免會(huì)有一種沖動(dòng)吧-自己動(dòng)手做一個(gè)DBGrid,下面就介紹一種自制DBGrid的方法啦。

    Delphi中的TDBGrid是一個(gè)使用頻率很高的VCL元件。TDBGrid有許多優(yōu)良的特性,例如它是數(shù)據(jù)綁定的,能夠定義功能強(qiáng)大的永久字段,事件豐富等,特別是使用非常簡(jiǎn)單。但是,與FoxPro、VB 、PB中的DBGrid相比就會(huì)發(fā)現(xiàn),TDBGrid也有明顯的缺陷:它的鍵盤操作方式非常怪異難用。雖然很多人都通過(guò)編程把回車鍵轉(zhuǎn)換成Tab鍵來(lái)改進(jìn)TDBGrid的輸入方式,但是仍然不能很好地解決問(wèn)題,這是為什么呢?本文將對(duì)造成這種缺陷的根本原因進(jìn)行分析,并在此基礎(chǔ)上制作一個(gè)輸入極其簡(jiǎn)便、界面風(fēng)格類似Excel的DBGridPro元件。

     DBGrid的格子(Cell)有四種狀態(tài):輸入狀態(tài)(有輸入光標(biāo),可以輸入,記作狀態(tài)A1);下拉狀態(tài)(彈出了下拉列表,可以選擇,記作狀態(tài)A2);高亮度狀態(tài)(沒(méi)有輸入光標(biāo),可以輸入,記作狀態(tài)B);顯示狀態(tài)(不能輸入,記作狀態(tài)C)。DBGrid接受的控制鍵有回車,Tab,Esc,以及方向鍵。據(jù)此可以畫(huà)出每個(gè)Cell的狀態(tài)轉(zhuǎn)換圖:

    不難看出,當(dāng)用戶移動(dòng)輸入焦點(diǎn)時(shí),對(duì)不同的移動(dòng)方向要用不同的操作方法,甚至可能必須使用多個(gè)不同的鍵或借助鼠標(biāo)來(lái)完成一個(gè)操作。當(dāng)有下拉列表和要斜向移動(dòng)的時(shí)候這種問(wèn)題尤為明顯。因此,輸入困難的根本原因是其狀態(tài)圖過(guò)于復(fù)雜和不一致。基于這種認(rèn)識(shí),我們可以對(duì)DBGrid作三點(diǎn)改造:

    改造1:顯然B狀態(tài)是毫無(wú)意義的,應(yīng)該去掉。這意味著焦點(diǎn)每進(jìn)入一個(gè)新的Cell,就立即進(jìn)入編輯狀態(tài),而不要再按回車了。每個(gè)進(jìn)入狀態(tài)B的Cell都需要重新繪制,因此我們可以在繪制動(dòng)作中判斷是否有狀態(tài)為gdFocused的Cell,若有則設(shè)置EditorMode為真。值得注意的是,TDBGrid用來(lái)畫(huà)Cell的函數(shù)DefaultDrawColumnCell并不是虛函數(shù),因此不能通過(guò)繼承改變其行為,而只能使用其提供的事件OnDrawColumnCell來(lái)插入一些動(dòng)作。在DBGridPro中,這一點(diǎn)是通過(guò)實(shí)現(xiàn)顯示事件OnDrawColumnCell來(lái)實(shí)現(xiàn)的。但是這樣一來(lái),外部對(duì)象就不能使用該事件了,所以提供了一個(gè)OnOwnDrawColumnCell事件來(lái)替代它。見(jiàn)代碼中的Create和DefaultDrawColumnCell函數(shù)。

    改造2:控制鍵應(yīng)該簡(jiǎn)化,盡量增加每個(gè)控制鍵的能力。在DBGridPro中,強(qiáng)化了方向鍵和回車鍵的功能:當(dāng)光標(biāo)在行末行首位置時(shí),按方向鍵就能跳格;回車能橫向移動(dòng)輸入焦點(diǎn),并且還能彈出下拉列表(見(jiàn)改造3)。在實(shí)現(xiàn)方法上,可以利用鍵盤事件API(keybd_event)來(lái)將控制鍵轉(zhuǎn)換成TDBGrid的控制鍵(如在編輯狀態(tài)中回車,則取消該事件并重新發(fā)出一個(gè)Tab鍵事件)。當(dāng)監(jiān)測(cè)到左右方向鍵時(shí),通過(guò)向編輯框發(fā)送EM_CHARFROMPOS消息判斷編輯框中的光標(biāo)位置,以決定是否應(yīng)該跳格。見(jiàn)代碼中的DoKeyUped函數(shù)。

    改造3:簡(jiǎn)化下拉類型Cell的輸入方式。在DBGridPro中,用戶可以用回車來(lái)彈出下拉列表。這種方式看起來(lái)可能會(huì)造成的回車功能的混淆,但是只要處理得當(dāng),用戶會(huì)覺(jué)得非常方便:當(dāng)進(jìn)入下拉類型的Cell之后,如果用戶直接鍵入修改,則按回車進(jìn)入下一格;否則彈出下拉列表,選擇之后再按回車時(shí)關(guān)閉下拉列表并立即進(jìn)入下一格。見(jiàn)代碼中的DoKeyUped函數(shù)和DefaultDrawColumnCell函數(shù)。

    一番改造之后,用戶輸入已經(jīng)非常方便了,但是又帶來(lái)了新的問(wèn)題:在TDBGrid中,用戶可以通過(guò)高亮度的Cell很快知道焦點(diǎn)在哪里,而DBGridPro中根本不會(huì)出現(xiàn)這種Cell,所以用戶可能很難發(fā)現(xiàn)輸入焦點(diǎn)!一種理想的方法是像Excel一樣在焦點(diǎn)位置處放一個(gè)黑框--這一點(diǎn)是可以實(shí)現(xiàn)的(如圖2)。

    Windows中提供了一組API,用于在窗口上建立可接受鼠標(biāo)點(diǎn)擊事件的區(qū)域(Region)。多個(gè)Region可以以不同的方式組合起來(lái),從而得到"異型"窗口,包括空心窗口。DBGridPro就利用了這個(gè)功能。它在內(nèi)部建立了一個(gè)黑色的Panel,然后在上面設(shè)置空心的Region,并把它"套"在有輸入焦點(diǎn)的Cell上,這樣用戶就能看到一個(gè)醒目的邊框了。

    好事多磨,現(xiàn)在又出現(xiàn)了新的問(wèn)題:當(dāng)Column位置或?qū)挾雀淖儠r(shí),其邊框必須同步變化。僅利用鼠標(biāo)事件顯然不能完全解決這個(gè)問(wèn)題,因?yàn)樵诔绦蛑幸部梢栽O(shè)置Column的寬度;用事件OnDrawColumnCell也不能解決(寬度改變時(shí)并不觸發(fā)該事件)。幸運(yùn)的是,TDBGrid中的輸入框?qū)嶋H上是一個(gè)浮動(dòng)在它上面的TDBGridInplaceEdit(繼承自TInplaceEdit),如果我們能監(jiān)測(cè)到TDBGridInplaceEdit在什么時(shí)候改變大小和位置,就可以讓邊框也跟著改變了。要實(shí)現(xiàn)這一點(diǎn),用一個(gè)從TDBGridInplaceEdit繼承的、處理了WM_WINDOWPOSCHANGED消息的子類來(lái)替換原來(lái)的TDBGridInplaceEdit將是最簡(jiǎn)單的辦法。通過(guò)查看源代碼發(fā)現(xiàn),輸入框由CreateEditor函數(shù)創(chuàng)建的,而這是個(gè)虛函數(shù)--這表明TDBGrid愿意讓子類來(lái)創(chuàng)建輸入框,只要它是從TInplaceEdit類型的。從設(shè)計(jì)模式的角度來(lái)看,這種設(shè)計(jì)方法被稱為"工廠方法"(Factory Method),它使一個(gè)類的實(shí)例化延遲到其子類??磥?lái)現(xiàn)在我們的目的就要達(dá)到了。

    不幸的是,TDBGridInplaceEdit在DBGrids.pas中定義在implement中(這樣外部文件就無(wú)法看到其定義了),因此除非把它的代碼全部拷貝一遍,或者直接修改DBGrids.pas文件(顯然這前者不可取;后者又會(huì)帶來(lái)版本兼容性問(wèn)題),我們是不能從TDBGridInplaceEdit繼承的。難道就沒(méi)有好辦法了嗎?當(dāng)然還有:我們可以利用TDBGridInplaceEdit的可讀寫(xiě)屬性WindowProc來(lái)捕獲WM_WINDOWPOSCHANGED消息。WindowProc實(shí)際上是一個(gè)函數(shù)指針,它指向的函數(shù)用來(lái)處理發(fā)到該窗口元件的所有消息。于是,我們可以在CreateEditor中將創(chuàng)建出的TDBGridInplaceEdit的WndProc替換成我們自己實(shí)現(xiàn)的勾掛函數(shù)的指針,從而實(shí)現(xiàn)和類繼承相同的功能。這樣做的缺點(diǎn)是破壞了類的封裝性,因?yàn)槲覀儾坏貌辉贒BGridPro中處理屬于TDBGridInplaceEdit的工作。當(dāng)然,可能還有其他更好的方法,歡迎讀者提出建議。

    至此,TDBGrid已經(jīng)被改造成一個(gè)操作方便、界面美觀的DBGridPro了,我們可以把它注冊(cè)成VCL元件使用。以下是它的源代碼:


unit DBGridPro;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Grids, DBGrids, ExtCtrls, richEdit, DBCtrls, DB;

type TCurCell = Record {當(dāng)前焦點(diǎn)Cell的位置}
  X : integer; {有焦點(diǎn)Cell的ColumnIndex}
  Y : integer; {有焦點(diǎn)Cell所在的紀(jì)錄的紀(jì)錄號(hào)}
  tag : integer; {最近進(jìn)入該Cell后是否彈出了下拉列表}
  r : TRect; {沒(méi)有使用}
end;

type
  TDBGridPro = class(tcustomdbgrid)
  private
    hr,hc1 : HWND; {創(chuàng)建空心區(qū)域的Region Handle}
    FPan : TPanel; {顯示黑框用的Panel}
    hInplaceEditorWndProc : TWndMethod; {編輯框原來(lái)的WindowProc}
    {勾掛到編輯框的WindowProc}
    procedure InPlaceEditorWndProcHook(var msg : TMessage);
    procedure AddBox; {顯示邊框}
    {實(shí)現(xiàn)TCustomDBGrid的OnDrawColumnCell事件}
    procedure DoOwnDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
    {處理鍵盤事件}
    procedure DoKeyUped(Sender: TObject; var Key: Word; Shift: TShiftState);

  protected
    curCell : TCurCell; {記錄當(dāng)前有焦點(diǎn)的Cell}
    FOwnDraw : boolean; {代替TCustomDBGrid.DefaultDrawing}
    FOnDraw : TDrawColumnCellEvent; {代替TCustomDBGrid.OnDrawColumnCell}
    function CreateEditor : TInplaceEdit; override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure DefaultDrawColumnCell(const Rect: TRect;DataCol: Integer; Column: TColumn; State: TGridDrawState); overload;

  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;

  published
    property Align;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property Color;
    property Columns stored False; //StoreColumns;
    property Constraints;
    property Ctl3D;
    property DataSource;
    property OwnDraw : boolean read FOwnDraw write FOwnDraw default false;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property FixedColor;
    property Font;
    property ImeMode;
    property ImeName;
    property Options;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property TitleFont;
    property Visible;
    property OnCellClick;
    property OnColEnter;
    property OnColExit;
    property OnColumnMoved;
    property OnDrawDataCell; { obsolete }
    property OnOwnDrawColumnCell : TDrawColumnCellEvent read FOnDraw write FOnDraw;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEditButtonClick;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyup;
    property OnKeyPress;
    property OnKeyDown;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
    property OnTitleClick;
end;

procedure Register;

implementation

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

{ TDBGridPro }
procedure TDBGridPro.AddBox;
var
  p,p1 : TRect;
begin
  GetWindowRect(InPlaceEditor.Handle,p);
  GetWindowRect(FPan.Handle,p1);
  if (p.Left=p1.Left) and (p.Top=p1.Top) and (p.Right=p1.Right) and (p.Bottom=p1.Bottom) then exit;
  if hr<>0 then DeleteObject(hr);
  if hc1<>0 then DeleteObject(hc1);
 {創(chuàng)建內(nèi)外兩個(gè)Region}
  hr := CreateRectRgn(0,0,p.Right-p.Left+4,p.Bottom-p.Top+4);
  hc1:= CreateRectRgn(2,2,p.Right-p.Left+2,p.Bottom-p.Top+2);
  {組合成空心Region}
  CombineRgn(hr,hc1,hr,RGN_XOR);
  SetWindowRgn(FPan.Handle,hr,true);
  FPan.Parent := InPlaceEditor.Parent;
  FPan.ParentWindow := InPlaceEditor.ParentWindow;
  FPan.Height := InPlaceEditor.Height+4;
  FPan.Left := InPlaceEditor.Left-2;
  FPan.Top :=InPlaceEditor.Top-2;
  FPan.Width := InPlaceEditor.Width+4;
  FPan.BringToFront;
end;

constructor TDBGridPro.Create(AOwner: TComponent);
begin
  inherited;
  {創(chuàng)建作為邊框的Panel}
  FPan := TPanel.Create(nil);
  FPan.Parent := Self;
  FPan.Height := 0;
  FPan.Color := 0;
  FPan.Ctl3D := false;
  FPan.BevelInner := bvNone;
  FPan.BevelOuter := bvNone;
  FPan.Visible := true;
  DefaultDrawing := false;
  OnDrawColumnCell := DoOwnDrawColumnCell;
  OnOwnDrawColumnCell := nil;
  curCell.X := -1;
  curCell.Y := -1;
  curCell.tag := 0;
  hr := 0;
  hc1 := 0;
end;

function TDBGridPro.CreateEditor: TInplaceEdit;
begin
  result := inherited CreateEditor;
  hInPlaceEditorWndProc := result.WindowProc;
  result.WindowProc := InPlaceEditorWndProcHook;
end;

procedure TDBGridPro.DefaultDrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  {如果要畫(huà)焦點(diǎn),就讓DBGrid進(jìn)入編輯狀態(tài)}
  if (gdFocused in State) then
  begin
    EditorMode := true;
    AddBox;
    {如果是進(jìn)入一個(gè)新的Cell,全選其中的字符}
    if (curCell.X <> DataCol) or (curCell.Y <> DataSource.DataSet.RecNo)
    then begin
      curCell.X := DataCol;
      curCell.Y := DataSource.DataSet.RecNo;
      curCell.tag := 0;
      GetWindowRect(InPlaceEditor.Handle,curCell.r);
      SendMessage(InPlaceEditor.Handle,EM_SETSEL,0,1000);
    end;
    end else {正常顯示狀態(tài)的Cell}
  TCustomDBGrid(Self).DefaultDrawColumnCell(Rect,DataCol,Column,State);
  end;

destructor TDBGridPro.Destroy;
begin
  FPan.Free;
  inherited;
end;

procedure TDBGridPro.DoKeyUped(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  cl : TColumn;
begin
  cl := Columns[SelectedIndex];
  case Key of
    VK_RETURN:
    begin
    {一個(gè)Column為下拉類型,如果:
      1 該Column的按鈕類型為自動(dòng)類型
      2 該Column的PickList非空,或者其對(duì)應(yīng)的字段是lookup類型}
    if (cl.ButtonStyle=cbsAuto) and ((cl.PickList.Count>0) or (cl.Field.FieldKind=fkLookup)) and (curCell.tag = 0) and not (ssShift in Shift) then
    begin
    {把回車轉(zhuǎn)換成Alt+向下彈出下拉列表}
      Key := 0;
      Shift := [ ];
      keybd_event(VK_MENU,0,0,0);
      keybd_event(VK_DOWN,0,0,0);
      keybd_event(VK_DOWN,0,KEYEVENTF_KEYUP,0);
      keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);
      curCell.tag := 1;
      exit;
    end;
    {否則轉(zhuǎn)換成Tab}
    Key := 0;
    keybd_event(VK_TAB,0,0,0);
    keybd_event(VK_TAB,0,KEYEVENTF_KEYUP,0);
  end;
  VK_RIGHT :
  begin
  {獲得編輯框中的文字長(zhǎng)度}
  i := GetWindowTextLength(InPlaceEditor.Handle);
  {獲得編輯框中的光標(biāo)位置}
  GetCaretPos(p);
  p.x := p.X + p.Y shr 16;
  j := SendMessage(InPlaceEditor.Handle,EM_CHARFROMPOS,0,p.X);
  if (i=j) then {行末位置}
    begin
      Key := 0;
      keybd_event(VK_TAB,0,0,0);
      keybd_event(VK_TAB,0,KEYEVENTF_KEYUP,0);
    end;
  end;
  VK_LEFT:
  begin
    GetCaretPos(p);
    p.x := p.X + p.Y shr 16;
    if SendMessage(InPlaceEditor.Handle,EM_CHARFROMPOS,0,p.X)=0 then
    begin {行首位置}
      Key := 0;
      keybd_event(VK_SHIFT,0,0,0);
      keybd_event(VK_TAB,0,0,0);
      keybd_event(VK_TAB,0,KEYEVENTF_KEYUP,0);
      keybd_event(VK_SHIFT,0,KEYEVENTF_KEYUP,0);
    end;
  end;
  else begin {記錄用戶是否作了修改}
    if (Columns[SelectedIndex].PickList.Count>0) and (curCell.tag = 0) then
      if SendMessage(InPlaceEditor.Handle,EM_GETMODIFY,0,0)=1 then
        curCell.tag := 1;
    end;
  end;
end;

procedure TDBGridPro.DoOwnDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  if FOwnDraw=false then DefaultDrawColumnCell(Rect,DataCol,Column,State);
  if @OnOwnDrawColumnCell<>nil then OnOwnDrawColumnCell(Sender,Rect,DataCol, Column,State);
end;

procedure TDBGridPro.InPlaceEditorWndProcHook(var msg: TMessage);
var m : integer;
begin
  m := msg.Msg;
  {=inherited}
  hInplaceEditorWndProc(msg);
  {如果是改變位置和大小,重新加框}
  if m=WM_WINDOWPOSCHANGED then AddBox;
end;

procedure TDBGridPro.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
  DoKeyUped(Self,Key,Shift);
end;

end.

{以上代碼在Windows2000,Delphi6上測(cè)試通過(guò)}

(出處:www.delphibbs.com)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲专区中文字幕视频| 久久香蕉综合网精品视频| 欧美一级特黄大片做受大屁股| 日本午夜免费福利视频| 国产精品欧美在线观看| 好吊日视频这里都是精品| 五月天丁香婷婷一区二区| 久热99中文字幕视频在线| 都市激情小说在线一区二区三区| 五月婷婷综合激情啪啪| 99精品国产自在现线观看| 亚洲精品一二三区不卡| 精品一区二区三区不卡少妇av| 视频在线观看色一区二区| 久热香蕉精品视频在线播放| 日本人妻熟女一区二区三区| 亚洲国产精品一区二区毛片| 男女午夜在线免费观看视频| 九九热精品视频在线观看| 午夜小视频成人免费看| 亚洲午夜精品视频在线| 97精品人妻一区二区三区麻豆| 欧美国产日产综合精品| 黄色av尤物白丝在线播放网址| 日韩不卡一区二区视频| 色婷婷在线精品国自产拍| 欧美综合色婷婷欧美激情| 欧美日韩亚洲综合国产人| 日韩欧美综合在线播放| 东京不热免费观看日本| 福利在线午夜绝顶三级| 色婷婷视频在线精品免费观看 | 国产一区二区三区草莓av| 欧美国产日韩在线综合| 国产成人精品一区二区三区| 欧美一区二区口爆吞精| 视频一区日韩经典中文字幕| 日韩人妻精品免费一区二区三区| 91在线国内在线中文字幕| 日韩精品中文字幕在线视频| 丁香六月啪啪激情综合区|