欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[ATL/WTL]_[初级]_[在ListView指定的单元格显示ToolTip]

程序员文章站 2022-05-29 09:28:33
...

场景

  1. WTLCListViewCtrl是常用的显示表格数据的类. 有时候我们需要在表格的某个单元格显示一个按钮,并且鼠标移动上去时按钮有状态变化;或者显示这个按钮的一个ToolTip提示。如何做?

说明

  1. 要在表格的指定Cell或者某个鼠标位置发生消息响应。最后选的方案就是子类化CListViewCtrl,并响应WM_MOUSEMOVE消息,之后就是在这个消息里通过得到的坐标点计算得出nItem行和nSubItem列. 再来处理现实显示单元格内的按钮或ToolTip问题。但是这种方案需要在CListViewCtrl子类里实现。

  2. 除了改动子类外,可以查阅ListView的消息和通知看有没有相关的可以在父窗口捕抓的消息。有一个通知LVN_HOTTRACK就可以在父窗口捕抓,并且它提供了参数NMLISTVIEW,这个类型的iItem的值有时候会是-1,而iSubItem也有可能得到0,所以这个两个成员变量不需要。可以通过CListViewCtrl.SubItemHitTest方法来获取指定坐标所属的单元格,它需要提供一个LVHITTESTINFO的参数,获取成功后会在iItemiSubItem里得到正确的单元格位置。

    typedef struct tagNMLISTVIEW {
      NMHDR  hdr;
      int    iItem;
      int    iSubItem;
      UINT   uNewState;
      UINT   uOldState;
      UINT   uChanged;
      POINT  ptAction;
      LPARAM lParam;
    } NMLISTVIEW, *LPNMLISTVIEW;
    
    typedef struct tagLVHITTESTINFO
    {
        POINT pt;
        UINT flags;
        int iItem;
    #if (_WIN32_IE >= 0x0300)
        int iSubItem;    // this is was NOT in win95.  valid only for LVM_SUBITEMHITTEST
    #endif
    #if _WIN32_WINNT >= 0x0600
        int iGroup; // readonly. index of group. only valid for owner data.
                    // supports single item in multiple groups.
    #endif
    } LVHITTESTINFO, *LPLVHITTESTINFO;
    
  3. 关于CListViewCtrl响应ToolTip,它提供了一个SetToolTips方法,我们只需要创建一个CToolTipCtrl并初始化即可使用. 当不需要显示ToolTip时,可以通过SetToolTips(NULL)来关闭, 可以通过这个特性来开关ToolTip.

    tooltip_.Create(listview_,NULL,NULL,TTS_NOPREFIX);
    tooltip_.AddTool(listview_,L"");
    listview_.SetToolTips(tooltip_);
    
  4. 最后就是在LVN_HOTTRACK处理函数进行判断条件并显示ToolTip.

    static int kShowTooltipColumn = 2;
    if(info.iSubItem == kShowTooltipColumn){
    	BSTR buf = NULL;
    	listview_.GetItemText(nItem,info.iSubItem,buf);
    	tooltip_.UpdateTipText(buf,listview_);
    	::SysFreeString(buf);
    
    	if(!listview_.GetToolTips()) 
    		listview_.SetToolTips(tooltip_);
    }else{
    	if(listview_.GetToolTips())
    		listview_.SetToolTips(NULL);
    }
    

例子

view.h

  1. 代码
    #pragma once
    
    #include <utility>
    #include <string>
    
    
    enum
    {
    	kMyStaticId = WM_USER+1,
    	kMyListViewId,
    	kMyCheckListViewId,
    	kMySortListViewId,
    	kMyButtonId
    };
    
    class CView : public CWindowImpl<CView>
    {
    public:
    	DECLARE_WND_CLASS(NULL)
    
    	BOOL PreTranslateMessage(MSG* pMsg);
    
    	BEGIN_MSG_MAP_EX(CView)
    		MSG_WM_CREATE(OnCreate)
    		MESSAGE_HANDLER(WM_PAINT, OnPaint)
    		NOTIFY_HANDLER(kMyListViewId,NM_CLICK,OnNMClickListResult)
    		NOTIFY_HANDLER(kMyListViewId,LVN_HOTTRACK,OnListItemHotTrack)
    		REFLECT_NOTIFICATIONS()
    	END_MSG_MAP()
    
    	int OnCreate(LPCREATESTRUCT lpCreateStruct);
    	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
    	void UpdateLayout();
    	LRESULT OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    	void AddMockData(CListViewCtrl& listview);
    	void OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl);
    	LRESULT OnListItemHotTrack(int idCtrl,LPNMHDR pnmh,BOOL &bHandled);
    
    private:
    	std::wstring GetControlText(HWND hwnd,wchar_t* buf = NULL);
    
    	CListViewCtrl listview_;
    
    	CFont font_normal_;
    	CFont font_bold_;
    
    	CBrushHandle brush_white_;
    	CBrushHandle brush_hollow_;
    	CBrush brush_red_;
    	CToolTipCtrl tooltip_;
    	TCHAR strToolTipText_[MAX_PATH];
    
    public:
    };
    
    

View.cpp

  1. 代码
    // View.cpp : implementation of the CView class
    //
    /
    
    #include "stdafx.h"
    #include "resource.h"
    #include <utility>
    #include <sstream>
    #include <assert.h>
    
    #include "View.h"
    #include <CommCtrl.h>
    #include <string>
    #include <regex>
    
    using namespace std;
    
    BOOL CView::PreTranslateMessage(MSG* pMsg)
    {
    	return FALSE;
    }
    
    LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
    	CPaintDC dc(m_hWnd);
    	CMemoryDC mdc(dc,dc.m_ps.rcPaint);
    
    	CRect rect_client;
    	GetClientRect(&rect_client);
    	mdc.FillSolidRect(rect_client,RGB(255,255,255));
    	//TODO: Add your drawing code here
    
    	return 0;
    }
    
    static HFONT GetFont(int pixel,bool bold,const wchar_t* font_name)
    {
    	LOGFONT lf; 
    	memset(&lf, 0, sizeof(LOGFONT)); // zero out structure 
    	lf.lfHeight = pixel; // request a 8-pixel-height font
    	if(bold)
    	{
    		lf.lfWeight = FW_BOLD;  
    	}
    	lstrcpy(lf.lfFaceName, font_name); // request a face name "Arial"
    	
    	HFONT font = ::CreateFontIndirect(&lf);
    	return font;
    }
    
    
    std::wstring CView::GetControlText(HWND hwnd,wchar_t* buf)
    {
    	auto length = ::GetWindowTextLength(hwnd);
    	bool bufNull = false;
    	if(!buf){
    		buf = new wchar_t[length+1]();
    		bufNull = true;
    	}
    	
    	::GetWindowText(hwnd,buf,length+1);
    	std::wstring str(buf);
    
    	if(bufNull)
    		delete []buf;
    
    	return str;
    }
    
    static std::wstring GetProductBinDir()
    {
    	static wchar_t szbuf[MAX_PATH];  
    	GetModuleFileName(NULL,szbuf,MAX_PATH);  
        PathRemoveFileSpec(szbuf);
    	int length = lstrlen(szbuf);
    	szbuf[length] = L'\\';
    	szbuf[length+1] = 0;
    	return std::wstring(szbuf);
    }
    
    LRESULT CView::OnNMClickListResult(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {
    	NMLISTVIEW* p = (NMLISTVIEW*) pnmh;
    	int row = p->iItem;
    	if(row == -1 || (row % 2))
    		return 0;
    	
    	auto header = listview_.GetHeader();
    	int nColumnCount = header.GetItemCount();
    	int minLength = 8;
    	wstringstream wss;
    	for(int i =0; i< nColumnCount; ++i){
    		BSTR buf = NULL;
    		listview_.GetItemText(row,i,buf);
    		wss << buf << L" : ";
    		::SysFreeString(buf);
    	}
    	wstring str = wss.str();
    	MessageBox(str.c_str());
    	return 0;
    }
    
    void CView::AddMockData(CListViewCtrl& listview)
    {
    	listview.InsertColumn(0,L"No.",LVCFMT_LEFT,40);
    	listview.InsertColumn(1,L"Name",LVCFMT_LEFT,200);
    	listview.InsertColumn(2,L"Website",LVCFMT_LEFT,200);
    	listview.InsertColumn(3,L"Level",LVCFMT_LEFT,200);
    
    	wchar_t buf[MAX_PATH] = {0};
    	LVCOLUMN co;
    	memset(&co,0,sizeof(co));
    	co.mask = LVCF_TEXT;
    	co.pszText = buf;
    	co.cchTextMax = MAX_PATH;
    	listview.GetColumn(1,&co);
    	std::wstring c0Text(buf);
    
    	listview.GetColumn(2,&co);
    	std::wstring c1Text(buf);
    	
    	listview.GetColumn(3,&co);
    	std::wstring c2Text(buf);
    
    	for(int i = 0; i<10;++i){
    		
    		wsprintf(buf,L"%d",i+1);
    		listview.AddItem(i,0,buf);
    
    		wsprintf(buf,(c0Text+L"-%d").c_str(),i);
    		listview.AddItem(i,1,buf);
    
    		wsprintf(buf,(c1Text+L"-%d").c_str(),i);
    		listview.AddItem(i,2,buf);
    
    		wsprintf(buf,(c2Text+L"-%d").c_str(),i);
    		listview.AddItem(i,3,buf);
    	}
    }
    
    LRESULT CView::OnListItemHotTrack(int idCtrl,LPNMHDR pnmh,BOOL &bHandled)
    {
    	bHandled = TRUE;
    	LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) pnmh;
    	
    	// 获取坐标所在的Item(行)和SubItem(列).
    	LVHITTESTINFO info;
    	memset(&info,0,sizeof(info));
    	info.pt = lpnmlv->ptAction;
    	auto nItem = listview_.SubItemHitTest(&info);
    	if(nItem == -1)
    		return 0;
    
    	static int kShowTooltipColumn = 2;
    	if(info.iSubItem == kShowTooltipColumn){
    		BSTR buf = NULL;
    		listview_.GetItemText(nItem,info.iSubItem,buf);
    		tooltip_.UpdateTipText(buf,listview_);
    		::SysFreeString(buf);
    
    		if(!listview_.GetToolTips()) 
    			listview_.SetToolTips(tooltip_);
    	}else{
    		if(listview_.GetToolTips())
    			listview_.SetToolTips(NULL);
    	}
    
    	return 0;
    }
    
    int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	font_normal_ = ::GetFont(16,false,L"Arial");
    	font_bold_ = ::GetFont(16,true,L"Arial");
    
    	// 1.创建CListViewCtrl
    	listview_.Create(m_hWnd,0,NULL,WS_CHILD | WS_TABSTOP |WS_VISIBLE
    		|LVS_ALIGNLEFT|LVS_REPORT|LVS_SHOWSELALWAYS|WS_BORDER,0,kMyListViewId);
    	// 2.如果需要监听在listview鼠标移动消息,创建时传入LVS_EX_TRACKSELECT扩展样式。
    	listview_.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_DOUBLEBUFFER|LVS_EX_TRACKSELECT);
    	listview_.SetFont(font_normal_);
    	auto header = listview_.GetHeader();
    	header.SetFont(font_bold_);
    	listview_.SetBkColor(RGB(255,255,255));
    	AddMockData(listview_);
    
    	tooltip_.Create(listview_,NULL,NULL,TTS_NOPREFIX);
    	tooltip_.AddTool(listview_,L"");
    	listview_.SetToolTips(tooltip_);
    	listview_.SetHoverTime(10);
    
    	brush_hollow_ = AtlGetStockBrush(HOLLOW_BRUSH);
    	brush_white_ = AtlGetStockBrush(WHITE_BRUSH);
    	brush_red_.CreateSolidBrush(RGB(255,0,0));
    	UpdateLayout();
    
    	return 0;
    }
    
    void CView::OnCommandIDHandlerEX(UINT uNotifyCode, int nID, CWindow wndCtl)
    {
    		
    }
    
    void CView::UpdateLayout()
    {
    	CRect rect;
    	GetClientRect(&rect);
    
    	CClientDC dc(m_hWnd);
    	dc.SelectFont(font_normal_);
    
    	CSize size_control(700,300);
    	CRect rect_control = CRect(CPoint(20,20),size_control);
    	listview_.MoveWindow(rect_control);
    
    }
    
    

图示

[ATL/WTL]_[初级]_[在ListView指定的单元格显示ToolTip]

项目下载

https://download.csdn.net/download/infoworld/13192317

参考

extended-list-view-styles

lvm-sethovertime