65.9K
CodeProject 正在变化。 阅读更多。
Home

向 MFC 网格控件添加超链接支持

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2000 年 6 月 4 日

CPOL
viewsIcon

112489

downloadIcon

1666

一个为 MFC 网格控件添加超链接支持的新类。

  • 下载源文件 - 3 Kb
  • 引言

    本文介绍了一个新的单元格类,用于 MFC 网格控件,它在单元格中提供超链接。

    要创建这个类,我们只需从 CGridCell 派生一个单元格。 从 CGridCell 派生单元格允许我们保持普通单元格的功能,并添加我们自己的 url 支持。 对于我们的新 url 单元格,我们需要处理绘制、点击、光标、URL 颜色和 URL 解析。 我们添加一个 COLORREF 变量来存储 URL 颜色。 m_clrUrl 将存储我们通过 SetUrlColor(COLORREF clr) 设置的 URL,或者默认颜色为 GetSysColor(COLOR_HIGHLIGHT)。 要处理绘制函数,我们声明

    #include "GridCtrl_Src\GridCell.h"
    //.... 
    virtual BOOL Draw(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBkgnd = TRUE); 
    

    然后我们解析单元格文本中的 URL,如果它有一个 URL,我们用高亮颜色绘制单元格,否则我们正常绘制单元格。 我们在绘制之前存储单元格矩形,以便在尝试确定是否点击了 URL 时知道我们的边界。

    BOOL CGridURLCell::Draw(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBkgnd)
    {
    	// If URL is present then change text color
    	if (HasUrl(GetText()))
    		SetTextClr(m_clrUrl);
    
    	// Good a place as any to store the bounds of the rect
    	m_Rect = rect;
    
    	return CGridCell::Draw(pDC, nRow, nCol, rect, bEraseBkgnd);
    } 
    

    HasUrl(CString str) 在单元格文本中搜索已知的 URL 前缀之一。 我们将已知的前缀存储在 URLStruct 中,其中包含前缀和字符串长度。 URLStruct 按照从最常用到最不常用的顺序包含前缀。 HasUrl() 一次检索一个前缀,然后在检索下一个前缀之前搜索单元格文本。 如果找到 URL 前缀,则返回 TRUE,否则我们继续搜索,否则返回 FALSE。

    // Possible prefixes that indicate a hyperlink
    URLStruct CGridURLCell::g_szURIprefixes[] = { 
    	{ _T("www."), _tcslen(_T("www.")) },
    	{ _T("http:"), _tcslen(_T("http:")) },
    	{ _T("mailto:"), _tcslen(_T("mailto:")) },
    	{ _T("ftp:"), _tcslen(_T("ftp:")) },
    	{ _T("ftp."), _tcslen(_T("ftp.")) },
    	{ _T("https:"), _tcslen(_T("https:")) },
    	{ _T("news:"), _tcslen(_T("news:")) },
    	{ _T("gopher:"), _tcslen(_T("gopher:")) },
    	{ _T("telnet:"), _tcslen(_T("telnet:")) },
    	{ _T("url:"), _tcslen(_T("url:")) },
    	{ _T("file:"), _tcslen(_T("file:")) }
    };
    
    BOOL CGridURLCell::HasUrl(CString str)
    {
    	int nNumPrefixes = sizeof(g_szURIprefixes) / sizeof(g_szURIprefixes[0]);
    	for (int i = 0; i < nNumPrefixes; i++)
    		if (str.Find(g_szURIprefixes[i].szURLPrefix) >= 0)
    			return TRUE;
    
    	return FALSE;
    }
    

    要处理光标,我们在初始化中调用 GetHandCursor() 并将结果存储在 HCURSOR g_hLinkCursor 中。 我们重写 OnSetCursor(),以便如果我们在 URL 上方,则显示手形光标,否则显示普通光标。 这允许我们在 URL 单元格上方显示普通光标(如果不存在 URL)。 这首先通过获取光标点来实现,然后调用 OverURL(CPoint& pt, CString& strURL)OverUrl() 将点转换为单元格文本,然后搜索单元格,如果我们点击了 URL 或普通文本,则返回 TRUE 和 strUrl 中的 URL,或者返回 FALSE 和 strUrl 中的单元格文本。

    // Return TRUE if you set the cursor
    BOOL CGridURLCell::OnSetCursor()
    {
    #ifndef _WIN32_WCE
    	CString strURL;
    	CPoint pt(GetMessagePos());
    	GetGrid()->ScreenToClient(&pt);
    	pt = pt - m_Rect.TopLeft();
    
    	if (OverURL(pt, strURL))
    	{
    		SetCursor(g_hLinkCursor);
    		return TRUE;
    	}
    	else
    #endif
    		return CGridCell::OnSetCursor();
    }
    
    #ifndef _WIN32_WCE
    // "Default hand cursor" from Paul DiLascia's Jan 1998 MSJ article.
    HCURSOR CGridURLCell::GetHandCursor()
    {
    	if (g_hLinkCursor == NULL) // No cursor handle - load our own
    	{
    		// Get the windows directory
    		CString strWndDir;
    		GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
    		strWndDir.ReleaseBuffer();
    
    		strWndDir += _T("\\winhlp32.exe");
    		// This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
    		HMODULE hModule = LoadLibrary(strWndDir);
    		if( hModule )
    		{
    			HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
    			if( hHandCursor )
    			{
    				g_hLinkCursor = CopyCursor(hHandCursor);
    			}
    		}
    		FreeLibrary(hModule);
    	}
    
    	return g_hLinkCursor;
    }
    #endif
    
    // here we figure out if we are over a URL or not
    BOOL CGridURLCell::OverURL(CPoint& pt, CString& strURL)
    {
    	BOOL bOverURL = FALSE;
    	CSize size = GetTextExtent(GetText());
    
    	// Add left of cell so we know if we clicked on text or not
    	pt.x += m_Rect.left;
    	CPoint center = m_Rect.CenterPoint();
    
    	if ((m_nFormat & DT_RIGHT) && pt.x >= (m_Rect.right - size.cx))
    	{
    		bOverURL = TRUE;
    	} 
    	else if ((m_nFormat & DT_CENTER) && 
    		((center.x - (size.cx/2)) <= pt.x) && (pt.x <= (center.x + (size.cx/2))) )
    	{
    		bOverURL = TRUE;
    	}
    	else if (pt.x <= (size.cx + m_Rect.left))
    	{
    		bOverURL = TRUE;
    	}
    
    	if (!bOverURL)
    		return FALSE;
    
    	// We are over text - but are we over a URL?
    	bOverURL = FALSE;
    	strURL = GetText();
    
    	// Use float, otherwise we get an incorrect letter from the point
    	float width = (float)size.cx/(float)strURL.GetLength();
    
    	// remove left of cell so we have original point again 
    	pt.x -= m_Rect.left;
    	if (m_nFormat & DT_RIGHT)
    	{
    		int wide = m_Rect.Width() - size.cx;
    		pt.x -= wide;
    		if (pt.x <= 0)
    			return FALSE;
    	}
    
    	if (m_nFormat & DT_CENTER)
    	{
    		int wide = m_Rect.Width() - size.cx;
    		pt.x -= (wide/2);
    		if (pt.x <= 0 || pt.x > (size.cx + (wide/2)))
    			return FALSE;
    	}
    
    	// Turn point into a letter
    	int ltrs = (int)((float)pt.x/width);
    
    	// Find spaces before and after letter, process text between
    	int endSpace = strURL.Find(_T(' '), ltrs);
    	if (endSpace != -1)
    		strURL.Delete(endSpace, strURL.GetLength()-endSpace);
    
    	int beginSpace = strURL.ReverseFind(_T(' '));
    	if (beginSpace != -1)
    		strURL.Delete(0, ++beginSpace);
    
    	// Does text have URL
    	return HasUrl(strURL);
    }
    

    OnClick(CPoint PointCellRelative) 中,我们检查是否应该自动启动 URL,如果是,我们调用 OverUrl() 来检索要启动的 URL,然后将 URL 传递给 ShellExecute 以启动默认浏览器。

    void CGridURLCell::OnClick(CPoint PointCellRelative)
    {
    #ifndef _WIN32_WCE
    	CString strURL;
    	if (GetAutoLaunchUrl() && OverURL(PointCellRelative, strURL))
    		ShellExecute(NULL, _T("open"), strURL, NULL,NULL, SW_SHOW);
    #endif
    } 
    

    GetAutoLaunchUrl()SetAutoLaunchUrl(BOOL bLaunch = TRUE) 已被包含以允许我们切换 URL 的启动。 默认情况下,单击时会自动启动 URL。

    要使用单元格

    要使用新的单元格类,可以使用 CGridCtrl::SetCellType 设置特定单元格的单元格类型,或者您可以通过调用 CGridCtrl::SetDefCellType 将整个网格设置为使用 URL 单元格

    例如,要将单元格 (1,1) 设置为 URL 单元格,您需要确保包含了 CGridURLCell 类头文件,然后

    // Set the cell type
    m_Grid.SetCellType(1,1, RUNTIME_CLASS(CGridURLCell));
    
    // Set the cell properties
    CGridURLCell* cell = (CGridURLCell *)m_Grid.GetCell(1, 1);
    cell->...
    
    © . All rights reserved.