Visual C++程序开发范例宝典(软件工程师典藏版)
上QQ阅读APP看书,第一时间看更新

1.5 导航界面应用实例

如今的应用软件不但具有丰富的功能,还应具有人性化的界面,这样,才能吸引用户。本节主要介绍几种常用的导航界面。

实例014 Outlook导航界面

本实例可以方便操作、提高效率

实例位置:光盘\mingrisoft\01\014

实例说明

采用导航式功能菜单,不但美观大方,而且为用户操作程序提供了方便。下面介绍Outlook导航界面式菜单的设计方法。运行程序,Outlook式导航界面的效果如图1.18所示。

图1.18 Outlook导航界面

技术要点

为了设计Outlook导航界面,需要从CListCtrl派生一个子类,本实例为COutlookList。在该类中显示一些导航按钮。当用户单击这些按钮时,会适当调整按钮的位置,并在客户区域(除按钮占用区域之外的区域)显示另一个CListCtrl控件,本实例为m_ClientList,目的是显示与导航按钮关联的项目。在设计COutlookList类时,需要解决几个关键问题:一是如何截获导航按钮的单击事件,并确定用户单击了哪个按钮;二是如何存储与导航按钮关联的项目;三是如何向外界提供一个接口,以方便处理用户双击视图项执行的动作。

对于问题一,可以在COutlookList的OnCmdMsg方法中实现,代码如下:

        BOOL COutlookList::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO*
        pHandlerInfo)
        {
              int index=  CommandToIndex(nID);
              if (index != -1)
              {
            OnButtonDown(index,nID);
              }
              m_ClientList.OnCmdMsg(nID,nCode,pExtra,pHandlerInfo);
              return CListCtrl::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
        }

在OnCmdMsg方法中首先调用自定义的CommandToIndex方法获取命令对应的按钮索引,因为在创建导航按钮时,会为按钮指定ID,并将按钮存储在m_pButton按钮数组中,只要遍历m_pButton,就可以根据按钮ID确定索引了。如果导航按钮索引不为-1,表示用户单击了导航按钮,执行自定义的OnButtonDown方法,重新排列按钮,在客户区域显示列表控件。

对于问题二,可以从CButton派生一个子类(本实例为CListButton),在该类中定义一个字符串列表m_ButtonItems(类型为CStringList),存储与导航按钮关联的项目文本。

对于问题三,可以定义一个回调函数,本实例为ItemDlbFun,代码如下:

        //定义双击列表视图项的回调函数
        typedef void(ItemDlbFun)(const CListCtrl* pListCtrl,int nItemIndex);

然后在COutlookList类中定义一个ItemDlbFun函数指针pItemDlbFun。最后在COutlookList类的PreTranslateMessage方法中判断用户是否双击了视图项,如果是,则调用pItemDlbFun。

        BOOL COutlookList::PreTranslateMessage(MSG* pMsg)
        {
              if ((pMsg->hwnd==m_ClientList.m_hWnd)&&(pMsg->message==WM_LBUTTONDBLCLK))
              {
            int index;
            index=m_ClientList.GetSelectionMark();         //获取用户双击的项目
            if (pItemDlbFun!= NULL)
              pItemDlbFun(&m_ClientList,index);
              }
              return CListCtrl::PreTranslateMessage(pMsg);
        }

实现过程

(1)新建一个基于对话框的应用程序。

(2)在对话框中添加列表视图控件和图片控件。设置图片控件属性如图1.19所示。

(3)从CListCtrl类派生一个子类COutlookList,在该类中定义如下成员变量:

            CPtrArray m_pButton;                    //按钮数组
            UINT m_ButtonCount;                     //按钮数量
            UINT m_LeftMargin;                      //图像列表显示的左边距
            CListCtrl m_ClientList;                 //在客户区域显示视图项
            ItemDlbFun*pItemDlbFun;                //视图项的双击事件

图1.19 图片控件属性设置

(4)从CButton类派生一个子类CListButton,作为导航按钮。

(5)在CListButton中定义如下成员变量:

            UINT m_Index;                           //导航按钮索引
            BOOL m_Toped;                           //按钮是否在列表上方
            CStringList m_ButtonItems;              //按钮关联的图像列表项

(6)在COutlookList中添加AddButton方法,用于添加导航按钮,代码如下:

        void COutlookList::AddButton(LPCSTR Btntext,UINT uID)
        {
            int index = m_pButton.Add(new CListButton);
            //创建按钮控件
            ((CListButton*)m_pButton[m_pButton.GetSize()-1])->Create(Btntext,WS_CHILD,GetAddButtonRect(),this,uID);
            ((CListButton*)m_pButton[m_pButton.GetSize()-1])->m_Index=index;         //设置按钮索引
            ((CListButton*)m_pButton[m_pButton.GetSize()-1])->ShowWindow(SW_SHOW); //显示控件
            m_ButtonCount+=1;                                                        //统计按钮数量
        }

(7)在COutlookList中添加AddButtonItems方法,用于设置导航按钮关联的项目,代码如下:

        void COutlookList::AddButtonItems(UINT nIndex, CString &strItem)
        {
            CListButton* temp;
            temp=(CListButton*)m_pButton[nIndex];            //获得按钮指针
            temp->m_ButtonItems.AddTail(strItem);            //记录列表项
        }

(8)在COutlookList中添加ShowButtonItems方法,用于显示指定导航按钮关联的项目,代码如下:

        void COutlookList::ShowButtonItems(UINT nIndex)
        {
          CListButton* temp;
          temp=(CListButton*)m_pButton[nIndex];            //获取指定的导航按钮
          m_ClientList.DeleteAllItems();                   //清空列表控件
          CRect showrect=GetListClientRect();               //获取客户区域
          if (temp->m_ButtonItems.GetCount()>0)
          {
            POSITION pos;
            pos=temp->m_ButtonItems.GetHeadPosition();                         //列表项索引
            CString str=temp->m_ButtonItems.GetHead();                          //获得列表项文本
            CRect ClientRect;
            ClientRect=GetListClientRect();                                    //获得列表区域
            int m = 0;
            m_ClientList.InsertItem(m,str,m);                                  //插入列表项
            m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));      //设置列表项位置
            while (pos != temp->m_ButtonItems.GetTailPosition())
            {
            str=temp->m_ButtonItems.GetNext(pos);                              //获得下一个列表项文本
            m_ClientList.InsertItem(m,str,m);                                  //插入列表项
            m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));      //设置列表项位置
            m+=1;
            }
            str=temp->m_ButtonItems.GetAt(pos);                                //获得文本
            m_ClientList.InsertItem(m,str,m);                                  //插入列表项
            m_ClientList.SetItemPosition(m,CPoint(m_LeftMargin,20+m*48));      //设置列表项位置
          }
        }

举一反三

根据本实例,读者可以:

利用TreeControl控件制作导航界面。

实例015 树状导航界面

本实例可以美化界面、简化操作

实例位置:光盘\mingrisoft\01\015

实例说明

树状导航界面很常见,系统中资源浏览器的左边就是一个树状的导航界面。本实例利用两层树型视图来实现树状导航界面,用户可以通过双击树型视图的单个项实现某些功能,如图1.20所示。

图1.20 树状导航界面

技术要点

本实例通过窗体的分割技术将树型视图显示在左边,然后通过树型视图的GetTreeCtrl方法来获得CTreeCtrl对象,通过CTreeCtrl对象接受双击事件来实现导航功能。

实现过程

(1)新建名为TreeNavi的单文档应用程序。

(2)在工程中添加以CTreeView类为基类的新类TreeView;在工程中添加Icon资源。

(3)在TreeView.h文件中添加如下代码:

#include "afxcview.h"

(4)修改TreeNavi.cpp文件中InitInstance函数的内容,代码如下:

        BOOL CTreeNaviApp::InitInstance()
        {
            …//此处代码省略
            CSingleDocTemplate* pDocTemplate;
            pDocTemplate = new CSingleDocTemplate(
              IDR_MAINFRAME,
              RUNTIME_CLASS(CTreeNaviDoc),
              RUNTIME_CLASS(CMainFrame),
            //去除原有的TreeNaviView视图的应用
            //    RUNTIME_CLASS(CTreeNaviView)
                  NULL);
            AddDocTemplate(pDocTemplate);
        …//此处代码省略
            return TRUE;
        }

(5)在MainFrm.cpp文件中添加WM_CREATECLIENT消息的实现函数,实现分割窗体,代码如下:

        BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
        {
            split.CreateStatic(this,1,2);                                                  //创建分割窗体
            split.CreateView(0,0,RUNTIME_CLASS(TreeView),CSize(100,100),pContext);         //创建左侧子视图
            split.CreateView(0,1,RUNTIME_CLASS(CTreeNaviView),CSize(100,100),pContext);    //创建右侧子视图
            return CFrameWnd::OnCreateClient(lpcs, pContext);
        }

(6)函数OnInitialUpdate实现树型视图的初始化,代码如下:

            void TreeView::OnInitialUpdate()
            {
            CTreeView::OnInitialUpdate();
            list.Create(32,32,ILC_COLOR32|ILC_MASK,0,0);           //创建图像列表
            list.Add(::AfxGetApp()->LoadIcon(IDI_ICON1));          //加载图标资源
            …//此处代码省略
            this->GetTreeCtrl().SetImageList(&list,TVSIL_NORMAL);   //关联图像列表
            HTREEITEM tree;
            //创建树状结构
            tree=this->GetTreeCtrl().InsertItem("导航1",0,1);
            this->GetTreeCtrl().InsertItem("子导航1",6,6,tree);
            this->GetTreeCtrl().InsertItem("子导航2",7,7,tree);
            this->GetTreeCtrl().InsertItem("子导航3",8,8,tree);
            tree=this->GetTreeCtrl().InsertItem("导航2",2,3);
            this->GetTreeCtrl().InsertItem("子导航4",9,19,tree);
            this->GetTreeCtrl().InsertItem("子导航5",10,10,tree);
            this->GetTreeCtrl().InsertItem("子导航6",11,11,tree);
            tree=this->GetTreeCtrl().InsertItem("导航3",4,5);
            this->GetTreeCtrl().InsertItem("子导航7",12,12,tree);
            this->GetTreeCtrl().InsertItem("子导航8",13,13,tree);
            this->GetTreeCtrl().InsertItem("子导航9",14,14,tree);
        }

(7)添加NM_DBLCLK消息的实现函数OnDblclk,代码如下:

        void TreeView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
        {
            HTREEITEM tree=this->GetTreeCtrl().GetSelectedItem();
            if(!this->GetTreeCtrl().GetChildItem(tree))
            {
            CString str=this->GetTreeCtrl().GetItemText(tree);      //获得树节点文本
            AfxMessageBox(str);                                    //弹出消息提示
            }
            *pResult = 0;
        }

举一反三

根据本实例,读者可以:

实现列表视图导航。

实例016 按钮导航界面

本实例可以美化界面、简化操作

实例位置:光盘\mingrisoft\01\016

实例说明

一般应用程序都使用菜单作为导航,用户通过操作菜单来实现某些功能。本实例使用多个按钮来作为用户操作的引导,效果如图1.21所示。

技术要点

为了使界面漂亮,本实例以CButton类为基类派生一个子类CCustomButton,然后声明一个枚举变量,用于设置按钮控件的3种状态,分别是正常状态、热点状态、按下状态,在CCustomButton类的WM_MOUSEMOVE事件中设置按钮是否为热点状态,在WM_LBUTTONDOWN事件和WM_LBUTTONUP事件中设置按钮的按下和正常状态,最后在CCustomButton类的OnPaint方法中绘制按钮,这样就使程序界面看起来更加美观了。

图1.21 按钮导航界面

实现过程

(1)新建一个基于对话框的应用程序。

(2)向对话框中添加两个按钮控件。

(3)以CButton类为基类派生一个按钮类CCustomButton,并为两个按钮控件分别关联一个CCustomButton类的变量。

(4)定义一个枚举类型,代码如下:

        enum ButtonState  {bsNormal,bsHot,bsDown};          //正常状态,热点状态、按下状态

(5)在CCustomButton类的头文件中声明变量,代码如下:

        int  m_State;                                  //按钮当前状态

(6)在CCustomButton类的构造函数和析构函数中设置按钮的状态,代码如下:

        CCustomButton::CCustomButton()                   //构造函数
        {
             m_State   =bsNormal;                         //设置为默认状态
        }
        CCustomButton::~CCustomButton()                 //析构函数
        {
             m_State   =bsNormal;                         //设置为默认状态
        }

(7)在CCustomButton类中,处理按钮的WM_MOUSEMOVE事件,在该事件的处理函数中设置按钮是否为热点状态,代码如下:

        void CCustomButton::OnMouseMove(UINT nFlags, CPoint point)
        {
             HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
             GetWindowRgn(hRgn);                                  //获得按钮区域
             BOOL ret=PtInRegion(hRgn,point.x,point.y);            //鼠标是否在按钮上
             if(ret)                                              //在按钮上
             {
                  if(m_State==bsDown)                            //判断按钮是否为按下状态
                    return ;
                  if(m_State!=bsHot)                             //判断按钮是否为热点状态
                  {
                    m_State=bsHot;                               //设置为热点状态
                    InvalidateRect(NULL,TRUE);                   //更新按钮
                    SetCapture();                                //捕获鼠标
                  }
             }
             else                                                 //不在按钮上
             {
                  m_State=bsNormal;                              //设置按钮状态
                  InvalidateRect(NULL,TRUE);                     //更新按钮
                  ReleaseCapture();                              //释放鼠标
             }
             DeleteObject( hRgn );
             CButton::OnMouseMove(nFlags, point);
        }

(8)在CCustomButton类中,处理按钮的WM_LBUTTONDOWN事件,在该事件的处理函数中设置按钮为按下状态,代码如下:

        void CCustomButton::OnLButtonDown(UINT nFlags, CPoint point)
        {
             m_State=bsDown;                                   //设置按钮按下状态
             InvalidateRect(NULL,TRUE);                        //更新按钮
        }

(9)在CCustomButton类中,处理按钮的WM_LBUTTONUP事件,在该事件的处理函数中设置按钮为默认状态,代码如下:

        void CCustomButton::OnLButtonUp(UINT nFlags, CPoint point)
        {
             if(m_State!=bsNormal)                              //判断按钮状态
             {
                  m_State=bsNormal;                            //设置按钮状态
                  ReleaseCapture();                            //释放鼠标捕捉
                  InvalidateRect(NULL,TRUE);                   //更新按钮
             }
             //向父窗口发送命令消息
             ::SendMessage(GetParent()->m_hWnd,WM_COMMAND, GetDlgCtrlID(), (LPARAM) m_hWnd);
        }

(10)在CCustomButton类中,处理按钮的WM_PAINT事件,在该事件的处理函数中根据按钮的状态绘制按钮控件,代码如下:

            void CCustomButton::OnPaint()
            {
             CPaintDC dc(this);                                //获取按钮的设备上下文
             CString        Text;                             //定义一个字符串变量
             CRect          RC;                               //定义一个区域对象
             CFont          Font;                             //定义一个字体对象
            CFont         *pOldFont;                            //定义一个字体对象指针,用于存储之前的字体
            CBrush        Brush;                                //定义一个画刷对象
            CBrush        *pOldBrush;                           //定义一个画刷对象指针,用于存储之前的画刷对象
            CPoint        PT(2,2);                              //定义一个点对象
            dc.SetBkMode(TRANSPARENT);                          //将设备上下文背景模式设置为透明
            Font.CreateFont(12, 0, 0, 0, FW_HEAVY, 0, 0, 0, ANSI_CHARSET,
                OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                VARIABLE_PITCH|FF_SWISS,"宋体");                //创建字体
            pOldFont=dc.SelectObject(&Font);                    //选中新的字体
            if(m_State==bsNormal)                               //判断按钮是否为正常状态
            {
                Brush.CreateSolidBrush(RGB(230,230,230));       //创建指定颜色的画刷
                dc.SetTextColor(RGB(0,0,0));                    //设置文本颜色
            }
            else if(m_State==bsDown)                             //判断按钮是否为按下状态
            {
                Brush.CreateSolidBrush(RGB(100,100,180));       //创建指定颜色的画刷
                dc.SetTextColor(RGB(250,250,0));                //设置文本颜色
            }
            else if(m_State==bsHot)                              //判断按钮是否为热点状态
            {
                Brush.CreateSolidBrush(RGB(230,230,130));       //创建指定颜色的画刷
                dc.SetTextColor(RGB(50,50,250));                //设置文本颜色
            }
            pOldBrush=dc.SelectObject(&Brush);                  //选中画刷
            GetClientRect(&RC);                                 //获取按钮的客户区域
            dc.RoundRect(&RC,PT);                               //利用当前选中的画刷和画笔绘制按钮区域
            HRGN hRgn=CreateRectRgn(RC.left,RC.top,RC.right,RC.bottom);     //创建一个选区
            SetWindowRgn(hRgn,TRUE);                            //设置按钮窗口区域
            DeleteObject(hRgn);                                 //删除选区
            GetWindowText(Text);                                //获取按钮文本
            dc.DrawText(Text,&RC,DT_CENTER|DT_VCENTER|DT_SINGLELINE);    //绘制按钮文本
            Font.DeleteObject();                                //删除字体对象
            Brush.DeleteObject();                               //删除画刷对象
            dc.SelectObject(pOldFont);                          //恢复原来选中的字体
            dc.SelectObject(pOldBrush);                         //恢复原来选中的画刷
        }

举一反三

根据本实例,读者可以:

在视图中利用按钮导航。

实例017 图片导航界面

本实例可以美化界面、简化操作

实例位置:光盘\mingrisoft\01\017

实例说明

本实例实现了图片导航界面。程序中的每个按钮中都有一个图片,当用户的鼠标在图片上滑过时,按钮显示热点效果的图片,使用户清楚地知道当前执行的操作,通过单击不同的图片可以实现相应的功能。程序运行效果如图1.22所示。

图1.22 图片导航界面

技术要点

要实现本实例的主要功能,主要通过按钮控件的显示和隐藏。首先设置按钮控件全部隐藏,然后在鼠标滑过当前按钮控件时,为按钮控件设置为显示图片,并显示当前的按钮控件,从而达到控件的热点效果。

实现过程

(1)新建名为Navigation的对话框应用程序。

(2)在工程中添加5个位图文件,并添加一个图片控件和4个按钮控件,设置按钮控件的Bitmap属性和Flat属性,设置图片控件显示背景位图。

(3)OnInitDialog函数中设置按钮控件隐藏,代码如下:

        BOOL CNavigationDlg::OnInitDialog()
        {
            CDialog::OnInitDialog();
            //系统代码省略
            //隐藏按钮控件
            GetDlgItem(IDC_BUTTON1)->ShowWindow(SW_HIDE);
            GetDlgItem(IDC_BUTTON2)->ShowWindow(SW_HIDE);
            GetDlgItem(IDC_BUTTON3)->ShowWindow(SW_HIDE);
            GetDlgItem(IDC_BUTTON4)->ShowWindow(SW_HIDE);
            return TRUE;
        }

(4)处理主窗体的WM_MOUSEMOVE消息,在该消息的处理函数中设置按钮的显示图片,并显示指定按钮控件,代码如下:

        void CNavigationDlg::OnMouseMove(UINT nFlags, CPoint point)
        {
            int x = point.x;
            int y = point.y;
            if(x>=27  &&  y>=141&&x<=56&&y<=171)         //判断鼠标是否在基本信息按钮范围内
            {
                  HBITMAP hbmp;
                  hbmp = (HBITMAP)::LoadImage(AfxFindResourceHandle(MAKEINTRESOURCE(IDB_BITMAP1),
                      RT_GROUP_ICON),MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,0);//加载图片资源
                  CButton *p = (CButton *)GetDlgItem(IDC_BUTTON1); //获得按钮指针
                  p->ShowWindow(SW_SHOW);                //显示控件
                  p->SetFocus();                         //按钮控件获得焦点
                  p->SetBitmap(hbmp);                    //显示图片
            }
            else if(x>=27&&y>=192&&x<=56&&y<=220)         //判断鼠标是否在库存管理按钮范围内
            {
                  HBITMAP hbmp;
                  hbmp = (HBITMAP)::LoadImage(AfxFindResourceHandle(MAKEINTRESOURCE(IDB_BITMAP2),
                      RT_GROUP_ICON),MAKEINTRESOURCE(IDB_BITMAP2),IMAGE_BITMAP,0,0,0);//加载图片资源
                  CButton *p = (CButton *)GetDlgItem(IDC_BUTTON2); //获得按钮指针
                  p->ShowWindow(SW_SHOW);                //显示控件
                  p->SetBitmap(hbmp);                    //显示图片
            }
            else if(x>=27&&y>=245&&x<=56&&y<=266)         //判断鼠标是否在查询管理按钮范围内
            {
                  HBITMAP hbmp;
                  hbmp = (HBITMAP)::LoadImage(AfxFindResourceHandle(MAKEINTRESOURCE(IDB_BITMAP3),
                      RT_GROUP_ICON),MAKEINTRESOURCE(IDB_BITMAP3),IMAGE_BITMAP,0,0,0);//加载图片资源
                  CButton *p = (CButton *)GetDlgItem(IDC_BUTTON3); //获得按钮指针
                  p->ShowWindow(SW_SHOW);                //显示控件
                  p->SetBitmap(hbmp);                    //显示图片
            }
            else if(x>=27&&y>=289&&x<=56&&y<=318)         //判断鼠标是否在系统设置按钮范围内
            {
                  HBITMAP hbmp;
                  hbmp = (HBITMAP)::LoadImage(AfxFindResourceHandle(MAKEINTRESOURCE(IDB_BITMAP4),
                      RT_GROUP_ICON),MAKEINTRESOURCE(IDB_BITMAP4),IMAGE_BITMAP,0,0,0);//加载图片资源
                  CButton *p = (CButton *)GetDlgItem(IDC_BUTTON4); //获得按钮指针
                  p->ShowWindow(SW_SHOW);                //显示控件
                  p->SetBitmap(hbmp);                    //显示图片
            }
            else
            {
                  //隐藏按钮控件
                  GetDlgItem(IDC_BUTTON1)->ShowWindow(SW_HIDE);
                  GetDlgItem(IDC_BUTTON2)->ShowWindow(SW_HIDE);
                  GetDlgItem(IDC_BUTTON3)->ShowWindow(SW_HIDE);
                  GetDlgItem(IDC_BUTTON4)->ShowWindow(SW_HIDE);
            }
            CDialog::OnMouseMove(nFlags, point);
        }

举一反三

根据本实例,读者可以:

开发图标导航界面。