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); }
举一反三
根据本实例,读者可以:
开发图标导航界面。