MFC 工具提示仅在特殊场合出现

Posted

技术标签:

【中文标题】MFC 工具提示仅在特殊场合出现【英文标题】:MFC tooltips only show up on special occasions 【发布时间】:2013-03-26 15:19:30 【问题描述】:

我的任务是为配置菜单中的每个项目分配工具提示。我已完成将工具提示“添加”到页面上的每个控件,但有时工具提示似乎会显示,有时它不会显示,具体取决于屏幕上控件的位置。

我首先要工具提示页面

EnableToolTips(TRUE);

在每个 CPropertyPage 的 OnInitDialog 方法中。 然后我添加通知地图

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText)

OnToolTipText 函数看起来像这样

BOOL CCfgPrefPage::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )

    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
    UINT nID = pNMHDR->idFrom;

    if (pTTT->uFlags & TTF_IDISHWND)
    
        nID = ::GetDlgCtrlID((HWND)nID);
        if(nID)
        
            if( nID == GetDlgItem(IDC_PICKDIST_EDIT)->GetDlgCtrlID())  
                _tcsncpy_s(pTTT->szText, _T("Tool Tip Text"), _TRUNCATE);
            else if( nID == GetDlgItem(IDC_ENDPTTOL_EDIT)->GetDlgCtrlID())  
                _tcsncpy_s(pTTT->szText, _T("Tool Tip Text"), _TRUNCATE);

            pTTT->lpszText = pTTT->szText; // Sanity Check
            pTTT->hinst = AfxGetResourceHandle(); // Don't think this is needed at all
            return TRUE;
        
    
    return FALSE;

似乎对于我的某些控件,工具提示不会显示。对于大多数复选框控件,工具提示会显示,但对于少数人来说,它们只是不显示。没有其他控件覆盖它们,它们没有被禁用。

另一件事,如果我使用非标准光标窗口反复闪烁工具提示,以至于在某些情况下它是不可读的。我怎样才能解决这个问题?这在 CEdit 控件上不是问题,为什么在其他地方会出现问题?

编辑:更新,多年来一直在页面上的控件似乎显示了工具提示。我现在/今天尝试添加的任何控件都不会显示工具提示。无论位置、控件类型、设置如何,我都无法在新插入的控件上显示单个工具提示。

【问题讨论】:

我在 Windows XP 上遇到过类似的问题(仅在 XP 上,在 Vista、7 和 8 上没有问题)。我在模态对话框中有一个带有工具提示的按钮。当鼠标停在按钮上时,工具提示会在很短的延迟后弹出。到现在为止还挺好。现在,只要满足以下两个条件之一,工具提示就会消失:1)用户将鼠标移出按钮,2)用户在 4 或 5 秒内没有移动鼠标。如果工具提示由于第二种情况而消失,那么它将永远不会在该对话框中返回,除非该对话框被关闭并再次打开。 我也在使用 Windows XP 机器。我没有您列出的问题#2,我可以让工具提示消失,并且在我再次删除+将鼠标移到它上面后它会回来。我的问题是工具提示永远不会出现,也不会出现在独特的控件上,而不是特定的控件上。它出现在某些 CCheckBox 中,而有些则不会出现,它会出现在某些 CEdit 中,而有些则不会。工具提示也大约每 50 毫秒闪烁一次,使其半不可读;但这仅适用于非标准鼠标,例如我经常用于屏幕精度的“十字”鼠标。 Windows 工具提示是有史以来最大的 PITA 功能之一(要实现)。 您是否尝试过使用 CToolTipCtrl 辅助类 (msdn.microsoft.com/en-us/library/6b4cb3a5%28v=vs.80%29.aspx)?我发现它更可靠。如果您的程序使用最新的 MFC 库(带有功能包),您可以使用更强大的 CMFCToolTipCtrl (msdn.microsoft.com/en-us/library/vstudio/bb983852.aspx) 【参考方案1】:

如果您不想使用我建议的辅助类,请修复代码中的问题。 首先,在映射偶数处理程序时使用 ON_NOTIFY_EX_RANGE 宏,如下所示(这将涵盖所有 ID):

ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)

接下来,您需要修复您的功能。我在这里看到了一些问题。首先,在测试 TTF_IDISHWND 标志时,您只需要重新初始化 nID。您不需要将其应用于整个函数。其次,在所有操作之后,您的 nID 将是实际的对话 ID。不需要GetDlgItem()函数

BOOL CCfgPrefPage::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )

    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
    UINT nID = pNMHDR->idFrom;

    if (pTTT->uFlags & TTF_IDISHWND)
    
        nID = ::GetDlgCtrlID((HWND)nID);
    
    if(nID)
    
        if( nID == IDC_PICKDIST_EDIT)  
            _tcsncpy_s(pTTT->szText, _T("Tool Tip Text"), _TRUNCATE);
        else if( nID == IDC_ENDPTTOL_EDIT)  
            _tcsncpy_s(pTTT->szText, _T("Tool Tip Text"), _TRUNCATE);

        //pTTT->lpszText = pTTT->szText; // Sanity Check
        *pResult = 0;
        return TRUE;
    
    return FALSE;

【讨论】:

您的努力,但这并不能解决我的问题。我同意这是编写函数的正确方法。但实际上每个 CPropertyPage 上仍有 4-5 个控件不会显示工具提示。我如何调试它以查看消息的标题? @BumSkeeter - cha 的答案应该有效。但EnableToolTips 仅适用于调用它的 CWnd 对象child 控件。您能否确认您添加的新控件是作为该 CWnd 对象的 控件创建的?【参考方案2】:

使用从旧 MFC 应用程序的菜单中重复某些菜单项的工具栏,我已经解决了这个工具提示问题以及 (1) 修改工具栏位图以包含其他图标和 (2) 为用户提供对当前应用程序状态的反馈。我的问题是我必须手动完成大部分操作,而不是使用各种向导和工具。

我所做的是 (1) 向 CView 派生类添加新成员以处理附加消息,(2) 修改工具栏位图以使用 MS Paint 和资源编辑器添加附加图标,以及 ( 3) 为 CView 派生类的消息映射添加了新的事件 ID 和事件处理程序。

我在更改工具栏位图时遇到的一个问题是,由于我要插入一个图标,因此我必须将位图中的现有图标向右移动。我的第一次尝试导致移动图标在应用程序工具栏上显示为空白。然后我意识到我需要在工具栏位图的长度上增加一点。在工具栏位图中的最后一个图标添加几列以使其成为标准像素宽度后,图标正确显示。

对于工具提示,我在消息映射中添加了以下内容:

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText)

然后我在我的类中添加了以下方法来处理我的菜单项的通知。附带说明一下,OnToolTipText() 似乎是CFrameWnd 类和CMDIChildWnd 类中使用的标准方法,但是CView 源自CWndCFrameWnd 也是如此,所以我怀疑它对什么有影响该方法已命名。

inline BOOL CPCSampleView::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )

    static wchar_t toolTextToggleExportSylk [64] = L"Toggle SYLK export.";
    static wchar_t toolTextClearWindow [64] = L"Clear the log displayed.";
    static wchar_t toolTextConnectLan [64] = L"Log on the POS terminal through the LAN.";

    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;

    switch (pNMHDR->idFrom) 
        case ID_TOGGLE_SYLK_EXPORT:
            pTTT->lpszText = toolTextToggleExportSylk;
            return TRUE;
        case ID_WINDOW_CLEAR:
            pTTT->lpszText = toolTextClearWindow;
            return TRUE;
        case ID_CONNECT_LAN_ON:
            pTTT->lpszText = toolTextConnectLan;
            return TRUE;
    
    // if we do not handle the message then return FALSE to let someone else do it.
    return FALSE;

对于在执行报告时切换文件导出的菜单项的用户反馈,我提供了对消息映射的以下更改,然后实施了必要的方法。涉及两种类型的消息,因此我必须添加两个方法和两个新的消息映射条目:

// New message map entries to handle the menu item selection event
// and to update the menu item and the toolbar icon with state changes
ON_COMMAND(ID_TOGGLE_SYLK_EXPORT, OnToggleExportSylk)
ON_UPDATE_COMMAND_UI(ID_TOGGLE_SYLK_EXPORT, OnUpdateToggleExportSylk)


// New methods added to the CView derived class

// handle the menu selection event generated by either selecting the menu item
// from the menu or by clicking on the icon in the toolbar.
void CPCSampleView::OnToggleExportSylk()

    // Exclusive Or to toggle the indicator bit from 0 to 1 and 1 to 0.
    GetDocument()->ulReportOptionsMap ^= CPCSampleDoc::ulReportOptionsExportSylk;


// handle the request from the MFC framework to update the displayed state this
// not only does a check mark against the menu item it also causes the toolbar
// icon to appear depressed if click is set or non-depressed if click is not set
inline void CPCSampleView::OnUpdateToggleExportSylk (CCmdUI* pCmdUI) 

    if (GetDocument()->ulReportOptionsMap & CPCSampleDoc::ulReportOptionsExportSylk)
    
        // SYLK export is turned on so indicate status to the user. This will
        // put a check mark beside the menu item and show the toolbar button depressed
        pCmdUI->SetCheck (1);
    
    else
    
        // SYLK export is turned off so indicate status to the user.  This will
        // remove the check mark beside the menu item and show the toolbar button as raised.
        pCmdUI->SetCheck (0);
    

需要更改资源文件来为切换操作提供新按钮以及为切换操作添加新菜单项。我对几个不同的事物使用相同的资源 ID,因为它们都是独立的。因此资源字符串的 id 与菜单项的 id 相同,工具栏按钮的 id 也相同,以简化我的生活并轻松找到所有特定的点点滴滴。

工具栏资源文件定义如下:

IDR_MAINFRAME TOOLBAR  16, 15
BEGIN
    BUTTON      ID_CONNECT_LAN_ON
    SEPARATOR
    BUTTON      ID_WINDOW_CLEAR
    SEPARATOR
    BUTTON      ID_TOGGLE_SYLK_EXPORT
    SEPARATOR
    BUTTON      ID_APP_ABOUT
END

菜单的特定部分使用相同的资源 ID 作为切换事件 ID,如下所示:

MENUITEM "Export to SYLK file",         ID_TOGGLE_SYLK_EXPORT

然后提供状态栏文本,鼠标悬停在上面,添加了一个字符串表:

ID_TOGGLE_SYLK_EXPORT   "Toggle export of SYLK format report files for spreadsheets."

结构的lpszText 成员在TOOLINFO 结构的MSDN 文档中描述为:

指向包含工具文本的缓冲区的指针,或 包含文本的字符串资源的标识符。该会员 有时用于返回值。如果您需要检查 返回值,必须指向一个足够大的有效缓冲区。 否则,可以将其设置为 NULL。如果 lpszText 设置为 LPSTR_TEXTCALLBACK,控件发送TTN_GETDISPINFO通知 代码到所有者窗口以检索文本。

查看此问题的现有答案,我想知道 if 语句检查 TTF_IDISHWND 标志。 TOOLINFO 结构的 MSDN 文档有这样的说法:

表示 uId 成员是工具的窗口句柄。如果 此标志未设置,uId 是工具的标识符。

【讨论】:

以上是关于MFC 工具提示仅在特殊场合出现的主要内容,如果未能解决你的问题,请参考以下文章

MFC为禁用的单选按钮添加工具提示

如何使用 mfc 应用程序在文件中写入特殊字符?

为什么在我“编写”FIFO特殊文件或在程序中创建FIFO特殊文件后,shell提示符不会出现?

如何仅在 PHP 中限制某些特殊字符?

文件特殊权限

插入数据时有特殊字符&且后面有字符被误认为变量名(insert时提示:输入值于***)