Go4Expert

Go4Expert (http://www.go4expert.com/)
-   MFC (http://www.go4expert.com/articles/mfc-tutorials/)
-   -   Make CStatic into a Hyperlink (http://www.go4expert.com/articles/cstatic-hyperlink-t741/)

shabbir 19Apr2006 22:21

Make CStatic into a Hyperlink
 
3 Attachment(s)
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: CPP

#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: CPP

// 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: CPP

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: CPP

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: CPP

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: CPP

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.

parvez.yu 6Mar2008 15:12

Re: Make CStatic into a Hyperlink
 
i will try to execute the code


All times are GMT +5.5. The time now is 14:52.