I have a Windows Form with a Rich Textbox control on the form. What I want to do is make it so that each line only accepts 32 characters of text. After 32 characters, I want the text to flow to the next line (I do NOT want to insert any carriage returns). The WordWrap property almost does this, except that it moves all the text entered, up to the last 开发者_如何学编程space in the text and moves it all. I just want to neatly wrap the text right after 32 characters. How can I do this? I'm using C#.
Ok, I have found a way to do this (after a lot of googling and Windows API referencing) and I am posting a solution here in case anyone else ever needs to figure this out. There is no clean .NET solution to this, but fortunately, the Windows API allows you to override the default procedure that gets called when handling word wrapping. First you need to import the following DLL:
[DllImport("user32.dll")]
extern static IntPtr SendMessage(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam);
Then you need to define this constant:
const uint EM_SETWORDBREAKPROC = 0x00D0;
Then create a delegate and event:
delegate int EditWordBreakProc(IntPtr text, int pos_in_text, int bCharSet, int action);
event EditWordBreakProc myCallBackEvent;
Then create our new function to handle word wrap (which in this case I don't want it to do anything):
private int myCallBack(IntPtr text, int pos_in_text, int bCharSet, int action)
{
return 0;
}
Finally, in the Shown event of your form:
myCallBackEvent = new EditWordBreakProc(myCallBack);
IntPtr ptr_func = Marshal.GetFunctionPointerForDelegate(myCallBackEvent);
SendMessage(txtDataEntry.Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, ptr_func);
If you are using only (one) fixed width font for text within rich text box then you can use MeasureString to count the width needed for 32 characters and then adjust rich text box width accordingly. Thats should do wrapping within 32 characters.
I've been looking into this problem myself and have found a really easy way to get around this. You need to place a small piece of code in the Key_Down Event on your TextBox or RichTextBox control.
Ensure that both Word Wrap and Multiline properties are still set to true, then, you just need to check for the Key Press of KeyCode.Return & Keycode.Enter.
Like Below :-
private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
{
e.Handled = true;
}
}
Setting the handled value you here to true, sends back the message that we have dealt with the Key Press event, and nothing happens. The Text Control continues to use Word Wrap and blocks the carriage return.
Hope this helps.
The accepted answer almost works, but is a bit awkward, due to the unnecessary use of event
, explicit delegate instantiation, and non-static callback method.
I say "almost", because it also has a subtle bug: the delegate that is created for the callback is not stored in any variable, which makes it eligible for garbage collection. The garbage collector doesn't have any way to know that you've handed the delegate-wrapped-in-a-pointer to the window object, and so would eventually be able to discard the delegate which would then cause the application to crash the next time the TextBox
control needed to wrap any text.
Here is a version that is more to my liking:
public partial class Form1 : Form
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static IntPtr SendMessage(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam);
private const uint EM_SETWORDBREAKPROC = 0x00D0;
private delegate int SetWordBreakProc(IntPtr text, int pos_in_text, int bCharSet, int action);
private readonly SetWordBreakProc _wordBreakCallback = (text, pos_in_text, bCharSet, action) => 0;
public Form1()
{
InitializeComponent();
textBox1.HandleCreated += TextBox1_HandleCreated;
}
private void TextBox1_HandleCreated(object sender, EventArgs e)
{
IntPtr callback = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(_wordBreakCallback);
SendMessage(((Control)sender).Handle, EM_SETWORDBREAKPROC, IntPtr.Zero, callback);
}
}
The other difference here is that I initialize the word-break proc in the TextBox.HandleCreated
event handler. This accomplishes two things: first, the initialization happens as soon as possible, right after the control's handle is valid, and second, by doing the initialization this early in the process, if the TextBox.Text
property was already set, e.g. in the Designer or the form's constructor, the wrapping will still be done correctly even for that initial text.
Text pasted in later would still be fine, and of course you could even just temporarily reset the text after initialization to force recomputation of the wrapping. But IMHO it's better to just get it to work early enough in the first place.
Also note that the above require explicit initialization of each TextBox
you've added to the form. Obviously, if you wanted to apply this technique to more than one TextBox
, it would make sense to create a new TextBox
subclass that does this initialization within its own OnHandleCreated()
override method.
精彩评论