Introduction
Address
Book demonstrates the use of GfxListCtrl
control, CHyperlink
class, CSystemTray
class in one useful application. It also demonstrates
dropdown tool button and well as using the template
class CArray
.
Even though a lot of
programs come with samples,
beginners find it difficult to learn how to actually use
them in real application because the demos usually stick
to just displaying the capabilities of that control.
I decided to write a program which will not just
demonstrate the functionality of the control
but also how to make it work for you in your programs.
I will be focusing on
the GfxListCtrl
because my program is based
around it. GfxListCtrl
is written by Iuri
Apollonio. CHyperLink
and CSystemTray
are written by Chris Maunder.
How do I use the Text
callback function ?
Well, first of all I
create a class called CPerson
. This class
holds all the details about the person and the Serialize
function is overloaded to save and load the details.
An array is created to
hold the CPerson
objects as in CAddressDoc
as
CArray<CPerson,CPerson&> m_PersonArray;
This makes it easy to
use the TextCallBack function. Your TextCallBack
function for the GfxListCtrl
is declared as
void GetTextCallback(int iIndex, int iSubItem, long lCode, CString &cs)
where lCode
gives which item in the array
is required. iIndex and iSubItem gives you
the row and column respectively on the screen. To get
the actual column you will have to use GetColumnIndex()
member function of the GfxListControl
,
which means in your view class you would write something
like this
Collapse
void CAddressView::GetTextCallback(int iIndex,
int iSubItem, long lCode, CString &cs)
{
int rc = wndList.GetColumnIndex(
iSubItem); // wndList is the GfxListControl
CPerson person;
cs = "";
if (GetDocument()->m_PersonArray.GetSize() == 0 ||
GetDocument()->m_PersonArray.GetSize() < (iIndex + 1)) {
return;
}
person = GetDocument()->m_PersonArray[lCode - 1 ];
switch (rc) {
case 0: cs.Format("%d", iIndex + 1 ); break;
case ADDR_FIRST_NAME: cs = person.m_strFirstName; break;
case ADDR_LAST_NAME: cs = person.m_strLastName; break;
case ADDR_MIDDLE_NAME:cs = person.m_strMiddleName; break;
case ADDR_NAME: cs = person.m_strName; break;
case ADDR_NICK_NAME: cs = person.m_strNickName; break;
case ADDR_EMAIL: cs = person.m_strEMail; break;
default: cs.Format("%d, %d", lCode, rc);
}
}
For the AutoPreview
alone you have to draw
the text you want on to the screen in the function
long CAddressView::GetExInfoCallback(LXHDREX * pLx)
as
:
:
case NTEX_AUTOPREVIEW: {
LXHDREX_DIV * pLxEx = (LXHDREX_DIV *) pLx;
COLORREF ocr = pLxEx->pDC->SetTextColor(RGB(0,0,255));
pLxEx->pDC->DrawText(
GetDocument()->m_PersonArray[pLx->dwItemData - 1].m_strNotes ,
pLxEx->rcItem, DT_END_ELLIPSIS|DT_WORDBREAK);
pLxEx->pDC->SetTextColor(ocr);
return 1;
}
where m_PersonArray[Index].strNotes
is the text I want it to display.
How to solve the
double click problem of GfxListCtrl
when
the tooltip is displayed
To solve the double
click problem just make the following changes to the
class CGfxListTip
.
Add the style CS_DBLCLKS
to the WNDCLASS
as
CGfxListTip::CGfxListTip()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if(!(::GetClassInfo(hInst, GFXLISTTIP_CLASSNAME, &wndcls)))
{
wndcls.style = CS_DBLCLKS | CS_SAVEBITS ; // Xavier added CS_DBLCLKS
wndcls.lpfnWndProc = AfxWndProc; // Xavier changed from ::DefWindowProc
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
:
:
and add the line
case WM_LBUTTONDBLCLK:
to the function
BOOL CGfxListTip::PreTranslateMessage(MSG* pMsg)
as
BOOL CGfxListTip::PreTranslateMessage(MSG* pMsg)
{
CWnd *pWnd;
int hittest;
switch(pMsg->message)
{
case WM_LBUTTONDBLCLK: // Xavier added
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
POINTS pts = MAKEPOINTS(pMsg->lParam);
POINT point;
point.x = pts.x;
:
:
To use double click
features in your program derive a class from GfxListCtrl
.
In my program that class is CAddressCtrl
.
You have to override
the OnLButtonDblClk(UINT nFlags, CPoint point)
member function. Pass the point to CGfxListCtrl::HitTestEx(CPoint
& point, int * col)
member function and it
will return to you the physical row and column on the
screen at which the double click occurred. You can use
the GetItemTextEx(index,column,cs)
function
where cs is a CString
object to get
the text of that Item. If you need to know the actual
column on which the double click occurred like I do then
you will have to use
column = pManager->FindColumnById(GetColumnIndex(column));
Based on this you can
take the required action. In my program if you double
click on the phone number it dials that number or double
clicking on the URL will take you there. Here is the
code
Collapse
void CAddressCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
if( GetFocus() != this ) SetFocus();
int index, column;
if ((index = HitTestEx(point, &column)) != -1) {
if (column > 0 ) {
CString cs;
CString strURL;
CString strName;
CString strComment;
GetItemTextEx(index,column,cs);
if (pManager) {
column = pManager->FindColumnById(GetColumnIndex(column));
}
switch (column) {
case ADDR_EMAIL:
if (!cs.IsEmpty()) {
strURL.Format("mailto:%s",(LPCTSTR)cs);
CHyperLink::GotoURL(strURL,SW_SHOWNORMAL);
break;
}
case ADDR_PERSONAL_WEB_PAGE:
case ADDR_BUSINESS_WEB_PAGE:
if (!cs.IsEmpty()) {
if (strncmp((LPCTSTR) cs, "http://",7)==0)
strURL=cs;
else {
strURL.Format("http://%s",(LPCTSTR)cs);
}
CHyperLink::GotoURL(strURL,SW_SHOWNORMAL);
break;
}
case ADDR_HOME_PHONE:
case ADDR_BUSINESS_PHONE:
if (!cs.IsEmpty()) {
if ( column == ADDR_BUSINESS_PHONE)
strComment = "Business Number";
else
strComment = "Home Number";
GetItemTextEx(index,ADDR_NAME,strName);
if (tapiRequestMakeCall(cs,"Address",strName, strComment)!=0) {
AfxMessageBox("Unable to dial the number");
}
break;
}
default:
DisplayProperties();
}
}
}
CGfxListCtrl::OnLButtonDblClk(nFlags, point);
}
I modified Chris
Maunder's class (CHyperLink
)
and made the GotoURL
and GetRegKey
member functions as static so that they can be called
directly without the need to create an instance of the
class. tapiRequestMakeCall
is the TAPI
function to make a call. If you want to use it in your
project just include <tapi.h> and TAPI32.LIB into
your project.
Serializations of the
Template class CArray
To load and save my address
array I serialized the CArray
derived class as follows
template <>
void AFXAPI SerializeElements <CPerson> (
CArchive& ar, CPerson* pNewPersons, int nCount ) {
for ( int i = 0; i < nCount; i++, pNewPersons++ ) {
// Serialize each CPerson object
pNewPersons->Serialize( ar );
}
}
which makes the
serialization of my CDocument
class as
simple as
void CAddressDoc::Serialize(CArchive& ar)
{
m_PersonArray.Serialize(ar);
}
You can start the address
book program with /tray as a command
line parameter to tray it as it starts up.
If you like this
program please let me know. If you want anymore details
ucancode.net me I will it try to add it to this page.
Wishing you all the best
with Visual C++