How to append text to a TextBox?

13,492

Solution 1

For a text box (edit control) the caret is basically a "selection" that start and end at the same place.

Use SetSel to create a selection that starts and ends after the last character currently in the control, then use ReplaceSel to replace that empty selection with new text.

Since you're using the raw Win32 API, SetSel will be

SendMessage(your_control, EM_SETSEL,-1, -1);

...and ReplaceSel will be:

SendMessage(your_control, EM_REPLACESEL, TRUE, string_to_add);

Oops -- as noted in the postscript to the question, this doesn't work as-is. You need to start with WM_GETTEXTLENGTH (or GetWindowTextLength) to get the length of the text, then set the selection to the end (i.e., the beginning and end both equal to the length you just got), then replace the selection. My apologies -- I should probably know better than to go from memory when dealing with something like this that I haven't done in a while.

Solution 2

  1. Use GetWindowTextLength to find the length of the text in there.
  2. Create a dynamic array of characters (std::vector<TCHAR>) with that length, plus the length of the appended text, plus the null.
  3. Use GetWindowText to store the current text in there.
  4. Add on the appended text (with something like _tcscat).
  5. Use SetWindowText to put everything into the textbox.

In summary:

int len = GetWindowTextLength(textbox);
std::vector<TCHAR> temp(len + lengthOfAppendedText + 1);

GetWindowText(textbox, temp.data(), temp.size());
_tcscat(temp.data(), appendedText);
SetWindowText(textbox, temp.data());

If you aren't using C++11, replace temp.data() with &temp[0]. If it has to be compatible with C, it's back to malloc and free instead of std::vector, but it's not much extra work considering there's no resizing going on.

Share:
13,492
0x6B6F77616C74
Author by

0x6B6F77616C74

Updated on June 04, 2022

Comments

  • 0x6B6F77616C74
    0x6B6F77616C74 almost 2 years

    I think the following code should be self-explanatory.

    #include <Windows.h>
    
    static HWND textBoxInput;
    static HWND button;
    static HWND textBoxOutput;
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR cmdLine,int nCmdShow)
    {
        HWND hMainWindow;
        WNDCLASS wc = {};
        wc.lpfnWndProc = WindowProc;
        wc.lpszClassName = "Main's window class";
        wc.hInstance = hInstance;
        RegisterClass(&wc);
    
    
        hMainWindow = CreateWindow(wc.lpszClassName,"Append text main window",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,500,400,NULL,NULL,hInstance,NULL);
    
        error=GetLastError();
    
        if(hMainWindow == NULL) return 1;
    
        textBoxInput = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 10, 300, 21, hMainWindow, NULL, NULL, NULL);
    
        button = CreateWindowEx(WS_EX_CLIENTEDGE,"Button","Append",WS_CHILD | WS_VISIBLE | ES_CENTER, 10, 41,75,30,hMainWindow,NULL,NULL,NULL); 
    
        textBoxOutput = CreateWindowEx(WS_EX_CLIENTEDGE,"Edit",TEXT("->This content is untouchable and unreadable!<-"),WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL |  ES_MULTILINE | ES_READONLY ,10,81,500,90,hMainWindow,NULL,NULL,NULL);
    
    
        ShowWindow(hMainWindow,SW_SHOW);
    
        MSG msg = { };
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch(uMsg)
        {
            case WM_COMMAND:
            if((HWND)lParam == button)
            {               
                TCHAR* buffer = new TCHAR[150];
    
                GetWindowText(textBoxInput,buffer,150);
    
                SetWindowText(textBoxOutput,buffer);
                //AppendWindowText(textBoxOutput,buffer,150) - I haven't found such function;           
                        delete [] buffer;       
            }
            break;
    
            case WM_PAINT:          
                {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
                HBRUSH pedzel;
    
                pedzel = CreateSolidBrush(RGB(10,250,10));
    
                FillRect(hdc, &ps.rcPaint, pedzel);
    
                EndPaint(hwnd, &ps);
                return 0;
                }
            case WM_DESTROY:
                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    

    In brief: this program creates two textBoxes and a button that launches a process of copying a content from the first to the second. The SetWindowText function causes cleaning the output box , what obviously isn't desired.

    Update after the Jerry Cofinn's answer

    SendMessage(textBoxOutput,EM_SETSEL,-1,-1); //no difference between passing 0 or -1
    SendMessage(textBoxOutput,EM_REPLACESEL,TRUE,(LPARAM)buffer);
    

    Surprisingly, it prepends the text. I've read the documentation about EM_SETSEL and I'm still wondering why doesn't it place the raw input at the end.

  • Marlon
    Marlon over 11 years
    There's really no point resizing it since you know both lengths to begin with.
  • Mark Ransom
    Mark Ransom over 11 years
    Don't use char, use TCHAR (or wchar_t once you realize you'll never target an 8-bit OS again).
  • chris
    chris over 11 years
    @MarkRansom, It was an example, but you're right. I changed it, and wchar_t is much less work in the long run if you make things like tstring and whatever and add the interfacing (like to_tstring, ttoi, etc), so it's definitely worth using wchar_t if you can.
  • Mark Ransom
    Mark Ransom over 11 years
    Also there's no need to multiply by sizeof(TCHAR) since the vector is allocated by elements, not bytes.
  • Remy Lebeau
    Remy Lebeau over 11 years
    Using wParam=-1 and lParam=-1 for EM_SETSEL removes the current selection but does not move the caret to the end of the current text. If the caret is in the middle of the text then you will insert the new text in the middle instead of the end. You have to position the caret correctly before sending EM_REPLACESEL.
  • chris
    chris over 11 years
    @MarkRansom, Crap, I was thinking of malloc for some reason. I guess that's my natural reaction when changing char (which has a size of 1) to something with a size greater than that.
  • David Heffernan
    David Heffernan over 11 years
    EM_REPLACESEL is the right way to append in an EDIT control. The details of the EM_SETSEL may need to be tidied up, but this is the answer that should be accepted. Get the control to do the appending. It's wasteful to append text in the app and sent it all back to the control.
  • chris
    chris over 11 years
    @DavidHeffernan, Agreed. It's always a great time to learn a better way to do something.
  • 0x6B6F77616C74
    0x6B6F77616C74 over 11 years
    Almost a good answer.Please read the postscript of my question.