65.9K
CodeProject 正在变化。 阅读更多。
Home

地址簿

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (20投票s)

1999年11月17日

CPOL

3分钟阅读

viewsIcon

310115

downloadIcon

5479

地址簿应用程序。

Sample Image

引言

地址簿演示了 GfxListCtrl 控件、CHyperlink 类、CSystemTray 类在一个实用应用程序中的使用。它还演示了下拉工具按钮以及使用模板类 CArray

尽管 CodeProject 上有很多程序都带有示例,但初学者很难学习如何在实际应用程序中使用它们,因为演示通常只展示该控件的功能。我决定编写一个程序,不仅演示控件的功能,还演示如何使其在你的程序中为你工作。

我将专注于 GfxListCtrl,因为我的程序是围绕它构建的。GfxListCtrl 由 Iuri Apollonio 编写。CHyperLinkCSystemTray 由 Chris Maunder 编写。

如何使用 Text callback 函数?

首先,我创建一个名为 CPerson 的类。此类包含有关该人的所有详细信息,并且重载了 Serialize 函数以保存和加载详细信息。

创建一个数组来保存 CPerson 对象,如 CAddressDoc 中所示:

CArray<CPerson,CPerson&> m_PersonArray;

这使得使用 TextCallBack 函数变得容易。你的 GfxListCtrl 的 TextCallBack 函数声明为:

void GetTextCallback(int iIndex, int iSubItem, long lCode, CString &cs)

其中 lCode 给出了所需的数组中的哪一项。iIndexiSubItem 分别给出了屏幕上的行和列。要获得实际的列,你将必须使用 GfxListControlGetColumnIndex() 成员函数,这意味着在你的视图类中,你将编写如下内容:

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); } }

对于 AutoPreview,你必须在函数中将你想要的文本绘制到屏幕上:

 long CAddressView::GetExInfoCallback(LXHDREX * pLx)

   :
   :
   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; }

其中 m_PersonArray[Index].strNotes 是我想让它显示的文本。

如何解决显示工具提示时 GfxListCtrl 的双击问题

要解决双击问题,只需对 CGfxListTip 类进行以下更改。

将样式 CS_DBLCLKS 添加到 WNDCLASS,如下所示:

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

并添加以下行:

case WM_LBUTTONDBLCLK: 

到函数

BOOL CGfxListTip::PreTranslateMessage(MSG* pMsg)

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

要在你的程序中使用双击功能,请从 GfxListCtrl 派生一个类。在我的程序中,该类是 CAddressCtrl

你必须重写 OnLButtonDblClk(UINT nFlags, CPoint point) 成员函数。将该点传递给 CGfxListCtrl::HitTestEx(CPoint & point, int * col) 成员函数,它将返回给你双击发生的屏幕上的物理行和列。你可以使用 GetItemTextEx(index,column,cs) 函数,其中 cs 是一个 CString 对象,以获取该项目的文本。如果你需要知道双击发生的实际列,就像我一样,那么你将必须使用:

column = pManager->FindColumnById(GetColumnIndex(column));

基于此,你可以采取所需的行动。在我的程序中,如果你双击电话号码,它会拨打该号码,或者双击 URL 会将你带到那里。这是代码:

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);
}

我修改了 Chris Maunder 的类 (CHyperLink),并将 GotoURLGetRegKey 成员函数设置为静态,以便可以直接调用它们,而无需创建该类的实例。tapiRequestMakeCall 是进行调用的 TAPI 函数。如果你想在你的项目中使用它,只需将 <tapi.h> 和 TAPI32.LIB 包含到你的项目中。

模板类 CArray 的序列化

为了加载和保存我的地址数组,我按如下方式序列化了 CArray 派生类:

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 ); } }
这使得我的 CDocument 类的序列化变得如此简单:
void CAddressDoc::Serialize(CArchive& ar)
{
   m_PersonArray.Serialize(ar);
}

你可以使用 /tray 作为命令行参数启动地址簿程序,以便在启动时将其放置到托盘。

如果你喜欢这个程序,请告诉我。如果你想要更多细节,请问我,我会尝试将其添加到此页面。

祝大家使用 Visual C++ 一切顺利
© . All rights reserved.