By Sibers Senior C/C++/C# Developer Alexey Vasiliev
You are likely to know that RichTextBox supports word wrap. But not everyone has a clue how to actually manage word wrapping. Not long ago I faced the following problem: word wrap in this control automatically starts with a white-space symbol or a punctuation mark. So if we want to insert 100 symbols to display in three lines with random line breaking, then problems begin as soon as we insert a space or a hyphen in the middle. OK… looks not good I couldn’t find any useful information about it, though.
One would think it’s not a big deal and could be fixed, but it’s where the number of problems is just starting to grow: the control RichTextBox appears to have a Boolean feature WordWrap but no ways to change the behavior of this word wrapping. I’ve already exhausted all possible combinations. There is almost no documentation about word-wrapping, word-breaking or hyphenation. Is .NET simply not armed with it?
So, where can we investigate this component? Where does it come from on .NET? Exactly! On MFC they have already had a lot of headache about it. I can’t just forget the thought about having to load a special library for using you-know-which control! Moreover, the .NET component is only a cover for a very familiar thing. Thus, I turned to sources of this component, the file RichTextBox.h and MSDN Brief search produced the following results:
1. Message EM_SETHYPHENATEINFO and linked styles WBF_WORDWRAP etc.
2. Message EM_SETWORDBREAKPROC and function EDITWORDBREAKPROC.
The former was excluded immediately because according to MSDN it serves only in Asian Windows. The latter appeared to be more interesting because the parameters transferred to callback-function EDITWORDBREAKPROC included such styles as WBF_BREAKAFTER and WBF_ISWHITE, and the text indicator itself. It raised certain interest In other words, we can now create our own function with tailored definitions of where and how to carry the text over in the control. So, I threw together some code:
[DllImport("user32.dll")]
extern static IntPtr SendMessage(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam);
const uint EM_SETWORDBREAKPROC = 0x00D0;
delegate int EditWordBreakProc(IntPtr text, int pos_in_text, int bCharSet, int action);
event EditWordBreakProc myCallBackEvent;
int myCallBack(IntPtr text, int pos_in_text, int bCharSet, int action)
{
return 0;
}
public Form1()
{
InitializeComponent();
richTextBox.Multiline = true;
richTextBox.WordWrap = true;
richTextBox.ScrollBars = RichTextBoxScrollBars.None;
myCallBackEvent = new EditWordBreakProc(myCallBack);
IntPtr ptr_func = Marshal.GetFunctionPointerForDelegate(myCallBackEvent);
SendMessage(richTextBox.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, ptr_func);
}
Thus, let’s see: I’ve created a delegate concurrent with definition EDITWORDBREAKPROC and subscribed its own function to it. Through the marshaling I got the indicator of my function – now it’s possible to make substitution. Then I took a handle of the control and sent it a message EM_SETWORDBREAKPROC for shifting the word wrap function. What is myCallBack function supposed to do? It is to return the position in which the line should be broken relatively to the current one. That is in fact we need to parse the entire text and according to some rule say – “here is the place for the word wrap”. For my task it was enough to return the zero position. So, the text just shifts to the next line without any rules for breaking. Yeah! This is exactly what I need!
As addition:
1. For other cases with the need to adapt lines and place hyphens, it’s recommended to gain the line from the callback function. It could be done with the aid of the following code:
int myCallBack(IntPtr text, int pos_in_text, int bCharSet, int action)
{
string str = Marshal.PtrToStringUni(text);
...
}
2. The control styles change after different window messages like changing focus, enable/disable and so on. Hence, for the above example to function properly the message EM_SETWORDBREAKPROC should be sent after all significant events. I wish I could forget about such little things. There is a way out! We make a new control, inherit from RichTextBox and… I think you’ll guess
3. The example on MFC would look much simpler
good stuff.
Alexey, thanks… just what I needed…
I am unsure if I should go along on this post or not. On the other
hand, given that we all have unique belief, I should consider your opinion.
But I need to say, I was roughly convinced basically
because you’ve composed it very well.
Are you sure? I have seen the majority of your works however I have to
disagree on you on this one. But then again were two different people with uncommon stand.
Regardless, keep up the good work