【问题标题】:Avoiding recursion when responding to EN_UPDATE message响应 EN_UPDATE 消息时避免递归
【发布时间】:2014-02-18 10:21:02
【问题描述】:

我正在尝试创建一个edit control,它将接受格式为 sign number separator number 的带符号十进制数字(例如 -1.5 )。

在 Internet 上进行广泛研究后,我发现很少有令人满意的蒙版编辑控件示例,但它们是在 MFC 中完成的。

由于我不知道MFC,我无法将该代码翻译成纯Win32 API,所以我决定尝试自己的方式,从头开始。

在仔细研究MFC 示例后,我得出结论,它们在回复EN_UPDATE 消息时执行文本验证。

当我尝试做同样的事情时,我的程序立即退出,没有任何警告或错误消息。我已经得出结论,我的问题一定是递归。

为了确认这一点,我在主窗口的过程中编写了一个小处理程序来测试它:

case WM_COMMAND:
    switch( HIWORD( wParam ) )
    {
    case EN_UPDATE:
        if( LOWORD(wParam) == IDC_OF_MY_EDIT_CONTROL)
        {


            static int counter = 0;   // increment it each time we get EN_UPDATE

            // after we receive EN_UPDATE 4 times stop testing

            if( ( counter++) < 4 )   
            {
                wchar_t text[10];  // get sample text

                GetWindowText( (HWND)lParam, text, 10 );

                // change current char to char + 1
                // which means A will be B, B will be C and so on...

                for( int i = 0; i < wcslen(text); i++ )
                    text[i] = (wchar_t)( 1 + text[i] );   

                SetWindowText( (HWND)lParam, text );   // show changed text
            }
        }
        break;
    }
    break;

case WM_CLOSE: // WM_CLOSE and other handlers...

当我在Debug 模式下启动程序时会发生这种情况:

在我输入字母a 后,编辑控件的文本变为e

这证实了我关于递归的结论:在我按下a 后,它被转换为b,然后另一个EN_UPDATE 被触发,它重复了这个过程,所以b 变成了c,依此类推,直到static 变量到达4 所以结果是e

我的问题很简单:

我应该如何处理EN_UPDATE,或者修改我的程序,以避免这种类型的递归?

编辑(2014 年 2 月 18 日):

我已根据成员 Jonathan Potter 的说明修改了上述 EN_UPDATE 处理程序。

虽然递归问题消失了,但输出不是我想要的。

我已经验证了我的代码在一个简单的控制台应用程序中用他的继任者替换一个字符的正确性,并在单击一个按钮时做同样的事情(对我来说快速添加一个按钮和一个处理程序并不难)点击它)。

所以问题一定是给定指令的执行,因此我提交更正的代码,希望有人指出我做错了什么:

case EN_UPDATE:
    if( LOWORD(wParam) == IDC_OF_MY_EDIT_CONTROL)
    {
        static bool OK_to_process_text = true;

        if( OK_to_process_text )
        {
            OK_to_process_text = false;

            wchar_t text[10];
            memset( text, L'0', sizeof(text) );

            GetWindowText( (HWND)lParam, text, 10 );

            for( size_t i = 0; i < wcslen(text); i++ )
                text[i] = (wchar_t)( 1 + text[i] );

            SetWindowText( (HWND)lParam, text ); 

            OK_to_process_text = true;
        }
    }
    break;
}
break;

现在,在按下a 后,它正确地变成了b,但是在我按下b 之后,我没有得到预期的结果bc,而是cc

这是意料之中的,因为在用户按下一个键后会生成EN_UPDATE 来显示文本。

因此,当按下a 时,它将转换为b。当我按下b 之后,会生成一个新的EN_UPDATE 消息,所以我的处理程序重新开始,这意味着它需要新的字符串bb 并将其正确转换为cc

有没有办法在处理当前文本时暂时禁止创建新的 EN_UPDATE 消息,所以在上面的示例中,当我按下 a 然后按下 @987654360 时,我得到的结果是 bc 而不是 cc @?

编辑结束

感谢您的时间和帮助。

最好的问候。

【问题讨论】:

  • 使用一个标志,如果你只有一个控件实例,则全局存储,否则为每个控件。检查标志是否在您的 EN_UDPATE 处理程序中设置。如果是这样,什么也不做。如果没有,设置标志,处理 EN_UPDATE,然后再次清除标志。
  • @JonathanPotter:对不起,但我有一个后续问题:我应该在哪里设置标志以响应WM_CHAR 或其他消息?至于处理EN_UPDATE,你的意思是这样的:case EN_UPDATE: if( FALSE == flag ) { flag = TRUE; /* do my stuff */ flag = FALSE; }?再次,我很抱歉打扰,但我没有完全理解你的评论。最好的问候。
  • 您最终会陷入无限循环,因为您在处理 EN_UPDATE 时更改了编辑字段的内容。更改字段的内容会生成另一个 EN_UPDATE,它会再次更改字段的内容,依此类推。使用标志会阻止您重新输入 EN_UPDATE 代码。
  • 类似问题的Compromise Answer

标签: c++ winapi recursion editcontrol


【解决方案1】:

I am trying to create an edit control that will accept signed decimal numbers in the format of sign number separator number ( e.g. -1.5 )

也许我误解了这种情况,为什么不使用 EN_CHANGE 并向用户指示值不正确,如下所示?

[以下代码适用于 MS Visual Studio]

case EN_CHANGE:
    if( LOWORD(wParam) == IDC_OF_MY_EDIT_CONTROL)
    {   TCHAR szValue[32];
        double dd;
        GetWindowText((HWND)lParam, szValue, _countof(szValue));
        if (_stscanf(szvalue, _T("%lf"), %dd) == 1)
        {   // optionally reset an error indicator on the screen
        }
        else
        {   MessageBeep(MB_ICONEXCLAMATION);
            // optionally set an error indicator on the screen
        }
        break;
    }

【讨论】:

  • 我以前从未尝试过这样做,所以我进行了彻底的互联网搜索,看看我的选择是什么。一位消息人士说,使用EN_CHANGE 会产生闪烁,所以我决定避免它。我真的很想尝试您的解决方案,但要这样做,我需要您向我解释这个概念,因为(我从未使用过 _stscanf_countof)我只是假设但不知道您所说的 optionally set/reset an error indicator **on the screen** 到底是什么意思.对于提到的功能,我可以找到在线文档,但请详细说明引用的部分。谢谢你的帮忙。最好的问候。
  • _stscanf_countof 是标准函数的 Microsoft 版本,适用于机器人 Unicode 和非 unicode 构建——抱歉,我只使用 Visual Studio; EN_CHANGE 只有在处理通知时更改编辑控件才会导致闪烁;在另一条评论中休息...
  • optionally set/reset an error indicator on the screen 可能是编辑控件之前/之后的一个小的静态控件,表示编辑控件中的值无效;如有必要,您还可以动态更改此控件的颜色,例如在此控件不为空时处理 WM_CTLCOLOR 并返回红色画笔,例如
  • 不用道歉,我总是喜欢学习新的有用的东西。至于闪烁的部分——我想丢弃不正确的字符之前显示文本。 如果在这种情况下没有闪烁,那么EN_CHANGEEN_UPDATE 一样可以接受。我总是可以激活tooltip control 来通知用户错误的类型,而不是刷子。我发现它比刷子更有效。再次感谢你。最好的问候。
  • 请注意discard the incorrect character before the text is shown 无法使用EN_CHANGE,文档说Unlike the EN_UPDATE notification code, this notification code is sent after the system updates the screen,这就是我选择错误指示器的原因...
【解决方案2】:

尝试为您的编辑控件设置 ES_MULTILINE。
或者
您可以在处理编辑控制通知之前检查一个标志。

case WM_COMMAND:
    switch( HIWORD( wParam ) )
    {
    case EN_UPDATE:
        if( LOWORD(wParam) == IDC_OF_MY_EDIT_CONTROL)
        {

if (m_bSentFromSWT)
{
//No need to process this notification
m_bSentFromSWT = FALSE;
break;
}
            static int counter = 0;   // increment it each time we get EN_UPDATE

            // after we receive EN_UPDATE 4 times stop testing

            if( ( counter++) < 4 )   
            {
                wchar_t text[10];  // get sample text

                GetWindowText( (HWND)lParam, text, 10 );

                // change current char to char + 1
                // which means A will be B, B will be C and so on...

                for( int i = 0; i < wcslen(text); i++ )
                    text[i] = (wchar_t)( 1 + text[i] );   

                SetWindowText( (HWND)lParam, text );   // show changed text
m_bSentFromSWT  = TRUE;
            }
        }
        break;
    }
    break;

case WM_CLOSE: // WM_CLOSE and other handlers..

在您的 WM_CREATE 中设置 m_bSentFromSWT = FALSE。

【讨论】:

  • 你能提供一个小的伪代码来说明你的其他观点(检查标志)吗?我将不胜感激。谢谢你的回答!最好的问候。
  • 在像上面那样更改我的代码并在我的WM_CREATE 中将变量设置为false 之后,我的程序退出而没有警告/错误消息。我已经用新的发现更新了我的帖子,并将继续研究你的代码 sn-p 以希望能正确调整它。再次感谢您的帮助,我们会保持联系。最好的问候。
猜你喜欢
  • 2017-01-31
  • 2021-12-02
  • 2021-05-13
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 2010-10-15
  • 2020-10-05
  • 2013-11-26
相关资源
最近更新 更多