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 ).

After extensive research on the Internet I have found few satisfying examples of masked edit controls, but they are done in MFC.

Since I do not know MFC, I have trouble translating that code into pure Win32 API, so I have decided to try doing it my own way, starting from scratch.

After carefully studying the MFC examples I have concluded that they perform text validation when responding to EN_UPDATE message.

When I try to do the same my program exits immediately, without any warning or error message. I have concluded that my problem must be recursion.

To confirm this I have wrote a small handler in my main window's procedure to test this:

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...

This is what happens when I start my program in Debug mode:

After I type letter a the text of the edit control turns into e.

This confirms my conclusion about recursion: After I have pressed a it was converted to b, then another EN_UPDATE fired off, which repeated the process so b turned into c and so on until static variable reached 4 so the result was e.

My question is simple:

How should I handle EN_UPDATE, or modify my program, in order to avoid this type of recursion ?

EDIT ( February 18th, 2014 ):

I have modified the above EN_UPDATE handler per member Jonathan Potter's instructions.

Although recursion problem disappeared, the output is not what I have desired.

I have verified the correctness of my code for substituting a character with his successor in a simple console application and by doing the same when clicking on a button ( it wasn't hard for me to quickly add a button and a handler for clicking on it ).

So the problem must be the implementation of the given instructions, therefore I submit the corrected code in hope that someone will point out what did I do wrong:

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;

Now, after pressing a it properly turns into b, but after I press b I do not get the expected result bc but cc.

This is expected, since after user presses a key EN_UPDATE is generated to display text.

Therefore when pressing a it will convert to b. When I press b afterwards, a new EN_UPDATE message is generated, so my handler starts over, which means that it takes new string bb and properly converts it to cc.

Is there a way to suppress temporarily creation of a new EN_UPDATE message while manipulating with current text so in the example above I get the result bc instead of cc when I press a and then b?

END OF EDIT

Thank you for your time and help.

Best regards.

AlwaysLearningNewStuff
  • 2,939
  • 3
  • 31
  • 84
  • 1
    Use a flag, stored globally if you only have one instance of the control, or per-control otherwise. Check if the flag is set in your EN_UDPATE handler. If so, do nothing. If not, set the flag, process the EN_UPDATE, and then clear the flag again. – Jonathan Potter Feb 18 '14 at 10:35
  • @JonathanPotter: My pardon, but I have a follow up questions: Where should I set the flag-in response to `WM_CHAR` or some other message? As for handling `EN_UPDATE` did you mean something like this: `case EN_UPDATE: if( FALSE == flag ) { flag = TRUE; /* do my stuff */ flag = FALSE; }` ? Again, I apologize for disturbing, but I did not completely grasped your comment. Best regards. – AlwaysLearningNewStuff Feb 18 '14 at 10:56
  • You end up in an infinite loop because you change the contents of the edit field while processing EN_UPDATE. Changing the contents of the field generates another EN_UPDATE, which changes the contents of the field again, and so on. Using a flag stops you entering the EN_UPDATE code re-entrantly. – Jonathan Potter Feb 18 '14 at 11:00
  • The [Compromise Answer](https://stackoverflow.com/a/63260178/14052203) of similar question – user14052203 Aug 05 '20 at 07:48

3 Answers3

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 )

Maybe I am misunderstanding the situation, why not use EN_CHANGE and give the user a indication that the value is not correct, with something like the following?

[code below is for 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;
    }
Edward Clements
  • 5,040
  • 2
  • 21
  • 27
  • I have never tried to do this before so I have conducted thorough Internet search to see what my options are. One source said that using `EN_CHANGE` can create flickering so I decided to avoid it. I would really like to try your solution but to do so I need you to explain me the concept since ( I have never used `_stscanf`, or `_countof` ) I only assume but do not know exactly what you mean by `optionally set/reset an error indicator **on the screen**`. For the functions mentioned, I can find online documentation, but please elaborate on the quoted part. Thank you for helping. Best regards. – AlwaysLearningNewStuff Feb 18 '14 at 14:11
  • [_stscanf](http://msdn.microsoft.com/en-us/library/t6z7bya3.aspx) and [_countof](http://msdn.microsoft.com/en-us/library/ms175773.aspx) are Microsoft versions of the standard functions which work with bot Unicode and non-unicode builds -- sorry, I work only with Visual Studio; `EN_CHANGE` can cause flickering only if you change the edit control while handling the notification; rest in another comment... – Edward Clements Feb 18 '14 at 15:06
  • `optionally set/reset an error indicator on the screen` could be a small static control just before/after the edit control which indicates that the value in the edit control is not valid; you can also dynamically change the colour of this control if necessary by handling WM_CTLCOLOR for this control when it is not empty and returning a red brush, for example – Edward Clements Feb 18 '14 at 15:09
  • There is no need to apologize, I always like learning new useful things. As for the flickering part-I would like to **discard** the incorrect character **before** the text is shown. **If there will be no flickering** in this case then `EN_CHANGE` is just as acceptable as `EN_UPDATE` is. Instead of a brush, I could always activate a `tooltip control` to inform the user about the type of error. I find it more efficient than a brush. Thank you again. Best regards. – AlwaysLearningNewStuff Feb 18 '14 at 15:51
  • Be aware that `discard the incorrect character before the text is shown` will not be possible with [EN_CHANGE](http://msdn.microsoft.com/en-us/library/windows/desktop/bb761676(v=vs.85).aspx), the docs say `Unlike the EN_UPDATE notification code, this notification code is sent after the system updates the screen`, which is why I went for an error indicator... – Edward Clements Feb 18 '14 at 16:59
  • Then I could popup a `tooltip`, and **disable SAVE button** until user corrects the mistake. That way I could use the brush to indicate the error, as you have suggested. I just do not know how to underline the invalid character. Can you suggest me how to do it? I could also change its color to further point it out. This would be ideal implementation of your solution. Thank you. Best regards. – AlwaysLearningNewStuff Feb 19 '14 at 00:01
  • Underlining a character in an edit box is not possible, you'll then need to use a RichEdit control and that seems an overkill for this purpose; changing the colour of an editbox has a lot of anwers on SO e.g.: [this one](http://stackoverflow.com/a/2262694/1850797), just search for `WM_CTLCOLOREDIT`, please post a new question if you have doubts about this -- `Please avoid extended discussions in comments` – Edward Clements Feb 19 '14 at 07:55
0

Try setting ES_MULTILINE for your edit control.
Or
you can check a flag before processing edit control notifications.

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..

Set m_bSentFromSWT = FALSE in your WM_CREATE.

Mukt G
  • 101
  • 3
  • Can you provide a small pseudo code that illustrates you other point ( checking of a flag) ? I would highly appreciate it. Thank you for answering! Best regards. – AlwaysLearningNewStuff Feb 18 '14 at 10:49
  • After changing my code like above, and setting the variable to `false` in my `WM_CREATE` my program exited without warning/error message. I have updated my post with new findings, and will continue to study your code snippet in hope to adapt it properly. Thank you again for helping, we shall stay in touch. Best regards. – AlwaysLearningNewStuff Feb 18 '14 at 12:07
0

I'm sure all these years later, there's no possible way that the OP is using MFC.

However, for anyone who happens on this problem, the simplest solution is to have a block like this at the top of your message handler:

// Avoid Recursion
static bool updating = false;
if (updating)
{
    updating = false;
    return;
}

Just before you call SetWindowText in your handler, set updating = true. It may feel a little ugly, but it is MFC.

Jamie
  • 1,754
  • 16
  • 34