[VC/MFC]MSFlexGrid 內嵌控件(附源代碼下載)
作者:茂葉 [轉貼] 瀏覽量: 4731 發表日期:2006-12-17 更新日期:2006-12-17
Key Words: MSFlexGrid 內嵌控件
http://www.maoyeah.com/display.asp?boardid=3&id=35
在VC中MSFlexGrid內嵌EDIT、COMBOBOX的實現方法
在CSDN網絡中經常會看到有人問起各種GRID控件內嵌EDIT、COMBOBOX的實現方法,本人在前階段的開發中也遇到這方面的困難,在網絡上找了又找,大多是針對ListView和DBGrid的,而對於MSFlexGrid的實現,則少之又少。在廣大網友的支持下,終於本人找到了實現MSFlexGrid內嵌EDIT、COMBOBOX的一種方法,我想本文對於採用MSFlexGrid進行應用開發的朋友一定有相當大的幫助。
總結一些網友以及本人在最初實現MSFlexGrid內嵌控件失敗的原因,大多是由兩方面造成的:
1、座標系轉換問題,MSFlexGrid採用的座標系和一般的控件不同,所以在操作時,需要進行轉換。
2、控件在創建上的問題,如果你把控件直接創建在主窗口中,那麼往往會存在,程序運行時,鼠標一點網格,控件就HIDE掉,所以在創建控件EDIT、COMBOBOX時,要以FlexGrid爲父窗口。
下面,我用一個示例程序來簡單的說明一下,同時我們的示例程序還實現了在FlexGrid中按TAB鍵跳至下一網格[下面提到的網格均指MSFlexGrid中的小單元格]的功能。想要源代碼的請登http://www.maoyeah.com/display.asp?boardid=3&id=35
首先,在對話框的初始化中調用我們的初始化函數:
void CProg5Dlg::InitControls()
{
//創建各個內嵌控件
m_edit.Create(WS_CHILD,CRect(0,0,0,0),&m_FlexGrid,IDC_EDIT);
m_cmb.Create(WS_CHILD|CBS_DROPDOWNLIST,CRect(0,0,0,0),&m_FlexGrid,IDC_CMB);
//設置爲和主窗口相同字體
m_edit.SetFont(GetFont());
m_cmb.SetFont(GetFont());
//用數據填充Grid 和 ComboBox
long lRow ;
long lRowCount = m_FlexGrid.GetRows();
long lCol ;
long lColCount = m_FlexGrid.GetCols();
for (lRow = 1; lRow < lRowCount; lRow++)
{
m_FlexGrid.SetRow(lRow);
for(lCol = 1; lCol < lColCount; lCol++)
{
m_FlexGrid.SetCol(lCol);
CString strText;
strText.Format("%ld-%ld",lRow,lCol);
//用數據填充Grid
m_FlexGrid.SetText(strText);
//用數據填充ComboBox
m_cmb.AddString(strText);
}
}
}
其中m_edit、m_cmb是我們聲明的類數據成員:
private:
CEdit m_edit;
CComboBox m_cmb;
它們的創建一定要用.Create的方法並以MSFlexGrid爲父窗口,要不然,程序運行時,你一點MsflexGrid,你的Edit或ComboBox就不見了 [這是因爲,MSFlexGrid和你的Edit或ComboBox同以Dialog爲父窗口,你點了MsflexGrid,在Z座標上,它就蓋住了你的內嵌控件] ,因爲在創建之後,它們採用的字體可能和你的主窗口風格不一致,所以還要設置一下字體。
接下來就是程序中最重要的一個函數了:
void CProg5Dlg::GridEdit(WORD nKeyAsciiCode, CWnd *p_wnd)
{
if(p_wnd == NULL)
{//得到當前編輯的網格的內嵌控件是m_edit or m_cmb
p_wnd = GetThisCellMaskControl();
}
ASSERT(p_wnd != NULL);
//支持座標變換
CDC* pDC = m_FlexGrid.GetDC();
int nLogX = pDC->GetDeviceCaps(LOGPIXELSX);
int nLogY = pDC->GetDeviceCaps(LOGPIXELSY);
ReleaseDC(pDC);
CString sz;
//當有文字輸入時,如果當前控件是Edit,那麼光標到末尾
if (nKeyAsciiCode >= 0 && nKeyAsciiCode < ’ ’)
{
if(p_wnd->IsKindOf(RUNTIME_CLASS(CEdit)))
{
((CEdit *)p_wnd)->SetSel(-1, -1);
}
}
else
{
CString Input = " ";
p_wnd->GetWindowText(sz);
if (nKeyAsciiCode > 0x100)
{//用來支持漢字輸入
Input.SetAt(0, nKeyAsciiCode >> 8);
Input.SetAt(1, nKeyAsciiCode & 0xff);
}
else
{//非漢字
Input = (char)nKeyAsciiCode;
}
sz += Input;
p_wnd->SetWindowText(sz);
}
if(p_wnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
{
p_wnd->MoveWindow(
(m_FlexGrid.GetCellLeft() * nLogX)/1440 - 3,
(m_FlexGrid.GetCellTop() * nLogY)/1440 - 3,
(m_FlexGrid.GetCellWidth()* nLogX)/1440 ,
(m_FlexGrid.GetCellHeight()* nLogY)/1440 + 100,FALSE);
}
else if(p_wnd->IsKindOf(RUNTIME_CLASS(CEdit)))
{
p_wnd->MoveWindow(
(m_FlexGrid.GetCellLeft() * nLogX)/1440 - 3,
(m_FlexGrid.GetCellTop() * nLogY)/1440 - 3,
(m_FlexGrid.GetCellWidth()* nLogX)/1440,
(m_FlexGrid.GetCellHeight()* nLogY)/1440,FALSE);
}
else
{
ASSERT(0);
}
//顯示我們的控件
p_wnd->ShowWindow(SW_SHOW);
p_wnd->SetFocus();
p_wnd->GetWindowText(sz);
if(p_wnd->IsKindOf(RUNTIME_CLASS(CEdit)))
{
((CEdit *)p_wnd)->SetSel(sz.GetLength(), sz.GetLength(), FALSE);
}
m_FlexGrid.RedrawWindow();
}
[說明:這個函數部分代碼並非原創]
這個函數的作用,主要是支持MSFlexGrid的編輯,並把你的內嵌控件顯示出來,當然,如果它是一個EDIT,那麼我們有責任把EDIT內的光標置於EDIT中字串的最後。這個函數有兩個參數:
WORD nKeyAsciiCode:如果激活編輯FlexGrid的事件是一次按鍵,那麼這個參數當然就和按鍵的信息有關了,另外在函數中,通過它可以實現支持中文,比如:您把焦點放到一個內嵌爲EDIT的網格中[只是讓FlexGrid的網格得到焦點,而不讓內嵌的EDIT顯示出來],直接輸入中文,然後就會發現,網格自動進入編輯狀態,並且你輸入的中文漢字位於字串的最後:
第二個參數:
CWnd *p_wnd可以指定你想使用的內嵌控件,傳入時可以使用&m_edit或&m_cmb。
當然,你可以不指定它而用我們當初設置好的規則[這規則是指MSFlexGrid哪列固定採用哪個內嵌控件],這是用什麼實現的呢?
看到函數GridEdit中的
if(p_wnd == NULL)
{
p_wnd = GetThisCellMaskControl();
}
了吧?
CWnd * CProg5Dlg::GetThisCellMaskControl()
{
switch(m_FlexGrid.GetCol())
{
//第一列,第三列用ComboBox做爲內嵌控件
case 1:
case 3:
return &m_cmb;
break;
//其它的用Edit
default:
return &m_edit;
}
}
我們可通過這個函數來設定一些基本規則。
那麼GridEdit函數是由誰來調用的呢?答案當然是由想實現編輯網格的事件觸發的,在這裏我設定爲鼠標雙擊和網格有焦點時的按鍵事件:
//鼠標雙擊激發
void CProg5Dlg::OnDblClickMsflexgrid()
{
//第一行和第一列是固定的,我不想編輯它們
if(m_FlexGrid.GetCol() == 0 || m_FlexGrid.GetRow() == 0)
return;
GridEdit(0,NULL);
}
//按鍵事件激發
void CProg5Dlg::OnKeyPressMsflexgrid(short FAR* KeyAscii)
{
// TODO: Add your control notification handler code here
if(m_FlexGrid.GetCol() == 0 || m_FlexGrid.GetRow() == 0)
return;
GridEdit(*KeyAscii,NULL);
}
引發這個函數之前,我們最好把ComboBox的當前選擇內容和Edit的內容設爲要編輯的那個格子的內容:
void CProg5Dlg::OnEnterCellMsflexgrid()
{
// TODO: Add your control notification handler code here
int nthisRow = m_FlexGrid.GetRow();
if(nthisRow == 0)
{
return;
}
CString sz;
sz = m_FlexGrid.GetText();
CWnd *pWnd = GetThisCellMaskControl();
if(pWnd->IsKindOf(RUNTIME_CLASS(CComboBox)))
{
((CComboBox *)pWnd)->SelectString(-1,sz);
}
else if(pWnd->IsKindOf(RUNTIME_CLASS(CEdit)))
{
pWnd->SetWindowText(sz);
}
else
{
ASSERT(0);
}
}
你覺得這樣就行了嗎?當然不行!我們在把焦點移到其它網格時[只是把焦點移走],我們有必要把那個已經顯示出來的內嵌控件HIDE掉,並把它的內容傳給網格,這也是我們編輯的目的。
void CProg5Dlg::OnLeaveCellMsflexgrid()
{
// TODO: Add your control notification handler code here
int nthisRow = m_FlexGrid.GetRow();
if(nthisRow == 0)
{
return;
}
CString sz;
CWnd * p_ThisWnd = GetThisCellMaskControl();
ASSERT(p_ThisWnd != NULL);
if (p_ThisWnd->IsWindowVisible())
{
p_ThisWnd->GetWindowText(sz);
m_FlexGrid.SetText(sz);
p_ThisWnd->ShowWindow(SW_HIDE);
}
}
基本上差不多了。
下面簡單介紹一下用Tab實現在MSFlexGrid的網格中跳轉的問題。
我一看到這種應用,馬上想到採用PreTranslateMessage函數,這個函數可是真好用,一般實現什麼窗口內焦點的跳轉我都用它。
在實現它之前,我們先定義一個跳到一下格子的函數:
void CProg5Dlg::GoToNextCell()
{
if(m_FlexGrid.GetCol() == m_FlexGrid.GetCols() - 1)
{
if(m_FlexGrid.GetRow() != m_FlexGrid.GetRows() - 1)
{
m_FlexGrid.SetRow(m_FlexGrid.GetRow() + 1);
m_FlexGrid.SetCol(1);
}
else
{
return;
}
}
else
{
m_FlexGrid.SetCol(m_FlexGrid.GetCol() + 1);
}
}
在我們的PreTranslateMessage會調用它實現跳到下一網格中:
BOOL CProg5Dlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
CWnd *pWnd = CWnd::FromHandle(pMsg->hwnd);
CWnd *pCon = GetThisCellMaskControl();
if(pMsg->message!=WM_KEYDOWN)
return CDialog::PreTranslateMessage(pMsg);
switch(pMsg->wParam)
{
case VK_TAB:
if(pCon->GetSafeHwnd() == pMsg->hwnd)
{//如果按TAB時,處於EDIT狀態,也會跳到下一格子
GoToNextCell();
return TRUE;
}
switch(pWnd->GetDlgCtrlID())
{
case IDC_MSFLEXGRID:
GoToNextCell();
return TRUE;
}
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
好了,整個應用就講完了,我想對於採用MSFlexGrid實現應用的朋友們,這個小東東一定能起到拋磚引玉的作用。