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





5.00/5 (7投票s)
一个为 MFC 网格控件添加超链接支持的新类。
引言
本文介绍了一个新的单元格类,用于 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->...