Click here to Skip to main content
15,847,963 members
Articles / Mobile Apps / Windows Mobile
Article

Adding Hyperlink support to the MFC Grid Control

,
Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
3 Jun 2000CPOL 109.7K   1.7K   50   9
A new class that adds hyperlink support to the MFC Grid Control
  • Download source files - 3 Kb
  • Introduction

    This article introduces a new cell class for the MFC Grid Control that provides hyperlinks in cells.

    The class

    To create the class we simply derive a cell from CGridCell. Deriving a cell CGridCell allows us to maintain the functionality of the normal cell, add add our own url support. For our new url cell, we will need to handle the draw, click, cursor, URL color, and URL parsing. We add a COLORREF variable to store the URL color. m_clrUrl will store our URL set by SetUrlColor(COLORREF clr) or the defualt color of GetSysColor(COLOR_HIGHLIGHT). To handle the draw function, we declare:

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

    We then we parse the cell text for a URL, and if it has a URL we draw the cell with our highlight color, otherwise we draw the cell as normal. We store the cell rect before drawing so that we know our bounds when trying to figure out if a URL was clicked.

    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) searches the cell text for one of the known URL prefixes. We store the known prefixes in a URLStruct with the prefixes and string length. The URLStruct contains the prefixes in order from most to least commonly used. HasUrl() retreives one prefix at a time, then searches the cell text before retreiving the next prefix. If a URL prefix is found, then TRUE is returned, else we keep searching and otherwise return 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;
    }

    To handle the cursor we call GetHandCursor() in our initialization and store the result in HCURSOR g_hLinkCursor. We override OnSetCursor() so that if we are over a URL then we show the hand cursor, otherwise the normal cursor if shown. This allows us to show the normal cursor over our URL cell, if a URL is not present. This is done by first getting the cursor point, then calling OverURL(CPoint& pt, CString& strURL). OverUrl() translates the point into cell text, then searches to cell if we clicked a URL or normal text, and returns TRUE with the URL in strUrl or else FALSE with the cell text in 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);
    }

    In OnClick(CPoint PointCellRelative) we check to see if we are supposed to launch a URL automatically, if so we call OverUrl() to retreive the URL to launch, then pass the URL for ShellExecute to launch the defualt browser.

    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() and SetAutoLaunchUrl(BOOL bLaunch = TRUE) have been included to allow us to toggle the luanching of URLs. By defualt, URLs are automatically launched when clicked.

    To use the cell

    To use the new cell class either set a particular cell's cell-type using CGridCtrl::SetCellType, or you can set the entire grid to use the URL cell by calling CGridCtrl::SetDefCellType

    For instance, to set cell (1,1) as a URL cell, you would ensure the CGridURLCell class header file was included, and then:

    // 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->...

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    Founder CodeProject
    Canada Canada
    Chris Maunder is the co-founder of CodeProject and ContentLab.com, and has been a prominent figure in the software development community for nearly 30 years. Hailing from Australia, Chris has a background in Mathematics, Astrophysics, Environmental Engineering and Defence Research. His programming endeavours span everything from FORTRAN on Super Computers, C++/MFC on Windows, through to to high-load .NET web applications and Python AI applications on everything from macOS to a Raspberry Pi. Chris is a full-stack developer who is as comfortable with SQL as he is with CSS.

    In the late 1990s, he and his business partner David Cunningham recognized the need for a platform that would facilitate knowledge-sharing among developers, leading to the establishment of CodeProject.com in 1999. Chris's expertise in programming and his passion for fostering a collaborative environment have played a pivotal role in the success of CodeProject.com. Over the years, the website has grown into a vibrant community where programmers worldwide can connect, exchange ideas, and find solutions to coding challenges. Chris is a prolific contributor to the developer community through his articles and tutorials, and his latest passion project, CodeProject.AI.

    In addition to his work with CodeProject.com, Chris co-founded ContentLab and DeveloperMedia, two projects focussed on helping companies make their Software Projects a success. Chris's roles included Product Development, Content Creation, Client Satisfaction and Systems Automation.

    Written By
    Web Developer
    United States United States
    Programming using MFC and ATL for almost 12 years now. Currently studying Operating System implementation as well as Image processing. Previously worked on DSP and the use of FFT for audio application. Programmed using ADO, ODBC, ATL, COM, MFC for shell interfacing, databasing tasks, Internet items, and customization programs.

    Comments and Discussions

     
    GeneralHand cursor Pin
    Griffter UK14-Jul-06 4:23
    Griffter UK14-Jul-06 4:23 
    GeneralRe: Hand cursor Pin
    Mattias G25-Nov-09 6:13
    Mattias G25-Nov-09 6:13 
    GeneralHyperlink support in Virtual Mode Pin
    Vinicius Pontes26-Feb-04 6:23
    Vinicius Pontes26-Feb-04 6:23 
    GeneralFind Spaces within a line of text Pin
    cypher2529-Oct-03 8:34
    cypher2529-Oct-03 8:34 
    QuestionHow to deal with <a href=link.htm>LinkText</a> ? Pin
    Ming Liu17-Aug-02 18:56
    Ming Liu17-Aug-02 18:56 
    AnswerRe: How to deal with &lt;a href=link.htm&gt;LinkText&lt;/a&gt; ? Pin
    Ming Liu17-Aug-02 19:00
    Ming Liu17-Aug-02 19:00 
    GeneralRe: How to deal with <a href=link.htm>LinkText</a> ? Pin
    Ming Liu17-Aug-02 19:11
    Ming Liu17-Aug-02 19:11 
    QuestionHow to get hyperlink on a column in listcontrol? Pin
    20-Mar-02 22:54
    suss20-Mar-02 22:54 
    AnswerRe: How to get hyperlink on a column in listcontrol? Pin
    Fred Ackers25-Mar-02 3:56
    Fred Ackers25-Mar-02 3:56 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.