1

I have edit control in a dialog box which input is checked for validity.

I should indicate validity by changing the background color of an edit control if input is invalid, otherwise I should do nothing.

I am checking the input in EN_CHANGE handler and if input is invalid I store the handle of the edit control in a vector. In the end I call InvalidateRect( (HWND)lParam, NULL, TRUE ); so edit control can be repainted with proper color.

To repaint edit control I am handling WM_CTLCOLOREDIT like this:

case WM_CTLCOLOREDIT:
    {
        bool IsInvalid = false;  // does this edit control hold invalid text ?

        // vector InvalidInput contains handles of edit controls
        // with invalid input, so we check if our window is stored there
        for( vector<HWND>::size_type i = 0; 
            !IsInvalid && ( i < InvalidInput.size() ); i++ )
        {
            if( InvalidInput[i] == (HWND)lParam )
                IsInvalid = true;
        }

        // if input is invalid change background color to light gray
        if( IsInvalid )
        {
            // Needed SetBkMode for text background transparency 
            SetBkMode( (HDC)wParam, TRANSPARENT ); 
            // return light gray brush 
            return (INT_PTR)( (HBRUSH)GetStockObject( LTGRAY_BRUSH ) );
        }
        else     
            return FALSE;   // say we didn't handle it 
                            // so dialog procedure can do that for us
    }

After I start the program edit control is painted properly.

After I type valid entry edit control is painted properly.

After I type invalid character immediately after, background is colored into light gray and everything seems to work fine.

If I delete the invalid character then the background stays gray instead of returning to default system color.

What am I doing wrong and how should I fix this ?

EDIT:

If I put InvalidateRect() in my WM_COMMAND handler for IDC_MYEDIT then the problem seems to disappear:

case WM_COMMAND:
    {
        switch( LOWORD(wParam) )
        {
        case IDC_MYEDIT:
            {
                if( HIWORD(wParam) == EN_CHANGE )
                {
                    //do your validation stuff
                }
                InvalidateRect(...);
            }
            break;
        // the rest of the code...
AlwaysLearningNewStuff
  • 2,939
  • 3
  • 31
  • 84
  • n.m my earlier (deleted) comment. Can you try to move the `Invalid` test *out* of this redrawing function and check the one given in your input test? It might be a timing problem -- not all WM_xx messages appear in the queue in the order you expect them to. – Jongware Apr 27 '14 at 23:41
  • I apologize, but I do not fully understand what you mean. Can you clarify a little? Thank you. Best regards. – AlwaysLearningNewStuff Apr 27 '14 at 23:49
  • I thought you were testing *validity* but on second reading I see you are checking if the window was *tagged* as "invalid". Check if explicitly returning another brush on 'valid' works; that way you can see if the overall idea is sound. – Jongware Apr 27 '14 at 23:58
  • @Jongware: The behavior is the same. However, if I remove `InvalidateRect` call from `EN_CHANGE` handler something happens that might be important: If I type `123` the color is OK. If I then paste letter ( edit control is numeric ) color does not change. After I delete that character **text background color** changes correctly but edits background color stays the same. Furthermore, if I change focus from edit control and then click again painting is done properly. Same is for minimize/maximize. – AlwaysLearningNewStuff Apr 28 '14 at 00:08
  • @Jongware: I have found "a solution" and documented it in edited part of my post. Can you take a look? Thank you. Best regards. – AlwaysLearningNewStuff Apr 28 '14 at 00:36
  • I don't know why the message behaves differently depending on where you call `InValidateRect()`. I just want to comment that you can eliminate your `for` loop completely by using `std::find()` instead: `bool IsInvalid = (std::find(InvalidInput.begin(), InvalidInput.end(), (HWND)lParam) != InvalidInput.end());` in which case you can even geet rid of your `IsInvalid` variable as well and use `std::find()` in your `if( IsInvalid )` statement directly: `if( std::find(InvalidInput.begin(), InvalidInput.end(), (HWND)lParam) != InvalidInput.end() )` – Remy Lebeau Apr 28 '14 at 01:20
  • 2
    When are you updating your "invalid" vector? If you're doing it on `EN_CHANGE` then the control has *already repainted itself*. Try doing it on `EN_UPDATE` instead. – Jonathan Potter Apr 28 '14 at 06:49
  • @JonathanPotter: Yes, I am doing it in `EN_CHANGE`. I do not know how to properly handle `EN_UPDATE` without entering endless recursion. This solution scares me even more since I have multiple edit controls. It seems that I will need a `bool` flag for every edit control in my `EN_UDATE` handler in order to avoid recursion. If you could provide code snippet or fairly detailed instructions on how to handle `EN_UPDATE`, and post them as a solution, I would accept your answer. Thank you. Best regards. – AlwaysLearningNewStuff Apr 28 '14 at 13:54
  • `EN_UPDATE` is really handled exactly the same as `EN_CHANGE`, it just comes before the control has repainted rather than after. – Jonathan Potter Apr 28 '14 at 14:29
  • @JonathanPotter: Yes, but I need to watch out for recursion right? I know I am supposed to check a flag when handling `EN_UPDATE` but I do not know how to handle the case with multiple edit controls (do I need a flag for *every* edit control? ). Thank you. Best regards. – AlwaysLearningNewStuff Apr 28 '14 at 14:40
  • @AlwaysLearningNewStuff: Please, "Thank you. Best regards." at the end of every question and comment is superfluous and noisy! Please desist. Thank you. Best regards. – Lightness Races in Orbit Apr 28 '14 at 14:40
  • I'm not sure what recursion you're watching out for, that normally happens when you do something in response to a message that can trigger another message, so it depends on the code in question. There's nothing special about `EN_UPDATE` that makes it more likely to cause recursion than any other notification message. – Jonathan Potter Apr 28 '14 at 14:48
  • @JonathanPotter: I have edited my post with the peculiar effect I am facing. Can you please take a short look and try to help? Thank you. – AlwaysLearningNewStuff Apr 28 '14 at 22:36

1 Answers1

5

The error is here

    else     
        return FALSE;   // say we didn't handle it 
                        // so dialog procedure can do that for us

The WM_CTLCOLOREDIT message is listed as one of the special exceptions to the rule that returning FALSE means "not handled". It must be handled. If you don't want to handle it, you can pass the message to DefWindowProc.

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135