Make CStatic into a Hyperlink

Discussion in 'MFC' started by shabbir, Apr 19, 2006.

  1. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    Whenever you write an application you tend to get default about dialog and you always tend to think if I can put up my contact details on it or my website URL to the website it will be awesome. At least I do think that way. After thinking when we tend to implement it in MFC we find that we have added the required information but its not user friendly i.e. clicking on the link does not open the website or mail client for sending emails. Thats because that URL is not a hyperlink. To create a Hyperlink all you need is a derived class from CStatic. I called it CHyperlink. Here is the code for the CHyperlink class.
    Code:
    #if !defined(AFX_HYPERLINK_H__A5017780_2E97_49C1_A0BA_833CA68EA820__INCLUDED_)
    #define AFX_HYPERLINK_H__A5017780_2E97_49C1_A0BA_833CA68EA820__INCLUDED_
    
    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // Hyperlink.h : header file
    //
    
    /////////////////////////////////////////////////////////////////////////////
    // CHyperlink window
    
    class CHyperlink : public CStatic
    {
    // Construction
    public:
    	CHyperlink();
    
    // Attributes
    public:
    
    // Operations
    public:
    
    // Overrides
    	// ClassWizard generated virtual function overrides
    	//{{AFX_VIRTUAL(CHyperlink)
    	public:
    	protected:
    	virtual void PreSubclassWindow();
    	//}}AFX_VIRTUAL
    
    // Implementation
    public:
    	virtual ~CHyperlink();
    
    	// Generated message map functions
    protected:
    	HCURSOR m_HypCursor;
    	CFont m_HypFont;
    	CString m_URL;
    	//{{AFX_MSG(CHyperlink)
    	afx_msg void OnPaint();
    	afx_msg UINT OnNcHitTest(CPoint point);
    	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
    	//}}AFX_MSG
    
    	DECLARE_MESSAGE_MAP()
    };
    
    /////////////////////////////////////////////////////////////////////////////
    
    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
    
    #endif // !defined(AFX_HYPERLINK_H__A5017780_2E97_49C1_A0BA_833CA68EA820__INCLUDED_)
    
    The class implementation is as follows.
    Code:
    // Hyperlink.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "HyperlinkSample.h"
    #include "Hyperlink.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    /////////////////////////////////////////////////////////////////////////////
    // CHyperlink
    
    
    /***********************************************************************/
    /// <b>Function: CHyperlink</b>
    ///
    ///	\param	NONE
    ///
    /// \remarks	Constructor
    ///
    /***********************************************************************/
    CHyperlink::CHyperlink()
    {
    	m_HypFont.CreateFont(15,0,0,0,FW_THIN,0,1,0,0,0,0,0,0,"Tahoma");
    	m_URL = _T("http://www.go4expert.com/");
    	m_HypCursor = NULL;
    }
    
    
    /***********************************************************************/
    /// <b>Function: ~CHyperlink</b>
    ///
    ///	\param	NONE
    ///
    /// \remarks	Destructor
    ///
    /***********************************************************************/
    CHyperlink::~CHyperlink()
    {
    	::DeleteObject(m_HypCursor);
    	::DeleteObject(m_HypFont);
    
    }
    
    
    BEGIN_MESSAGE_MAP(CHyperlink, CStatic)
    	//{{AFX_MSG_MAP(CHyperlink)
    	ON_WM_PAINT()
    	ON_WM_NCHITTEST()
    	ON_WM_LBUTTONUP()
    	ON_WM_SETCURSOR()
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    
    /////////////////////////////////////////////////////////////////////////////
    // CHyperlink message handlers
    
    
    /***********************************************************************/
    /// <b>Function: OnPaint</b>
    ///
    ///	\param	NONE
    ///
    /// \return	void 
    ///
    /// \remarks	WM_PAINT HANDLER
    ///
    /***********************************************************************/
    void CHyperlink::OnPaint() 
    {
    	// TODO: Add your message handler code here
    	
    	CPaintDC		dc (this);		// device context for painting
    	RECT			rectWnd;		// window rectangle
    	CString			strText;		// window text
    	
    	dc.SelectObject(&m_HypFont);
    	dc.SetBkMode(TRANSPARENT);
    
    	GetWindowText(strText);
    	GetClientRect(&rectWnd);
    	
    	dc.SetTextColor( RGB(0, 0, 255) );
    
    	/// Draw text
    	::DrawTextEx(dc.m_hDC, strText.GetBuffer(0), strText.GetLength(), &rectWnd, DT_LEFT, NULL);
    	strText.ReleaseBuffer();
    
    	// Do not call CStatic::OnPaint() for painting messages
    }
    
    
    /***********************************************************************/
    /// <b>Function: PreSubclassWindow</b>
    ///
    ///	\param	NONE
    ///
    /// \return	void 
    ///
    /// \remarks	Initialize variables specially the hand 
    ///				cursor before the window is created.
    ///				"Default hand cursor" from Paul DiLascia's Jan 1998 
    ///				MSJ article.
    ///
    /***********************************************************************/
    void CHyperlink::PreSubclassWindow() 
    {
    	// TODO: Add your specialized code here and/or call the base class
    	
        if (m_HypCursor == 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)
                    m_HypCursor = CopyCursor(hHandCursor);
            }
            FreeLibrary(hModule);
        }
    
    	CStatic::PreSubclassWindow();
    }
    
    
    /***********************************************************************/
    /// <b>Function: OnNcHitTest</b>
    ///
    /// \param	point	(in\out)
    ///
    /// \return	UINT 
    ///
    /// \remarks	Override WM_NCHITTEST message handler to handle 
    ///				the Mouse events to be handled by the Client and not 
    ///				the parent window.
    ///
    /***********************************************************************/
    UINT CHyperlink::OnNcHitTest(CPoint point) 
    {
    	// TODO: Add your message handler code here and/or call default
    	return HTCLIENT  ;
    	//return CStatic::OnNcHitTest(point);
    }
    
    
    /***********************************************************************/
    /// <b>Function: OnLButtonUp</b>
    ///
    /// \param	nFlags	(in\out)
    ///
    /// \param	point	(in\out)
    ///
    /// \return	void 
    ///
    /// \remarks	Launch the web browser to display the URL.
    ///
    /***********************************************************************/
    void CHyperlink::OnLButtonUp(UINT nFlags, CPoint point) 
    {
    	// TODO: Add your message handler code here and/or call default
    	HINSTANCE result = ShellExecute(NULL, _T("open"), m_URL, NULL,NULL, SW_SHOW);
    
    	CStatic::OnLButtonUp(nFlags, point);
    }
    
    
    /***********************************************************************/
    /// <b>Function: OnSetCursor</b>
    ///
    /// \param	pWnd	(in\out)
    ///
    /// \param	nHitTest	(in\out)
    ///
    /// \param	message	(in\out)
    ///
    /// \return	BOOL 
    ///
    /// \remarks	Change the cursor so that the cursor is always hand cursor
    ///
    /***********************************************************************/
    BOOL CHyperlink::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
    {
    	// TODO: Add your message handler code here and/or call default
    	if (m_HypCursor)
        {
            ::SetCursor(m_HypCursor);
            return TRUE;
        }
        return FALSE;
    	//return CStatic::OnSetCursor(pWnd, nHitTest, message);
    }
    
    I would explain a bit on the implementation of each of the function.
    Code:
    UINT CHyperlink::OnNcHitTest(CPoint point) 
    {
    	// TODO: Add your message handler code here and/or call default
    	return HTCLIENT  ;
    	//return CStatic::OnNcHitTest(point);
    }
    Override WM_NCHITTEST message handler to handle the Mouse events to be handled by the Client and not the parent window.
    Code:
    void CHyperlink::OnPaint() 
    {
    	// TODO: Add your message handler code here
    	
    	CPaintDC		dc (this);		// device context for painting
    	RECT			rectWnd;		// window rectangle
    	CString			strText;		// window text
    	
    	dc.SelectObject(&m_HypFont);
    	dc.SetBkMode(TRANSPARENT);
    
    	GetWindowText(strText);
    	GetClientRect(&rectWnd);
    	
    	dc.SetTextColor( RGB(0, 0, 255) );
    
    	/// Draw text
    	::DrawTextEx(dc.m_hDC, strText.GetBuffer(0), strText.GetLength(), &rectWnd, DT_LEFT, NULL);
    	strText.ReleaseBuffer();
    
    	// Do not call CStatic::OnPaint() for painting messages
    }
    We overwrite the PAINT message so that it displays the control in the hyperlink method. We change the FONT of the display as well as change the color of the text displayed. Here if you want you can further customize it for the color to be passed with the help of Get/Set method.
    Code:
    void CHyperlink::PreSubclassWindow() 
    {
    	// TODO: Add your specialized code here and/or call the base class
    	
        if (m_HypCursor == 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)
                    m_HypCursor = CopyCursor(hHandCursor);
            }
            FreeLibrary(hModule);
        }
    
    	CStatic::PreSubclassWindow();
    }
    We load the default cursor from the winhlp32.exe in the windows directory with the resource ID as 106.
    Code:
    BOOL CHyperlink::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
    {
    	// TODO: Add your message handler code here and/or call default
    	if (m_HypCursor)
        {
            ::SetCursor(m_HypCursor);
            return TRUE;
        }
        return FALSE;
    	//return CStatic::OnSetCursor(pWnd, nHitTest, message);
    }
    We change the cursor of the control to out loaded Hand cursor and we comment out the parent implementation of the OnSetCursor handler.

    Scope of improvements:
    1.You can generalized the class by giving a seperate color to the visited links and further customizing the WM_PAINT event
    2. Add the Get/Set methods for the font.
    3. Add the Get/Set methofs for the URL.
     

    Attached Files:

  2. parvez.yu

    parvez.yu New Member

    Joined:
    Feb 14, 2008
    Messages:
    100
    Likes Received:
    0
    Trophy Points:
    0
    i will try to execute the code
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice