Q: When I append text to the end of the current content of a multiline listbox, it is always scrolled back to the first line. Even if I later use ScrollToCaret to return to the end of the text, the whole control briefly flickers. Is there a way to avoid it?
A: Indeed there seems to be an issue in Compact Framework where replacing selection in an edit control. Apparently someone wanted to be clever and even though there is a dedicated control message EM_REPLACESEL, he implemented set_SelectedText in the following way:
string SelectedText
{
get
{
if ( SelectionLength == 0 )
return "";
return new string(Text.ToCharArray(), SelectionStart, SelectionLength);
}
set
{
this.Text = this.Text.Remove(SelectionStart, SelectionLength).Insert(SelectionStart, value);
SelectionStart = this.Text.Length;
SelectionLength = 0;
}
}
Why is it done this way, I don't know. There are perfectly good tools at the edit control message level to work with selection. What's important here is that set_SelectedText internally calls Control.set_Text which translates into WM_SETTEXT message. In case of an edit control it has a side effect of moving caret to the beginning and scrolling the text to the same place, thus creating an unpleasant flicker.
To remedy this problem we simply replace set_SelectedText with our own implementation:
Instead of
textBox.SelectedText = "A quick brown fox jumped over lazy dogs. ";
use
SendMessage(hWnd, EM_REPLACESEL, false, "A quick brown fox jumped over lazy dogs. ");
You will also need the following PInvoke definitions:
const uint EM_REPLACESEL = 0xc2;
[DllImport("coredll")]
extern static IntPtr GetCapture();
[DllImport("coredll")]
extern static int SendMessage(IntPtr hWnd, uint Msg, bool WParam, string LParam);
To get HWND of your textbox use something like this:
private IntPtr GetHWND(Control ctl)
{
ctl.Capture = true;
IntPtr hWnd = GetCapture();
ctl.Capture = false;
return hWnd;
}