Titletip for individual cells

To implement titletips we define a custom class for the titletip. This class is responsible for displaying the tips when needed. Within the mouse move handler of the listview control we simply call a member function of the CTitleTip object with the cell rectangle and the text to display within it. The CTitleTip object then determines whether the titletip should display. The title tip destroys itself whenever the mouse moves outside of the cell rectangle passed in as an argument or whenever the application loses focus.

Step 1: Define CTitleTip class

The listing for the CTitleTip class is given below. First the header file then the implementation file. This class is very generic and can be used with other controls and in other situations also.

In the constructor of CTitleTip we register the window class if it has not already been registered by another instance of the program. The background brush used for the class is COLOR_INFOBK. This is the same color used by the tooltip.

The Create() function follows the convention of other MFC classes. It is within this function that the window is created. The important thing to notice in this function is the window style. The WS_BORDER style draws a border around the titletip window. The WS_POPUP style is needed because we want the titletip window to be able to extend beyond the boundary of the listview control. If we do not specify this style, the window will be clipped at the control boundary and will not be very useful. The WS_EX_TOOLWINDOW style stops the window from appearing in the task bar. The WS_EX_TOPMOST style makes sure that the titletip is visible.

The Show() function gets repeatedly called by the client control, in this case the listview control. The primary task of Show() is to determine the text extent of the the titletext and display the titletip if the rectangle passed in as an argument is not big enough to display it completely. It also stores a rectangle which is used later to remove the titletip is the mouse moves outside this rectangle.

The handler for WM_MOUSEMOVE - OnMouseMove() - checks whether the mouse is within the cell rectangle that the titletip is being shown for. This rectangle is smaller than the titletip client area rectangle (else there would not have been any need to show the titletip). If the mouse is outside this rectangle, then the titletip is hidden and the WM_MOUSEMOVE or a WM_NCMOUSEMOVE message is passed on to the underlying window.

The tooltip also needs to be dismissed when the user presses a key or a mouse button. We override the PreTranslateMessage() to look for these messages. If any of these messages are received, the titletip is dismissed and an appropriate message passed on to the list view control.

#if !defined(AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_)
#define AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// TitleTip.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CTitleTip window

#define TITLETIP_CLASSNAME _T("ZTitleTip")


class CTitleTip : public CWnd
{
// Construction
public:
	CTitleTip();

// Attributes
public:

// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CTitleTip)
	public:
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	//}}AFX_VIRTUAL

// Implementation
public:
	void Show( CRect rectTitle, LPCTSTR lpszTitleText, int xoffset = 0);
	virtual BOOL Create( CWnd *pParentWnd);
	virtual ~CTitleTip();

protected:
	CWnd *m_pParentWnd;
	CRect m_rectTitle;


	// Generated message map functions
protected:
	//{{AFX_MSG(CTitleTip)
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations 
// immediately before the previous line.

#endif // !defined(AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_)







////////////////////////////////////////////////////////////////////////////
// TitleTip.cpp : implementation file
//

#include "stdafx.h"
#include "TitleTip.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTitleTip

CTitleTip::CTitleTip()
{
	// Register the window class if it has not already been registered.
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();
	if(!(::GetClassInfo(hInst, TITLETIP_CLASSNAME, &wndcls)))
	{
		// otherwise we need to register a new class
		wndcls.style = CS_SAVEBITS ;
		wndcls.lpfnWndProc = ::DefWindowProc;
		wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
		wndcls.hInstance = hInst;
		wndcls.hIcon = NULL;
		wndcls.hCursor = LoadCursor( hInst, IDC_ARROW );
		wndcls.hbrBackground = (HBRUSH)(COLOR_INFOBK + 1); 
		wndcls.lpszMenuName = NULL;
		wndcls.lpszClassName = TITLETIP_CLASSNAME;
		if (!AfxRegisterClass(&wndcls))
			AfxThrowResourceException();
	}
}

CTitleTip::~CTitleTip()
{
}


BEGIN_MESSAGE_MAP(CTitleTip, CWnd)
	//{{AFX_MSG_MAP(CTitleTip)
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CTitleTip message handlers

BOOL CTitleTip::Create(CWnd * pParentWnd)
{
	ASSERT_VALID(pParentWnd);

	DWORD dwStyle = WS_BORDER | WS_POPUP; 
	DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
	m_pParentWnd = pParentWnd;
	return CreateEx( dwExStyle, TITLETIP_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, 
		NULL, NULL, NULL );
}


// Show 		- Show the titletip if needed
// rectTitle		- The rectangle within which the original 
//			  title is constrained - in client coordinates
// lpszTitleText	- The text to be displayed
// xoffset		- Number of pixel that the text is offset from
//			  left border of the cell
void CTitleTip::Show(CRect rectTitle, LPCTSTR lpszTitleText, int xoffset /*=0*)
{
	ASSERT( ::IsWindow( m_hWnd ) );
	ASSERT( !rectTitle.IsRectEmpty() );

	// If titletip is already displayed, don't do anything.
	if( IsWindowVisible() ) 
		return;

	// Do not display the titletip is app does not have focus
	if( GetFocus() == NULL )
		return;

	// Define the rectangle outside which the titletip will be hidden.
	// We add a buffer of one pixel around the rectangle
	m_rectTitle.top = -1;
	m_rectTitle.left = -xoffset-1;
	m_rectTitle.right = rectTitle.Width()-xoffset;
	m_rectTitle.bottom = rectTitle.Height()+1;

	// Determine the width of the text
	m_pParentWnd->ClientToScreen( rectTitle );

	CClientDC dc(this);
	CString strTitle(lpszTitleText);
	CFont *pFont = m_pParentWnd->GetFont(); 	// use same font as ctrl
	CFont *pFontDC = dc.SelectObject( pFont );

	CRect rectDisplay = rectTitle;
	CSize size = dc.GetTextExtent( strTitle );
	rectDisplay.left += xoffset;
	rectDisplay.right = rectDisplay.left + size.cx + 3;

	// Do not display if the text fits within available space
	if( rectDisplay.right <= rectTitle.right-xoffset )
		return;

	// Show the titletip
	SetWindowPos( &wndTop, rectDisplay.left, rectDisplay.top, 
			rectDisplay.Width(), rectDisplay.Height(), 
			SWP_SHOWWINDOW|SWP_NOACTIVATE );

	dc.SetBkMode( TRANSPARENT );
	dc.TextOut( 0, 0, strTitle );
	dc.SelectObject( pFontDC );

	SetCapture();
}

void CTitleTip::OnMouseMove(UINT nFlags, CPoint point) 
{
	 if (!m_rectTitle.PtInRect(point)) {
		  ReleaseCapture();
		  ShowWindow( SW_HIDE );

		  // Forward the message
		  ClientToScreen( &point );
		  CWnd *pWnd = WindowFromPoint( point );
		  if ( pWnd == this ) 
			  pWnd = m_pParentWnd;
		  int hittest = (int)pWnd->SendMessage(WM_NCHITTEST,
						0,MAKELONG(point.x,point.y));
		  if (hittest == HTCLIENT) {
			   pWnd->ScreenToClient( &point );
			   pWnd->PostMessage( WM_MOUSEMOVE, nFlags, 
						MAKELONG(point.x,point.y) );
		  } else {
			   pWnd->PostMessage( WM_NCMOUSEMOVE, hittest, 
						MAKELONG(point.x,point.y) );
		  }
	 }
}

BOOL CTitleTip::PreTranslateMessage(MSG* pMsg) 
{
	CWnd *pWnd;
	int hittest ;
	switch( pMsg->message )
	{
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
		POINTS pts = MAKEPOINTS( pMsg->lParam );
		POINT  point;
		point.x = pts.x;
		point.y = pts.y;
		ClientToScreen( &point );
		pWnd = WindowFromPoint( point );
		if( pWnd == this ) 
			pWnd = m_pParentWnd;

		hittest = (int)pWnd->SendMessage(WM_NCHITTEST,
					0,MAKELONG(point.x,point.y));
		if (hittest == HTCLIENT) {
			pWnd->ScreenToClient( &point );
			pMsg->lParam = MAKELONG(point.x,point.y);
		} else {
			switch (pMsg->message) {
			case WM_LBUTTONDOWN: 
				pMsg->message = WM_NCLBUTTONDOWN;
				break;
			case WM_RBUTTONDOWN: 
				pMsg->message = WM_NCRBUTTONDOWN;
				break;
			case WM_MBUTTONDOWN: 
				pMsg->message = WM_NCMBUTTONDOWN;
				break;
			}
			pMsg->wParam = hittest;
			pMsg->lParam = MAKELONG(point.x,point.y);
		}
		ReleaseCapture();
		ShowWindow( SW_HIDE );
		pWnd->PostMessage(pMsg->message,pMsg->wParam,pMsg->lParam);
		return TRUE;		
	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
		ReleaseCapture();
		ShowWindow( SW_HIDE );
		m_pParentWnd->PostMessage( pMsg->message, pMsg->wParam, pMsg->lParam );
		return TRUE;
	}

	if( GetFocus() == NULL )
	{
		ReleaseCapture();
		ShowWindow( SW_HIDE );
		return TRUE;
	}

	return CWnd::PreTranslateMessage(pMsg);
}

Step 2: Define helper function CellRectFromPoint()

This function is primarily used to get the rectangle of the cell under the cursor. It also returns the row index of the cell. The basic strategy that this function uses is to first determine the row that the point falls in. Then it goes through each cell in that row to find which cell the point falls in.
// CellRectFromPoint	- Determine the row, col and bounding rect of a cell
// Returns		- row index on success, -1 otherwise
// point		- point to be tested.
// cellrect		- to hold the bounding rect
// col			- to hold the column index, can be NULL
int CMyListCtrl::CellRectFromPoint(CPoint & point, RECT * cellrect, int * col) const
{
	int colnum;

	// Make sure that the ListView is in LVS_REPORT
	if( (GetStyle() & LVS_TYPEMASK) != LVS_REPORT )
		return -1;

	// Get the top and bottom row visible
	int row = GetTopIndex();
	int bottom = row + GetCountPerPage();
	if( bottom > GetItemCount() )
		bottom = GetItemCount();
	
	// Get the number of columns
	CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
	int nColumnCount = pHeader->GetItemCount();

	// Loop through the visible rows
	for( ;row <=bottom;row++)
	{
		// Get bounding rect of item and check whether point falls in it.
		CRect rect;
		GetItemRect( row, &rect, LVIR_BOUNDS );
		if( rect.PtInRect(point) )
		{
			// Now find the column
			for( colnum = 0; colnum < nColumnCount; colnum++ )
			{
				int colwidth = GetColumnWidth(colnum);
				if( point.x >= rect.left && 
						point.x <= (rect.left + colwidth ) )
				{
					// Found the column
					RECT rectClient;
					GetClientRect( &rectClient );
					if( point.x > rectClient.right )
						return -1;
					if( col ) 
						*col = colnum;
					rect.right = rect.left + colwidth;
					if( rect.right > rectClient.right ) 
						rect.right = rectClient.right;
					*cellrect = rect;
					return row;
				}
				rect.left += colwidth;
			}
		}
	}
	return -1;
}

Step 3: Add handler for WM_MOUSEMOVE

The OnMouseMove() code uses the CellRectFromPoint() function to determine the row and column index and the sub-item rectangle. It then passes the rectangle and the item text information to the titletip object. It is the titletip object that decides whether it needs to be displayed.

The label text is always displayed with a small offset from the left edge of the cell. In the case of the first column this offset is equal to the width of one space character from the item image. In the case of other columns, this offset is twice the width of a space character. Of course, if the column is wide enough, the offset could be more depending on the text justification. I have used a hard coded value in the code below. It is better to compute and save this value in a member variable. This variable would have to be updated whenever the font changes.

void CMyListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	if( nFlags == 0 )
	{
		// To enable Title Tips
		int row, col;
		RECT cellrect;
		row = CellRectFromPoint(point, &cellrect, &col );
		if( row != -1 )
		{
			// offset is equal to TextExtent of 2 space characters.
			// Make sure you have the right font selected into the
			// device context before calling GetTextExtent.
			// You can save this value as a member variable.
			// offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
			int offset = 6;
			if( col == 0 ) 
			{
				CRect rcLabel;
				GetItemRect( row, &rcLabel, LVIR_LABEL );
				offset = rcLabel.left - cellrect.left + offset / 2;
			}
			cellrect.top--;
			m_titletip.Show( cellrect, GetItemText( row, col ), offset-1 );
		}
	}

	CListCtrl::OnMouseMove(nFlags, point);
}

Step 4: Create the titletip object

First add a member variable of the type CTitleTip to the class CListView or CListCtrl derived class. If you are using a CListCtrl derived class, override the PreSubclassWindow() function and add the code shown below. If you are using a CListView derived class instead, you can add this code to OnCreate() or OnInitialUpdate().
void CMyListCtrl::PreSubclassWindow() 
{
	CListCtrl::PreSubclassWindow();

	// Add initialization code
	m_titletip.Create( this );
}

Update for users of the new control (IE4)

The new listview control with IE4 among other things, supports dragging of the columns to rearrange their sequence. If you use that feature, the above code does not work. Mark Findlay ran acroos this problem and was kind enough to send us a fix. Here goes -

Handling title tips for list controls that support drag and drop using "Titletip for individual cells" by Zafir Anjum as a starting point.

The new CListCtrl allows for the drag and drop of columns. While this is a cool new feature, it also introduces a little more programmer responsibility to keep track of the column position. Fortunately the Column header item helps by maintaining the current column position as well as the original column position.

We will use this information to display title tips for columns which have been dragged to a new position.

Using the "Titletip for individual cells" by Zafir Anjum, we add 2 additional functions:

GetTrueItemText() and GetTrueColumnWidth().

Add the following function prototypes anywhere they will be accessible to the OnMouseMove() and CellRectFromPoint() functions.

Note that you might need to modify the implementations of these functions if you place them in a non-CListView derived class as I have coded them.

    
    CString GetTrueItemText( int row, int col );
    int GetTrueColumnWidth(int nCurrentPosition);

Add the following function implementations for the prototypes:

First - the GetTrueItemText

//**************************************************
CString CMyListView::GetTrueItemText(int row, int col)
{
    CListCtrl& ctlList = GetListCtrl();

    // Get the header control 
	CHeaderCtrl* pHeader = (CHeaderCtrl*)ctlList.GetDlgItem(0);
    _ASSERTE(pHeader);

    // get the current number of columns 
    int nCount = pHeader->GetItemCount();

    // find the actual column requested. We will compare
    // against hi.iOrder
    for (int x=0; x< nCount; x++)
    {
        HD_ITEM hi = {0};
        hi.mask = HDI_ORDER;

        BOOL bRet = pHeader->GetItem(x,&hi);
        _ASSERTE(bRet);
        if (hi.iOrder == col)
        {
            // Found it, get the associated text
            return ctlList.GetItemText(row,x);
        }
    }

    _ASSERTE(FALSE);
    return "We better never fall through to here!";

}

Next the GetTrueColumnWidth

//**************************************************************
int CMyListView::GetTrueColumnWidth(int nCurrentPosition)
{
    CListCtrl& ctlList = GetListCtrl();

	CHeaderCtrl* pHeader = (CHeaderCtrl*)ctlList.GetDlgItem(0);
    _ASSERTE(pHeader);

    int nCount = pHeader->GetItemCount();

    for (int x=0; x< nCount; x++)
    {
        HD_ITEM hi = {0};
        hi.mask = HDI_WIDTH | HDI_ORDER;

        BOOL bRet = pHeader->GetItem(x,&hi);
        _ASSERTE(bRet);
        if (hi.iOrder == nCurrentPosition)
            return hi.cxy;
    }

    _ASSERTE(FALSE);
    return 0; // We would never fall through to here!

}

Then all we need to do is

1) modify the CMyListCtrl::OnMouseMove() function:
replace the call to GetItemText() with GetTrueItemText().

2) modify the CMyListCtrl::OnMouseMove() function:
comment out the following code:

			/*if( col == 0 ) 
			{
				CRect rcLabel;
				ctlList.GetItemRect( row, &rcLabel, LVIR_LABEL );
				offset = rcLabel.left - cellrect.left + offset / 2;
			}*

3) modify the CellRectFromPoint() function:
replace the GetColumnWidth() call with GetTrueColumnWidth().



Handle double click in TitleTips

Here is another enhancement that Mark Findlay sent in.

The TitleTips helper written by Zafir Anjum is a cool tool that allows you to show titletips for individual CListCtrl cells. The CTitleTip PreTranslateMessage handler traps mouse clicks for tip display purposes but this stifles the mouse double-click message since each individual mouse click is handled.

With a minor modification the CTitleTips helper can also support mouse double-clicks (WM_LBUTTONDBLCLK) messages.

To modify the CTitleTips helper:

1) Create a member variable in the CTitleTips header file

    DWORD m_dwLastLButtonDown

2) In the CTitleTips constructor, initialize m_dwLastLButtonDown to zero.

3) Add the following #define to the CTitleTip::PreTranslateMessage function:

    #define DBLCLICK_MILLISECONDS 150

The DBLCLICK_MILLISECONDS specifies the duration between single mouse clicks that CTitleTips should handle as a double-click instead of 2 single clicks. The 150 is a pretty good setting (.15 of a second between clicks will constitute a double-click) but you can change this to suit your needs/taste

4) Add the following local variables to the beginning of the CTitleTip::PreTranslateMessage function:

    // Used to qualify WM_LBUTTONDOWN messages as double-clicks
    DWORD dwTick=0;
    BOOL  bDoubleClick=FALSE;

5) Insert the following code for the 1st "case WM_LBUTTONDOWN:" at the beginning of the function:

    // Get tick count since last LButtonDown
    dwTick = GetTickCount();
    bDoubleClick = ((dwTick - m_dwLastLButtonDown) <= DBLCLICK_MILLISECONDS);
    m_dwLastLButtonDown = dwTick;
    // NOTE: DO NOT ADD break; STATEMENT HERE! Let code fall through

6) Just above the case WM_KEYDOWN handler, alter the pWnd->PostMessage function:

        // If this is the 2nd WM_LBUTTONDOWN in x milliseconds,
        // post a WM_LBUTTONDBLCLK message instead of a single click.
		pWnd->PostMessage(
            bDoubleClick ? WM_LBUTTONDBLCLK : pMsg->message,
            pMsg->wParam,
            pMsg->lParam);

That's all there is to it. Below is an example of this.
//************************************************************************
BOOL CTitleTip::PreTranslateMessage(MSG* pMsg) 
{
// Number of elapsed milliseconds between WM_LBUTTONDOWN messages to 
// qualify as a double-click
#define DBLCLICK_MILLISECONDS 150

    // Used to qualify WM_LBUTTONDOWN messages as double-clicks
    DWORD dwTick=0;
    BOOL  bDoubleClick=FALSE;

	CWnd *pWnd;
	int hittest ;
	switch( pMsg->message )
	{

	case WM_LBUTTONDOWN:
        // Get tick count since last LButtonDown
        dwTick = GetTickCount();
        bDoubleClick = ((dwTick - m_dwLastLButtonDown) <= DBLCLICK_MILLISECONDS);
        m_dwLastLButtonDown = dwTick;
        // NOTE: DO NOT ADD break; STATEMENT HERE! Let code fall through
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:

		POINTS pts = MAKEPOINTS( pMsg->lParam );
		POINT  point;
		point.x = pts.x;
		point.y = pts.y;
		ClientToScreen( &point );
		pWnd = WindowFromPoint( point );
		if( pWnd == this ) 
			pWnd = m_pParentWnd;

		hittest = (int)pWnd->SendMessage(WM_NCHITTEST,
					0,MAKELONG(point.x,point.y));
		if (hittest == HTCLIENT) 
        {
			pWnd->ScreenToClient( &point );
			pMsg->lParam = MAKELONG(point.x,point.y);
		} 
        else 
        {
			switch (pMsg->message) 
            {
			case WM_LBUTTONDOWN: 
				pMsg->message = WM_NCLBUTTONDOWN;
				break;
			case WM_RBUTTONDOWN: 
				pMsg->message = WM_NCRBUTTONDOWN;
				break;
			case WM_MBUTTONDOWN: 
				pMsg->message = WM_NCMBUTTONDOWN;
				break;
			}
			pMsg->wParam = hittest;
			pMsg->lParam = MAKELONG(point.x,point.y);
		}
		ReleaseCapture();
		ShowWindow( SW_HIDE );

        // If this is the 2nd WM_LBUTTONDOWN in x milliseconds,
        // post a WM_LBUTTONDBLCLK message instead of a single click.
		pWnd->PostMessage(
            bDoubleClick ? WM_LBUTTONDBLCLK : pMsg->message,
            pMsg->wParam,
            pMsg->lParam);

		return TRUE;
        
	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
		ReleaseCapture();
		ShowWindow( SW_HIDE );
		m_pParentWnd->PostMessage( pMsg->message, pMsg->wParam, pMsg->lParam );
		return TRUE;
	}

	if( GetFocus() == NULL )
	{
		ReleaseCapture();
		ShowWindow( SW_HIDE );
		return TRUE;
	}

	return CWnd::PreTranslateMessage(pMsg);
}



Comments

  • HELP: Program CRASHES when creating dynamic ListCtrl with TitleTip in VC6

    Posted by Legacy on 11/08/1999 12:00am

    Originally posted by: Mohsen Samad-Yazdchi

    I'm trying to create a dynamic ListCtrl, but when I use MyListCtrl.Create(.....), the statement

    m_titletip.Create(this);

    within PreSubclassWindow() function causes the program to crash.

    PS. I'm Using VC6.

    Any help is welcome
    Regards
    Mohsen

    Reply
  • How can I make a long message wrap around to additional lines?

    Posted by Legacy on 10/22/1999 12:00am

    Originally posted by: Jason Douglas

    The application I'm writing has some extremely large fields that I need to pop-up to the user when the mouse moves over them. My problem, however, is that the CTitleTip just runs the text off the right side of the screen.

    Is there any way to make the text wrap? I've tried changing the derivation from CWnd to CStatic or CEdit (hoping to inherit some nice wrap-around feature), with no luck. I can get the text box to draw itself to the proper dimensions, but the text still just runs off the first line with no wrap-around at all.

    Please help.

    Thanks,
    Jason

    Reply
  • thread errors when accessed via different DLL?

    Posted by Legacy on 10/06/1999 12:00am

    Originally posted by: Chris Johnson

    Hello,
    
    

    Nice list control w/ titletips. I was using it without problems, until I tried to move the list as well as titletip classes to a seperate utilities DLL. They're already being used in a DLL, but I wanted to move them to a different general-purpose DLL so other developers can use them. I ended up getting an exception in MFC. Here's the stack at the time of the error, which occurs during the initial UpdateData in CListCtrlEx::PreSubclassWindow():

    CThreadLocalObject::GetData(CNoTrackObject * (void)* 0x5f4908d0 CThreadLocal<AFX_MODULE_THREAD_STATE>::CreateObject(void)) line 389 + 3 bytes
    CThreadLocal<AFX_MODULE_THREAD_STATE>::GetData() line 173 + 13 bytes
    AfxGetModuleThreadState() line 254
    afxMapHWND(int 0x00000000) line 264 + 5 bytes
    CWnd::FromHandlePermanent(HWND__ * 0x00011496) line 298 + 7 bytes
    _AfxCbtFilterHook(int 0x00000003, unsigned int 0x00011496, long 0x0012d804) line 530 + 9 bytes
    USER32! 77e72067()
    USER32! 77e83b11()
    NTDLL! 77f7637b()
    USER32! 77e76dbb()
    CWnd::CreateEx(unsigned long 0x00000088, const char * 0x00bf0350 ??_C@_09NIJK@ZTitleTip?$AA@, const char * 0x00000000, unsigned long 0x80800000, int 0x00000000, int 0x00000000, int 0x00000000, int 0x00000000, HWND__ * 0x00000000, HMENU__ * 0x00000000, void * 0x00000000) line 694 + 54 bytes
    CTitleTip::Create(CWnd * 0x0012f18c {CListCtrlEx hWnd=???}) line 101
    CListCtrlEx::PreSubclassWindow() line 601 + 24 bytes
    CWnd::SubclassWindow(HWND__ * 0x0001148e) line 3852
    DDX_Control(CDataExchange * 0x0012dbc0, int 0x00001f5c, CWnd & {CWnd hWnd=0x0001148e}) line 628 + 12 bytes
    CCalculatorPage::DoDataExchange(CDataExchange * 0x0012dbc0) line 52
    CWnd::UpdateData(int 0x00000000) line 3109
    CDialog::OnInitDialog() line 680 + 10 bytes
    CCalculatorPage::OnInitDialog() line 94
    AfxDlgProc(HWND__ * 0x00011452, unsigned int 0x00000110, unsigned int 0x00000000, unsigned int 0x00000000) line 35 + 14 bytes
    USER32! 77e80cbb()
    USER32! 77e83451()
    USER32! 77e72ba5()
    USER32! 77e72b78()
    [...deleted...]

    Anyone know of a fix, or why this is happening? I'm not knowingly messing around with threads in my code.

    I can move the listctrlex class back into the DLL in which it's used, and leave the CTitleTip class in the Utilities DLL and it works fine. Weird!

    If anyone wants more info, let me know. Thanks, --cj

    Reply
  • thread errors when accessed via different DLL? (WRAPPED!)

    Posted by Legacy on 10/06/1999 12:00am

    Originally posted by: Chris Johnson

    (sorry about that last post... anyone know how to edit your own posts like the sub form boasts about?)

    Hello,

    Nice list control w/ titletips. I was using it without problems, until I tried to move the list as well as titletip classes to a seperate utilities DLL. They're already being used in a DLL, but I wanted to move them to a different general-purpose DLL so other developers can use them. I ended up getting an exception in MFC. Here's the stack at the time of the error, which occurs during the initial UpdateData in CListCtrlEx::PreSubclassWindow():

    CThreadLocalObject::GetData(CNoTrackObject * (void)* 0x5f4908d0 CThreadLocal<AFX_MODULE_THREAD_STATE>::CreateObject(void)) line 389 + 3 bytes
    CThreadLocal<AFX_MODULE_THREAD_STATE>::GetData() line 173 + 13 bytes
    AfxGetModuleThreadState() line 254
    afxMapHWND(int 0x00000000) line 264 + 5 bytes
    CWnd::FromHandlePermanent(HWND__ * 0x00011496) line 298 + 7 bytes
    _AfxCbtFilterHook(int 0x00000003, unsigned int 0x00011496, long 0x0012d804) line 530 + 9 bytes
    USER32! 77e72067()
    USER32! 77e83b11()
    NTDLL! 77f7637b()
    USER32! 77e76dbb()
    CWnd::CreateEx(unsigned long 0x00000088, const char * 0x00bf0350 ??_C@_09NIJK@ZTitleTip?$AA@, const char * 0x00000000, unsigned long 0x80800000, int 0x00000000, int 0x00000000, int 0x00000000, int 0x00000000, HWND__ * 0x00000000, HMENU__ * 0x00000000, void * 0x00000000) line 694 + 54 bytes
    CTitleTip::Create(CWnd * 0x0012f18c {CListCtrlEx hWnd=???}) line 101
    CListCtrlEx::PreSubclassWindow() line 601 + 24 bytes
    CWnd::SubclassWindow(HWND__ * 0x0001148e) line 3852
    DDX_Control(CDataExchange * 0x0012dbc0, int 0x00001f5c, CWnd & {CWnd hWnd=0x0001148e}) line 628 + 12 bytes
    CCalculatorPage::DoDataExchange(CDataExchange * 0x0012dbc0) line 52
    CWnd::UpdateData(int 0x00000000) line 3109
    CDialog::OnInitDialog() line 680 + 10 bytes
    CCalculatorPage::OnInitDialog() line 94
    AfxDlgProc(HWND__ * 0x00011452, unsigned int 0x00000110, unsigned int 0x00000000, unsigned int 0x00000000) line 35 + 14 bytes
    USER32! 77e80cbb()
    USER32! 77e83451()
    USER32! 77e72ba5()
    USER32! 77e72b78()
    [...deleted...]

    Anyone know of a fix, or why this is happening? I'm not knowingly messing around with threads in my code.

    I can move the listctrlex class back into the DLL in which it's used, and leave the CTitleTip class in the Utilities DLL and it works fine. Weird!

    If anyone wants more info, let me know. Thanks, --cj

    Reply
  • Titletips uses SetCapture, so no keyboard accelerators when shown

    Posted by Legacy on 02/16/1999 12:00am

    Originally posted by: Brad Kremer

    This code works great. The only problem is that if you have accelerator defined, they
    won't get called when the titletip is shown. This is because the SetCapture call. When
    it is active, no accelerators get through.

    I haven't thought of a cool work around yet. I was thinking that instead of the SetCapture,
    I might setup a timer event to check the mouse position and hide/show the titletip.

    If any of you have some insight on this, let me know.

    Reply
  • Instead of # defining DBLCLICK_MILLISECONDS

    Posted by Legacy on 01/07/1999 12:00am

    Originally posted by: Michael Pryor

    If developing for Win95 or later, when adding double click support using the suggested code, you should probably call GetDoubleClickTime to get the actual double click interval instead of relying on an estimate.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Hundreds of millions of users have adopted public cloud storage solutions to satisfy their Private Online File Sharing and Collaboration (OFS) needs. With new headlines on cloud privacy issues appearing almost daily, the need to explore private alternatives has never been stronger. Join ESG Senior Analyst Terri McClure and Connected Data in this on-demand webinar to take a look at the business drivers behind OFS adoption, how organizations can benefit from on-premise deployments, and emerging private OFS …

Most Popular Programming Stories

More for Developers

RSS Feeds