问题
In C#, I need to be able to at least give the impression of 2 columns in a single RichTextBox. For my purposes, it could look like this:
1+1 2
70*8+5 565
1000000-300000 700000
76-10 66
For each 'question' in the left column, there will be a corresponding answer.
The 'obvious' solution is to use tabs like this:
SelectionTabs = new int[] { 0, 500 };
(or simply set \deftab to about 3000 in the raw Rtf).
This is all well and good, until the left column input ITSELF contains tabs. When this is the case, everything goes pear-shaped. I don't mind inserting multiple tab-stops, but this won't work well when the input contains text which sometimes goes 'past' a particular tab-stop. Detecting this and accounting for it properly is not just hard to do, but also fraught with peril due to the 'very-almost-but-not-quite-WYSIWIG' results that occur when printing, and how tabs are affected.
Another idea is to have a global tab size (using \deftab in the Rtf), and then calculate how many tabs should automatically be inserted after each line in the left column (based on the longest line in the left column). The problem with this approach is that while things look great on the screen, when it comes to printing, the right column has some glitches. By 'glitches', I mean approximately 1 in every 20 lines will have a 'lost' or 'extra' tab. This is because printing isn't entirely WYSIWIG, even after I followed this tutorial: http://msdn.microsoft.com/en-us/library/ms996492.aspx
Suffice to say, even a few pixels/points off in print can have a drastic effect on the positioning of the tabs in comparison to the RichTextBox's view on the screen.
I've also tried changing the tabstop position halfway through a line in the Rtf, but unfortunately, such a control code is applied from the beginning of the line, despite its location in the Rtf.
I've also been combing the Rtf specification from http://www.biblioscape.com/rtf15_spec.htm, and there doesn't seem to be much I can do about it. C#'s RichTextBoxes don't seem to get on well with tables (which was another potential solution). There's also Rtf columns, but even if they are supported, they probably won't help due to the way the first column will switch to the second column on the same sheet of paper once it's been filled (instead of keeping to the first column and going to the second page of paper which is what I would want).
I've given this problem so much time, and I'm at a complete loss. Something so simple is actually incredibly tricky to do properly. Unless I'm missing something obvious...
---------------- EDIT 1: This needs to work with non fixed-width fonts, so I can't just pad with spaces.
回答1:
The trick is easy: I've recently done the same thing (well, almost) - I had to create a text file that gives the impression that it has multiple columns (in a text file? sheesh)
whats the trick? padding!
Assume you have the values you stated earlier
1+1 2
70*8+5 565
1000000-300000 700000
76-10 66
Now, all you need to do is to set the width (in characters) of the left column, and the width for the right column. let's say both width's are 30 characters.
all you have to do now, is to construct a row as in the following pseudo code:
BEGIN-BUILD-TEXT
FOR EACH PAIR X, Y
STRING S1 = X.PADRIGHT(30);
STRING S2 = Y.PADRIGHT(30);
WRITE_LINE_TO_TEXTBOX(S1 + S2);
This way, the columns will be left aligned and with a fixed width.
Worked for me!
EDIT: Your RichTextBox
should use a fixed-width font (it has the same width for all characters).
回答2:
This solution is ugly, but it works. To have 2 columns in a RichTextBox where alignment needs to be correct even in printing AND where the left column itself may contain tabs, one needs to do the following. (There may exist another solution, but after researching for almost a solid few days, nothing came up.)
The general idea is to create a new Rtf string from the existing Rtf/Text in the RichTextBox. The new Rtf needs to define a different set of tab stops in each line. To understand what tab stops are, open up Wordpad, and see how you can click on the ruler to define them. Anyway, each tab stop is measured from the original RichTextBox input using GetPositionFromCharIndex(charIndex). You may want to store these in a 2D array of ints (line,tabstop number). Because Rtf tab stops are measured in twips, you need to convert from the value GetPositionFromCharIndex() returns (pixels) to twips using the following formula:
Graphics g = this.CreateGraphics();
double twips = pixels * 1440.0 / g.DpiX; // usually g.DpiX is 96
g.Dispose();
If the left column has say, 3 tabs, you need to insert 3 tab stops accordingly (\tx). By default, these values should be multiples of 720 (+1 for the pixel margin). You then need to set the last tab stop (a 4th tab stop) to your desired 2nd column position, and also add the extra final tab to the Rtf (using \tab). This tab stop value is derived by taking the position of the end of the longest line in the entire left column + a little padding. After the final tab in the Rtf, insert our right column 'answer' for that line. Make sure you insert "\pard" for that line too, otherwise the tab stops won't work. Each line ends with "\par", and for easier coding I think the tab stops can be anywhere in that line of the Rtf.
Okay, once you've followed the above instructions, although it looks fine on the screen (2 perfect columns), sometimes it still won't work properly when printed out (use print preview to check), due to the almost-but-not-entirely-wysiwig nature of RTB printing. However, there is a (kludgy) workaround. First of all, follow this tutorial to get printing in the first place: http://msdn.microsoft.com/en-us/library/ms996492.aspx
Now the trick is to 'stretch' each and every tabstop by a factor of at least 1.03. I use 1.05, but 1.1 may be safer still. The reason this is required is because (at least I find) printing stretches the text horizontally very slightly. Most of the time, this is harmless, but on occasion it will wreak havoc with the tabs. By stretching each tabstop position by 1.05, you're making sure that the text contained between tabstops will not 'overrun' past the next tabstop (thus causing following text to wrongly jump to the next tab after that).
Make sure that when debugging the above code, you create an independent bite-size test program which allows you to instantly see the results of the Rtf as you type (create 2 rich text boxes, the left one is the verbose Rtf code itself, and the right one is the DISPLAY of the aforementioned Rtf code). You can see changes instantly to either side if you use the TextChanged event for both RichTextBoxes. In a nutshell, type in the left, watch the output magically adjust on the right. Or type in the right and magically see the resulting Rtf text on the left. I can't stress enough how much time this has saved me when I work with the Rtf format generally.
来源:https://stackoverflow.com/questions/8804958/two-columns-in-winforms-richtextbox