如何在Pocket PC上实现向导式对话框

Pocket PC操作系统并不支持向导对话框,尽管文档中提到属性页支持PSH_WIZARD标志,但实际上它并不像预期的那样工作。本文将介绍一种通过属性页实现类似向导对话框的简单方法。

在之前的文章中,介绍了属性页回调机制,并使其与MFC兼容,通过CCePropertySheet类。通过自定义回调函数,可以在大多数设置属性页中添加标题和页脚。本文将探讨属性页是如何由系统组装的,以及如何利用这些信息来实现自己的向导式对话框。

Pocket PC属性页

属性页是一个包含以下项目的对话框:

  • 属性页对话框
  • 标题。这是一个可选的静态子控件,其ID为0x3028。
  • 标签控件。这是一个子控件,其ID为0x3020。MFC将其识别为AFX_IDC_TAB_CONTROL。
  • 页脚。这是一个可选的富文本控件,其文本通过PSCB_GETLINKTEXT设置,并且作为EM_INSERTLINKS消息的参数(更多信息请参见richink.h)。

本文的主要思想是隐藏标签控件,并通过SetActivePage()、GetActiveIndex()和GetPageCount()实现类似向导的导航。

实现

现在,准备使用属性页(CCeWizard)实现一个类似向导的对话框。这个类派生自CCePropertySheet,允许用户插入标题和页脚。首先,需要设置对话框:

BOOL CCeWizard::OnInitDialog() { BOOL bResult = CCePropertySheet::OnInitDialog(); HWND hWndTab; hWndTab = ::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL); if (hWndTab) ::ShowWindow(hWndTab, SW_HIDE); ModifyStyle(0, WS_NONAVDONEBUTTON, SWP_NOSIZE); SHDoneButton(m_hWnd, SHDB_HIDE); PopulateToolBar(); UpdateControls(); return bResult; }

稍后将讨论PopulateToolBar()和UpdateControls()方法。现在,可能会注意到,在隐藏标签控件后,将标题与对话框分隔的线也消失了。显然,这条线是标签控件的一部分,因此它也被隐藏了。为了解决这个问题,必须自己在OnPaint方法中绘制它:

void CCeWizard::OnPaint() { CPaintDC dc(this); CRect rc; if (!m_strTitle.IsEmpty()) { GetClientRect(&rc); dc.MoveTo(0, 23); dc.LineTo(rc.right, 23); } }

请注意,只有在有标题(m_strTitle属于CCePropertySheet类)时才绘制这条线。

导航

现在,必须考虑如何在向导中导航:在隐藏标签控件后,必须提供一种方法让用户可以翻阅多个页面(属性页)。放置控制按钮的最佳位置是命令栏。CCeWizard类提供了两种在命令栏上放置控件的选项(尽管当然可以覆盖此功能):图形按钮或文本按钮。如果想提供图形按钮(见上图),请在资源编辑器中创建一个工具栏,至少包含四个按钮:ID_BAR_OK、ID_BAR_CANCEL、ID_BAR_BACK和ID_BAR_NEXT。当创建CCeWizard对象时,将工具栏ID作为第二个参数传递给构造函数。要显示文本按钮(见下图),请在类构造函数的第二个参数上使用0,并定义以下字符串资源:IDS_BAR_OK、IDS_BAR_CANCEL、IDS_BAR_BACK和IDS_BAR_NEXT。

处理导航命令是一个简单的事情。以下是ID_BAR_BACK处理程序:

void CCeWizard::OnBarBack() { SetActivePage(GetActiveIndex() - 1); UpdateControls(); }

现在是ID_BAR_NEXT处理程序:

void CCeWizard::OnBarNext() { SetActivePage(GetActiveIndex() + 1); UpdateControls(); }

更新控件

现在,让看看如何更新向导的控件。这项任务是必要的,以便让用户知道他们在向导中的位置。这可以通过两种方式完成:更新导航按钮和在向导的标题中报告进度。所有这些只需一个方法即可实现:

void CCeWizard::UpdateControls() { int iIndex = GetActiveIndex(), nPages = GetPageCount(); CToolBarCtrl& rToolBar = m_pWndEmptyCB->GetToolBarCtrl(); CWnd* pWndHdr; pWndHdr = GetDlgItem(AFX_IDC_HEADER_CONTROL); if (pWndHdr) { CString strMsg, strHeader; strMsg.Format(_T(" (%d/%d)"), iIndex + 1, nPages); strHeader = m_strTitle + strMsg; pWndHdr->SetWindowText(strHeader); } rToolBar.EnableButton(ID_BAR_BACK, iIndex > 0); rToolBar.EnableButton(ID_BAR_NEXT, iIndex < nPages - 1); ResizePage(); }

请注意,无论导航按钮是图形还是文本,更新其状态的方式都是相同的。

插入工具栏

图形和文本工具栏都使用一个方法插入:

void CCeWizard::PopulateToolBar() { CCeCommandBar* pCmdBar; pCmdBar = (CCeCommandBar*)m_pWndEmptyCB; if (m_idToolBar) pCmdBar->LoadToolBar(m_idToolBar); else { TBBUTTON tbButton; CString strMenu; memset(&tbButton, 0, sizeof(TBBUTTON)); tbButton.iBitmap = I_IMAGENONE; tbButton.fsState = TBSTATE_ENABLED; tbButton.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE; strMenu.LoadString(IDS_BAR_OK); tbButton.iString = (int)(LPCTSTR)strMenu; tbButton.idCommand = ID_BAR_OK; pCmdBar->SendMessage(TB_INSERTBUTTON, 0, (LPARAM)&tbButton); strMenu.LoadString(IDS_BAR_CANCEL); tbButton.iString = (int)(LPCTSTR)strMenu; tbButton.idCommand = ID_BAR_CANCEL; pCmdBar->SendMessage(TB_INSERTBUTTON, 1, (LPARAM)&tbButton); strMenu.LoadString(IDS_BAR_BACK); tbButton.iString = (int)(LPCTSTR)strMenu; tbButton.idCommand = ID_BAR_BACK; pCmdBar->SendMessage(TB_INSERTBUTTON, 2, (LPARAM)&tbButton); strMenu.LoadString(IDS_BAR_NEXT); tbButton.iString = (int)(LPCTSTR)strMenu; tbButton.idCommand = ID_BAR_NEXT; pCmdBar->SendMessage(TB_INSERTBUTTON, 3, (LPARAM)&tbButton); } }

终止向导

终止向导不应该直接调用EndDialog()。亲身经历告诉,这样做不会调用适当的DDX和DDV例程。相反,直接向属性页发送IDOK和IDCANCEL命令。

代码的第一版没有考虑到隐藏标签控件的一个不可避免的副作用:属性页不知道它被隐藏了,所以它会愉快地调整子属性页的大小,就好像标签控件在那里一样。发生的情况是,一些对话框的区域被偷走了(标签控件应该在的区域)。这是由John Simmons指出的(这就是为什么他的名字在图片中被引用)。谢谢,John!

解决这个错误涉及到调整活动页面的大小。这是在以下方法中完成的:

void CCeWizard::ResizePage() { CPropertyPage* pPage = GetActivePage(); if (pPage) { CRect rc; pPage->GetWindowRect(&rc); ScreenToClient(&rc); rc.bottom += 22; pPage->MoveWindow(&rc); } }

此方法在代码中的多个地方被调用,特别是在UpdateControls()内部。

void CCeWizard::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) { HWND hWnd = NULL; if (pWndOther) hWnd = *pWndOther; SHHandleWMActivate(m_hWnd, MAKELPARAM(nState, bMinimized), (LPARAM)hWnd, &m_sai, 0); } void CCeWizard::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) { SHHandleWMSettingChange(m_hWnd, (WPARAM)uFlags, (LPARAM)lpszSection, &m_sai); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485