得到1

// HitTestEx   
- Determine the row index and column index for a point
// Returns     
- the row index or -1 if point is not over a row
// point       
- point to be tested.
// col         
- to hold the column index
int
 CMyListCtrl::HitTestEx(CPoint &point, int
 *col) const

{
        
int
 colnum = 0;
        
int
 row = HitTest( point, NULL );
        

        
if
( col ) *col = 0;
 
        
// Make sure that the ListView is in LVS_REPORT

        
if
( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
               
return
 row;
 
        
// Get the top and bottom row visible

        
row = GetTopIndex();
        
int
 bottom = row + GetCountPerPage();
        
if
( bottom > GetItemCount() )
               
bottom = GetItemCount();
        

        
// Get the number of columns

        
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
        
int
 nColumnCount = pHeader->GetItemCount();
 
        
// Loop through the visible rows

        
for
( ;row <= bottom;row++)
        
{
               
// Get bounding rect of item and check whether point falls in it.

               
CRect rect;
               
GetItemRect( row, &rect, LVIR_BOUNDS );
               
if
( rect.PtInRect(point) )
               
{
                       
// Now find the column

                       
for
( colnum = 0; colnum < nColumnCount; colnum++ )
                       
{
                               
int
 colwidth = GetColumnWidth(colnum);
                               
if
( point.x >= rect.left 
                                      
&& point.x <= (rect.left + colwidth ) )
                               
{
                                      
if
( col ) *col = colnum;
                                      
return
 row;
                               
}
                               
rect.left += colwidth;
                       
}
               
}
        
}
        
return
 -1;
}

 

 

CListCtrl 類成員

CListCtrl::HitTest

int HitTest(LVHITTESTINFO* pHitTestInfo) const
int HitTest(CPoint pt,UINT* pFlags=NULL) const

返回值:
返回參數pHitTestInfo 指定位置的項的索引,否則爲-1

參數: pHitTestInfo 含有要進行擊中測試的位置以及接受擊中測試有關結果信息的LVHITTESTINFO 結構的地址。
pt
被測試的指針。
pFlags
指向接受測試結果信息的整數的指針。請參閱聯機文檔 平臺SDK” 中有關LVHITTESTINFO 結構的flags 成員的註解。

說明:
如果有,則決定哪一個列表視圖項在指定的位置上。
可以通過使用結構中flags 成員的LVHT_ABOVE, LVHT_BELOW,LVHT_TOLEFT 以及LVHT_TORIGHT 的值來決定是否滾動列表視圖控件的內容。上述兩種標誌可以自由組合,例如,假設其位於客戶區域的左上角。
可以通過測試結構中flags 成員的LVHT_ONITEM 值來決定是否給定的位置位於列表視圖項的上方。該數值通過結構flags 成員中的 LVHT_ONITEMICONLVHT_ONITEMLABEL,LVHT_ONITEMSTATEICON 的值的位或運算而獲取。

 

 

HitTest :得到當前鼠標位置的Item

其實關鍵是要有ScreenToClient 這個函數的使用,我先前沒有用這個函數,HitTest 老是返回-1, 搞得我都頭大了。不過這個不能用於SubItem, 那應該要用SubItemHitTest

LVHITTESTINFO ht ;

GetCursorPos(&(ht.pt)) ;

m_friendList.ScreenToClient(&ht.pt) ;

m_friendList.HitTest(&ht) ;

 if(ht.iItem == -1)  // 檢查是否有item 選中

      return  ;

SubItemHitTest

void   CTest6Dlg::OnClickList1(NMHDR*   pNMHDR,   LRESULT*   pResult)  
{
/****************************************/
/*      
確定單擊的listctrl 的行列號   方法1       */
/****************************************/

/*
DWORD   dwPos   =   GetMessagePos();
CPoint   point(   LOWORD(dwPos),   HIWORD(dwPos)   );

m_list.ScreenToClient(&point);  

LVHITTESTINFO   lvinfo;
        lvinfo.pt   =   point;
        lvinfo.flags   =   LVHT_ABOVE;
       
int   nItem   =   m_list.SubItemHitTest(&lvinfo);
if(nItem   !=   -1)
{
CString   strtemp;
strtemp.Format( "
單擊的是第%d 行第%d ",   lvinfo.iItem,   lvinfo.iSubItem);
}
*pResult   =   0;
}

雙擊時鼠標所在的位置。下面我們就再來爲 CScheduleView 添加一個處理 NM_DBLCLK 通知消息的處理函數 OnDblclk() ,並輸入下面的實現代碼:
void CScheduleView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) {
if( ((LPNMLISTVIEW) pNMHDR)->iItem != -1 )
GetDocument()->OnEdittask();
*pResult = 0;
}
這個函數比較有意思,它的第一個參數本來是一個指向 NMHDR 結構的指針,然而在函數體中我們卻將之強制轉換成指向一個 NMLISTVIEW 結構的指針,爲什麼呢?原因是 4.71 版及更高版本的 List 控件( IE 4.0 之後的 List 控件都滿足版本要求)在發送通知消息 NM_DBLCLK 的時,實際上傳遞的是
一個 NMLISTVIEW 結構的指針,但標準的 NM_DBLCLK 消息傳遞的卻是 NMHDR 結構的指針, ClassWizard 在生成函數框架時是按照標 準消息格式來處理的。 NMLISTVIEW 結構除了在開頭部分包含了一個 NMHDR 結構外,它還提供了被雙擊項目的相關信息,如果它的 iItem 成員等於 -1 ,則表示用戶不是對
着一個項目雙擊的,如果是這樣的話,我們也就不必調用 OnEdittask() ,從而避免了出現不必要的操作提示信息。實際上, OnLButtonDblClk() 也可以實現 OnDblclk() 的這個功能,但需要將相關代碼改爲:
if( GetListCtrl().GetSelectedCount() == 1 )
GetDocument()->OnEdittask();
現在這兩個函數的功能就基本一致了,朋友們可以任意保留其中一個函數,而把另一個函數刪掉,當然,今後在編寫其它程序時 OnLButtonDblClk() OnDblclk() 就不一定能做到等價了。另外,大家不妨進一步研究一下 WM_LBUTTONDBLCLK NM_DBLCLK 之間的關係,解釋爲什麼用戶的一次雙
擊能引發這兩種消息,它們的發生順序又如何等等。
接下來我們再次利用 ClassWizard CScheduleView 添加一個處理 WM_RBUTTONDOWN 消息的函數 OnRButtonDown() ,準備在其中彈出一個關聯菜單。
   
編輯和使用彈出式菜單
關聯菜單實質上是一種彈出式菜單,通常情況下,彈出式菜單相當於主菜單的一個子菜單項。我們打開資源編輯器,添加一個新的菜單資源,修改其 ID IDR_TASKMENU ,然後在第一個頂級菜單項下面添加三個命令,它們與程序主菜單的 " 安排 " 子菜單下面的命令完全一樣,包括 ID Caption
Prompt 。編輯完畢後,以 Popup 方式來查看 IDR_TASKMENU ,其效果應如圖 18-1 所示。
現在我們來爲 OnRButtonDown() 編寫實現代碼:
void CScheduleView::OnRButtonDown(UINT nFlags, CPoint point) {
CMenu tmpMenu;
CMenu* pSubMenu;
tmpMenu.LoadMenu(IDR_TASKMENU); //
裝載菜單資源
pSubMenu=tmpMenu.GetSubMenu(0); //
獲得子菜單的指針
ClientToScreen(&point); //
將客戶區的座標位置轉換成屏幕座標位置
pSubMenu->TrackPopupMenu( TPM_RIGHTBUTTON|TPM_LEFTALIGN|TPM_TOPALIGN, point.x, point.y, this,0 ); //
顯示彈出式菜單
tmpMenu.DestroyMenu(); //
釋放菜單資源
}
在上面的代碼中, LoadMenu() 用於裝載菜單資源 IDR_TASKMENU ,由於我們使用了局部變量來存放不屬於任何一個窗口的菜單對象,所以在退 OnRButtonDown() 函數之前必須釋放相應的菜單資源,否則多次調用該函數之後,程序將會佔掉不少的系統資源。
ClientToScreen()
的作用是將客戶區的座標位置轉換成屏幕座標位置,因爲 WM_RBUTTONDOWN 消息中的鼠標位置參數是相對於客戶區 的,而 TrackPopupMenu() 函數的參數必須是基於屏幕座標系的,如果不經過一次轉換,菜單彈出的位置就不知道會偏到哪裏去了。
現在我們來運行一下 Schedule ,在客戶區內單擊鼠標右鍵,就會彈出如圖 18-2 所示的關聯菜單,選擇命令之後它就會調用相應的處理函數。
到本講爲止, Schedule 的編程工作也就要告一段落了, Schedule 只是心鈴用來講解 VC 編程的一個實例,它的功能很單一,但仍有一定的實用價 值。已經用慣了各種優秀軟件的朋友肯定不會滿足於現在的 Schedule ,因爲它現在只是一隻醜小鴨而已,要把它變成一隻天鵝還需要我們更多的辛
勤勞動,心鈴在下面列出了一些可能的改進,希望已經喜愛上 VC 編程的朋友們自已動手來進一步修飾 Schedule
1
.刪除 Schedule 中一些不必要的菜單命令和工具欄上的按鈕;
2
.根據用戶選中事件條目的情況自動改變某些菜單命令的有效狀態,例如沒有選中任何條目時, " 編輯條目 " 應處於不能選擇的無效狀態,它所對應的工具按鈕也應無效;
3
.讓 Schedule 能夠象網絡螞蟻那樣在最小化時隱藏自己的窗口,在任務欄的通知區域內放置一個圖標,並設計相關的菜單;
4
.修改 Schedule 的用戶界面,設計更好的圖標和位圖;
5
.修改添加和編輯事件條目的對話框,讓用戶能更直觀地設置時間;
6
.設計一個啓動畫面,把某些選項保存到註冊表之中;
7
.爲 Schedule 增添打印功能;
… …
等等

 

 

CListCtrl 幾點經驗

每個List 控件都有一個CHeaderCtrl 。且它的ID0
CHeaderCtrl* pHeader =(CHeaderCtrl*)m_listCtrl.GetDlgItem(0);
即使List 控件非report 模式,Header 控件也存在,只是此時它的尺寸爲0
可利用以下代碼使得控件的第一列自適應大小:
m_listctrl.SetColumnWidth( 0, LVSCW_AUTOSIZE );
List
控件中的圖標初始化時可如下設置:
m_listCtrl.InsertItem( LVIF_TEXT | LVIF_IMAGE, nRow, sItemText, 0, 0, nImage
, NULL);
在運行中需動態改變可調用SetItem() 函數.
m_listCtrl.SetItem( 0, 0, LVIF_IMAGE, NULL, nImage, 0, 0, 0 );
刪去圖標可將nImage 設置爲-1

以下代碼可將列表框的第一列限定大小:
BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;
if((pHDN->hdr.code == HDN_BEGINTRACKW || pHDN->hdr.code == HDN_BEGIN
TRACKA)
&& pHDN->iItem == 0) // Prevent only first (col#
0) from resizing
{
*pResult = TRUE; // disable tracking
return TRUE; // Processed message
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}


選定cell
CListCtrl::OnClick(...)
{
int column;
CRect m_rect;
//the function below is provided in CListCtrl inPlace editing
int index = GetRowColumnIndex(point, &column);
if(index == -1)return;
int offset = 0;
for(int i = 0; i < column; i++)
offset += GetColumnWidth(i);
//Get the rectangle of the label and the icon
GetItemRect(index, &m_rect, LVIR_BOUNDS);
m_rect.left += offset + 4;
//Get the columnWidth of the selected column
m_rect.right = m_rect.left + GetColumnWidth(column);
Update(index);
CClientDC dc(this); //this is the pointer of the current view
dc.DrawFocusRect(m_rect);
}

Select NonFirst cell:

void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
CListCtrl::OnLButtonDown(nFlags, point);
int index;
point.x = 2;
if( ( index = HitTest( point, NULL )) != -1 )
{
SetItemState( index, LVIS_SELECTED | LVIS_FOCUSED ,
LVIS_SELECTED | LVIS_FOCUSED);
}
}


HitTestEx
的代碼如下:
判斷點擊的是哪一列:
// HitTestEx - Determine the row index and column index for a point
// Returns - the row index or -1 if point is not over a row
// point - point to be tested.
// col - to hold the column index
int CMyListCtrl::HitTestEx(CPoint &point, int *col) const
{
int colnum = 0;
int row = HitTest( point, NULL );
if( col ) *col = 0;
// Make sure that the ListView is in LVS_REPORT
if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
return row;
// Get the top and bottom row visible
row = GetTopIndex();
int bottom = row + GetCountPerPage();
if( bottom > GetItemCount() )
bottom = GetItemCount();
// Get the number of columns
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// Loop through the visible rows
for( ;row <=bottom;row++)
{
// Get bounding rect of item and check whether point falls in it.
CRect rect;
GetItemRect( row, &rect, LVIR_BOUNDS );
if( rect.PtInRect(point) )
{
// Now find the column
for( colnum = 0; colnum < nColumnCount; colnum++ )
{
int colwidth = GetColumnWidth(colnum);
if( point.x >= rect.left
&& point.x <= (rect.left + colwidth ) )
{
if( col ) *col = colnum;
return row;
}
rect.left += colwidth;
}
}
}
return -1;
}


選中一定範圍內的Item
// SelItemRange - Selects/Deselect a range of items
// Returns - The number of new items selected
// bSelect - TRUE to select, FALSE to deselect
// nFirstItem - index of first item to select
// nLastItem - index of last item to select
int CMyListCtrl::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem)
{
// make sure nFirstItem and nLastItem are valid
if( nFirstItem >= GetItemCount() || nLastItem >= GetItemCount() )
return 0;
int nItemsSelected = 0;
int nFlags = bSelect ? 0 : LVNI_SELECTED;
int nItem = nFirstItem - 1;
while( (nItem = GetNextItem( nItem, nFlags )) >=0
&& nItem <= nLastItem )
{
nItemsSelected++;
SetItemState(nItem, bSelect ? LVIS_SELECTED : 0, LVIS_SELECT
ED );
}
return nItemsSelected;
}

SetHeaderBitmap:

static int _gnCols = 5;
static int _gnColSize[] =
{
18,21,22,18,380
};
static CString _gcsColLabel[] =
{
_T("x"),
_T("x"),
_T("x"),
_T("x"),
_T("Description:")
};
void CRightView::BuildColumns()
{
// Insert the columns into the list control
LV_COLUMN lvCol;
lvCol.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
for (int i = 0; i < _gnCols; ++i)
{
lvCol.iSubItem = i;
lvCol.pszText = (char*)(LPCTSTR)_gcsColLabel;
lvCol.cx = _gnColSize;
lvCol.fmt = LVCFMT_LEFT;
m_ListCtrl->InsertColumn(i, &lvCol);
}
for (int x = 0; x < _gnCols-1; x++)
SetHeaderBitmap(x, nHeaderBmps[x], HDF_STRING);
}
void CRightView::SetHeaderBitmap(int nCol, int nBitmap, DWORD dwRemove)
{
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
HD_ITEM hdi;
hdi.mask = HDI_FORMAT;
pHeader->GetItem (nCol, &hdi);
hdi.mask = HDI_BITMAP | HDI_FORMAT;
hdi.fmt |= HDF_BITMAP;
hdi.hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
if (dwRemove)
hdi.fmt &= ~dwRemove;
pHeader->SetItem (nCol, &hdi);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章