用控件聚合技術爲FlexGrid增添PickList功能

 摘要:本文主要介紹了一種通過控件聚合技術來爲網格控件FlexGrid增添PickList功能的方法。

  前言

  在對數據庫進行數據錄入時,PickList功能能極大的方便用戶,在dELPHI程序中的DBgrid控件就直接提供了PickList功能,但VB和VC下的DBgrid和MSFlexGrid卻沒有提供相應的功能,FlexGrid甚至連直接編輯網格的能力都沒有提供。經過筆者的探索,在VC++下通過標準控件和ActiveX控件相聚合的技術成功地爲FlexGrid控件增加了編輯和PickList的能力,使之成爲真正功能強大的網格控件。

  方案設計

  由於FlexGrid控件不允許對網格單元進行編輯,因此如果要在其上實現PickList功能就首先要有一組合框來與其配合共同實現該功能:在鼠標或光標(即當前焦點)移到FlexGrid的某個網格時,通過事件響應來激發組合框的顯示,並且要求組合框在位置、大小等方面與FlexGrid的當前網格完全重合。顯示的組合框的當前值爲其所在網格原先的取值,在用戶通過鼠標完成對內容的選擇後再將用戶的選擇結果輸送回網格。而一旦該網格不再需要編輯(即失去焦點)時組合框應當隱藏。從整個設計過程來看,FlexGrid並不是直接同用戶進行交互的,而是通過組合框的中介來完成的。FlexGrid只負責數據的顯示和組織,PickList功能是由動態顯示的組合框來完成。用戶絲毫不會意識到兩個控件的存在,唯一的感覺只會是FlexGrid控件增添了PickList功能。

  控件聚合的過程中有兩個地方是比較關鍵的,也是程序實現的重點:一個是當鼠標點擊網格單元時,能通過事件的觸發在網格上覆蓋一個與其等大的組合框,並完成網格數據向組合框的傳送;另一點是當用戶完成對組合框內容的選擇後能將組合框從當前位置隱藏,並將用戶選擇的結果返回與其對應的網格單元。

  編碼實現

  在進行程序編碼之前首先要通過添加組件和控件的方式向程序中添加FlexGrid控件,Visual Studio 提供有此控件--Microsoft FlexGrid Control, version 6.0。由於在程序中需要準確地將組合框同網格控件的單元格相聚合,因此手工拖動FlexGrid到對話框是無法同組合框準確定位的,因此需要通過SetWindowPos()函數在像素級上動態設定網格控件在對話框中的具體位置:

//獲取客戶區矩形
CRect rcBounds;
GetClientRect(&rcBounds);
//設定FlexGrid控件在對話框中的位置和大小
m_ctlMSFlexGrid.SetWindowPos(&wndTop,rcBounds.left,rcBounds.top,rcBounds.Width(),
   rcBounds.Height(),NULL);

  用來接受用戶輸入的組合框控件在程序運行之初,如果用戶進行輸入操作,是不顯示的,只有在用戶發出鼠標單擊的消息需要進行編輯時纔將其顯示,因此需要將組合框控件的初始屬性設爲不可見。通過前面的方案設計可以看出,整個操作的全過程無非是對鼠標和焦點的消息響應和處理。通過FlexGrid控件響應鼠標單擊的事件可以得知用戶是否進行編輯操作,並在捕獲到該操作後,通過對消息的響應完成用戶輸入信息從組合框到網格單元的複製:

//對FlexGrid控件的單擊響應
BEGIN_EVENTSINK_MAP(CFlexGridDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CFlexGridDlg)
ON_EVENT(CFlexGridDlg, IDC_MSFLEXGRID, -600 /* Click */, OnClickMsflexgrid, VTS_NONE)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
……
//鼠標單擊消息的響應處理
//計算被選中的單元格的座標,以便組合框能恰好與之吻合
long lRow = m_ctlMSFlexGrid.GetRowSel();
long lCol = m_ctlMSFlexGrid.GetColSel();
……
// MSFlexGrid 控件的函數的長度單位是"緹(twips)",
//需要將其轉化爲像素,1440 緹 = 1 英寸
int nTwipsPerDotX = 1440 / pDC->GetDeviceCaps(LOGPIXELSX) ;
int nTwipsPerDotY = 1440 / pDC->GetDeviceCaps(LOGPIXELSY) ;
……
//將緹轉換爲像素
long lCellWidth = m_ctlMSFlexGrid.GetCellWidth()/ nTwipsPerDotX;
long lCellHeight = m_ctlMSFlexGrid.GetCellHeight()/ nTwipsPerDotY;
long lCellLeft = m_ctlMSFlexGrid.GetCellLeft() / nTwipsPerDotX;
long lCellTop = m_ctlMSFlexGrid.GetCellTop() / nTwipsPerDotY;

  在完成對網格單元的座標計算及轉換後,需要動態調整組合框的座標位置及大小,使其能與當前網格單元相配,同時把焦點由當前的網格單元轉移到組合框上,從而實現兩控件的聚合:

//設置組合框座標
m_ctlPickList.SetWindowPos(&wndTop,lCellLeft,lCellTop,lCellWidth,lCellHeight,NULL);
//將焦點轉移到組合框
m_ctlPickList.SetFocus();
//將改動完畢的組合框變爲可視
m_ctlPickList.ShowWindow(SW_SHOW);

  如果此時的網格原先已有內容,此時亦應當將內容轉移到組合框中,在用戶修改後再作回存處理:

CString sData = m_ctlMSFlexGrid.GetText(); //獲取原先的內容
if(sData=="緊急") //網格內容爲"緊急"
m_ctlPickList.SetCurSel(0); //設置組合框顯示第一項內容"緊急"
else if(sData=="一般") //網格內容爲"一般"
m_ctlPickList.SetCurSel(1); //設置組合框顯示第二項內容"一般"
else //網格內容爲"暫緩"
m_ctlPickList.SetCurSel(2); //設置組合框顯示第三項內容"暫緩"

  此時只能接收用戶的輸入,當對下一單元進行編輯時輸入過的數據並沒有保存到網格中,因此在組合框控件失去焦點時應完成當把當前用戶修改過的內容回存到單元格的處理。具體來說,是對組合框ON_KILLFOCUS消息的響應:

int i=m_ctlPickList.GetCurSel(); //根據組合框的當前選項決定回存的內容
if(i==0)
sInputData="緊急";
if(i==1)
sInputData="一般";
if(i==2)
sInputData="暫緩";
……
m_ctlMSFlexGrid.SetText(sInputData);//將用戶的選擇寫入網格控件
m_ctlPickList.ShowWindow(SW_HIDE); //隱藏組合框

  通過以上幾步編碼基本上實現了對FlexGrid控件和組合框控件的聚合,並通過這種聚合技術成功地爲FlexGrid控件增添了PickList功能,不僅方便了用戶的使用,也使人機界面更爲友好。下圖就是一個使用了本文所述控件聚合技術實現的具有PickList能力的程序界面,從界面上和使用效果看根本感覺不出是兩種各自相互獨立組件的作用結果,同Delphi提供的DBGrid控件在效果上很相似:


  小結

  本文所述控件聚合方法具有很好的通用性和可擴展性,不僅在本文可以實現PickList功能,也可以通過類似的方法來實現Windows標準控件同自己或是第三方開發的ActiveX控件的聚合,來拓展原有控件的功能。甚至還可將聚合後的控件編寫爲一個新的Active X控件作爲一個完整的控件以備頻繁使用。綜上所述,利用控件聚合技術可以在較小編碼量的前提下大大擴展基本控件的功能,有着很好的應用前景。本文所述程序在Windows 98下,由Microsoft Visual C++ 6.0編譯通過。

發佈了3 篇原創文章 · 獲贊 29 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章