6

I have an EDIT control created like this:

hwndEDIT_5 = CreateWindowEx (
  0, "EDIT",  NULL,
  WS_VSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
  135, 450, 555, 200,
  h2, ( HMENU ) ID_EDIT_CONSOLE,
  h1, NULL
);

As you can see it is a read-only EDIT area where multi lines text can be displayed. It is supposed to be a console where I can display some information for users when they use the program. I would like the text area to automatically scroll to the bottom-most entry (the newest one) whenever a new line (or message for an user) is added. I've implemented this:

 SetDlgItemText ( h2, ID_EDIT_CONSOLE, ch_s );
 SCROLLINFO scr;
 SCROLLINFO * scr_p = &scr;
 scr.cbSize = sizeof ( SCROLLINFO );
 scr.fMask = SIF_RANGE;
 GetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p );
 int mmax = scr.nMax;
 scr.fMask = SIF_POS;
 scr.nPos = mmax;
 SetScrollInfo ( GetDlgItem ( h2, ID_EDIT_CONSOLE), SB_VERT, scr_p, TRUE );

That code is scrolling vertical scrollbar to the end of an EDIT control after adding new msg and it works great, the scrollbar gets scrolled but the text still remains visible from the beginning - it rewinds to the beginning after addition while scrollbar rewinds to the bottom. How to make it properly?

Last but not least - this is might be important - in order to display a message firstly I capture the text that is already displayed by using: GetDlgItemText ( h2, ID_EDIT_CONSOLE, buf, len + 1 ); then I convert buf into string and add to that string a new message that I want to display. Then I convert it back to char array and set it up with SetDlgItemText. I seperate lines by using \r\n. I've coded it that way because I didn't know how to add a line to an EDIT control in different way than using SetDlgItemText. And it adds only one entry AFAIK - if used twice I will not come up with two entries added to an EDIT control, but the first one will get replaced by second function call.

maxhb
  • 8,554
  • 9
  • 29
  • 53
okt
  • 175
  • 1
  • 5
  • 9

5 Answers5

8

Don't use SetScrollInfo. Use SendMessage() with the EM_LINESCROLL message, sending the message to the edit control's window handle.

SendMessage(MemoHwnd, EM_LINESCROLL, 0, NumLinesToScroll);

The documentation says:

The control does not scroll vertically past the last line of text in the edit control. If the current line plus the number of lines specified by the lParam parameter exceeds the total number of lines in the edit control, the value is adjusted so that the last line of the edit control is scrolled to the top of the edit-control window.

Ken White
  • 123,280
  • 14
  • 225
  • 444
6

I had the same problem and solved it with Jerry Coffin's answer and some research. This is the way I use now:

string text = "Append this text";
SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)(text.c_str())); //append text to current pos and scroll down

If needed: To scroll at the end of Edit Control without appending text:

SendMessageA(hEdit, EM_SETSEL, 0, -1); //Select all. 
SendMessageA(hEdit, EM_SETSEL, -1, -1);//Unselect and stay at the end pos
SendMessageA(hEdit, EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos
Bluefire
  • 408
  • 4
  • 10
5

You can add text by setting the beginning and end of the selection to the end of the text in the control (EM_SETSEL), then replacing the (empty) selection with your new text (EM_REPLACESEL).

Scrolling to the bottom can be done with EM_SCROLLCARET after the caret (the selection) is at the end of the text. There are other ways, but if you're doing it immediately after adding text, this is probably the easiest.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    I tried doing that and the result is that it keeps flashing to the top and then down again.. not pretty – Marenz Apr 30 '14 at 21:26
1

in my case I had a multi line string and Ken White's idea worked very well: HWND hEdit = this->GetDlgItem(IDC_EDIT_LOG)->m_hWnd; if (hEdit) { int lineCount = m_strClientLog.Replace(_T("\n"), _T("\n")); ::SendMessage(hEdit, EM_LINESCROLL, 0, lineCount); }

dmihailescu
  • 1,625
  • 17
  • 15
0

for MFC projects you can use:

mLoggingTextCtl.SendMessage(EM_SETSEL, 0, -1); //Select all. 
mLoggingTextCtl.SendMessage(EM_SETSEL, -1, -1);//Unselect and stay at the end pos
mLoggingTextCtl.SendMessage(EM_SCROLLCARET, 0, 0); //Set scrollcaret to the current Pos
ingconti
  • 10,876
  • 3
  • 61
  • 48