netlib.narod.ru | < Назад | Оглавление | Далее > |
Позиции табуляции определяют, как метод DrawString интерпретирует символы табуляции Unicode '\t' или '\x0009'. Если в вызове DrawString нет аргумента StringFormat, табуляция, измеряемая в пунктах, равна четырехкратному размеру шрифта (иначе говоря, четырем размерам эм). Скажем, для 9-пунктного шрифта табуляция равна 36 пт (или полдюйма), для 18-пунктного шрифта — 72 пт (1 дюйм), 36-пунктный шрифт будет иметь табуляцию в 144 пт (2 дюйма). Позиции табуляции отсчитываются от начала текста, устанавливаемого аргументами PointF или RectangleF метода DrawString.
Если в вызове DrawString есть аргумент StringFormat, позиций табуляции по умолчанию нет, и метод игнорирует знаки табуляции в тексте. Позиции табуляции устанавливаются методом SetTabStops класса StringFormat. Класс StringFormat также включает метод для получения текущих параметров настройки позиций табуляции:
Методы StringFormat (выборочно)
void SetTabStops(float fFirstTab, float[] afTabs) |
float[] GetTabStops(out float fFirstTab) |
Позиции табуляции задаются в глобальных координатах. Заметьте: они указываются как отдельным значением с плавающей запятой, так и массивом значений с плавающей запятой, из чего можно сделать вывод, что отдельное значение интерпретируется особым образом. При этом последний элемент массива также интерпретируется особым образом, хотя из синтаксиса методов этого и не видно.
Позвольте привести несколько примеров, прежде чем мы рассмотрим работу метода SetTabStops в общем случае. Я буду подразумевать, что единицы измерения страницы установлены как GraphicsUnit.Point.
Если нужна только одна позиция табуляции, скажем, 4 дюйма (288 пт), ее величину можно задать в первом аргументе метода и определить массив, состоящий из единственного 0:
strfmt.SetTabStops(288, new float[] { 0 });
Нельзя присвоить массиву значение null. Вы можете также использовать:
strfmt.SetTabStops(0, new float[] { 288, 0 });
Если требуются две позиции табуляции, например, через 1 дюйм (72 пт) и 3 дюйма (216 пт), можно применить:
strfmt.SetTabStops(0, new float[] { 72, 144, 0 });
Заметьте: второй элемент массива равен разнице между 216 и 72 пт. Хотя в примере массив определяется прямо в методе SetTabStops, его, конечно, можно определить и вне метода.
Если вам нужны табуляции с приращением в полдюйма (36 пт), используйте:
strfmt.SetTabStops(0, new float[] { 36 });
Позиции табуляции будут равны 36 пт, 72 пт, 108 пт, 144 пт и т.д.
Таким образом, SetTabStops может определить как отдельные позиции табуляции, так и повторяющиеся, и именно комбинация этих двух вариантов делает этот метод столь сложным. В общем случае аргументы SetTabStops выглядят так:
strfmt.SetTabStops(S, new float[] { A, B, C, ... N, R});
Я использую букву R для обозначения повторения (Repeating) и букву S для обозначения сдвига (Shift). Обе эти величины могут быть равны 0 или отрицательным числам. Метод SetTabStops устанавливает позиции табуляции в следующих положениях, измеряемых от начальной позиции текста:
S + A
S + A + B
S + A + B + C
S + A + B + C + ... + N
S + A + B + C + ... + N + R
Метод также устанавливает позиции табуляции в положениях R, 2R, 3R и т.д., но эти повторяющиеся табуляции начинаются только после самой длинной из остальных табуляций. Например:
strfmt.SetTabStops(100, new float[] { 50, 75, 50, 100 });
устанавливает табуляции в позициях 150, 225, 275, 375, 400, 500, 600 и т.д. Размеры задаются в глобальных координатах.
Если вы хотите, чтобы все позиции табуляции были определены явно, установите последний элемент массива (который я назвал R) в 0. Вы также можете установить S в 0. Однако можно использовать этот первый аргумент метода SetTabStops для решения более сложных задач. Например, можно сначала определить массив, который состоит из четырех позиций табуляции, измеряемых от горизонтальной координаты 0:
float[] afTabs = { 100, 150, 100, 50, 0 };
Заметьте, что последний аргумент равен 0, поэтому повторяющихся позиций табуляции не будет.
Если хотите начать вывод текста в точке с координатой 0, вызовите SetTabStops с первым аргументом 0:
strfmt.SetTabStops(0, afTabs);
Этот метод установит позиции табуляции равными 100, 250, 350, 400 единицам. Если теперь понадобится выводить текст, начиная с координаты 50, но с теми же табуляциями, можно использовать –50 в качестве первого аргумента SetTabStops:
strfmt.SetTabStops(-50, afTabs);
Теперь позиции табуляции равны 50, 200, 300 и 350, но они отмеряются от начальной координаты 50, поэтому в действительности они равны 100, 250, 350 и 400, т.е. остались такими же, как прежде.
Давайте применим полученные знания на практике, разбив фрагмент текста на колонки. Текст взят из романа Эдит Уортон «Пора невинности» (1920). Следующий класс имеет единственное неизменяемое свойство Text, которое возвращает первые пять абзацев романа:
AgeOfInnocence.cs
//-------------------------------------------------------------------- // AgeOfInnocence.cs © 2001 by Charles Petzold; text by Edith Wharton //-------------------------------------------------------------------- class AgeOfInnocence { public static string Text { get { return "On a January evening of the early seventies, Christine Nilsson was " + "singing in Faust at the Academy of Music in New York." + "\n" + "\tThough there was already talk of the erection, in remote metropolitan " + "distances \"above the Forties,\" of a new Opera House which should " + "compete in costliness and splendour with those of the great European " + "capitals, the world of fashion was still content to reassemble every " + "winter in the shabby red and gold boxes of the sociable old Academy. " + "Conservatives cherished it for being small and inconvenient, and thus " + "keeping out the \"new people\" whom New York was beginning to dread and " + "yet be drawn to; and the sentimental clung to it for its historic " + "associations, and the musical for its excellent acoustics, always so " + "problematic a quality in halls built for the hearing of music." + "\n" + "\tIt was Madame Nilsson's first appearance that winter, and what the " + "daily press had already learned to describe as \"an exceptionally " + "brilliant audience\" had gathered to hear her, transported through the " + "slippery, snowy streets in private broughams, in the spacious family " + "landau, or in the humbler but more convenient \"Brown &c&o&u&p&é.\" To " + "come to the Opera in a Brown &c&o&u&p&é was almost as honourable a way " + "of arriving as in one's own carriage; and departure by the same means " + "had the immense advantage of enabling one (with a playful allusion to " + "democratic principles) to scramble into the first Brown conveyance in " + "the line, instead of waiting till the cold-and-gin congested nose of " + "one's own coachman gleamed under the portico of the Academy. It was one " + "of the great livery-stableman's most masterly intuitions to have " + "discovered that Americans want to get away from amusement even more " + "quickly than they want to get to it." + "\n" + "\tWhen Newland Archer opened the door at the back of the club box the " + "curtain had just gone up on the garden scene. There was no reason why " + "the young man should not have come earlier, for he had dined at seven, " + "alone with his mother and sister, and had lingered afterward over a " + "cigar in the Gothic library with glazed black-walnut bookcases and " + "finial-topped chairs which was the only room in the house where Mrs. " + "Archer allowed smoking. But, in the first place, New York was a " + "metropolis, and perfectly aware that in metropolises it was \"not the " + "thing\" to arrive early at the opera; and what was or was not \"the " + "thing\" played a part as important in Newland Archer's New York as the " + "inscrutable totem terrors that had ruled the destinies of his " + "forefathers thousands of years ago." + "\n" + "\tThe second reason for his delay was a personal one. He had dawdled " + "over his cigar because he was at heart a dilettante, and thinking over a " + "pleasure to come often gave him a subtler satisfaction than its " + "realisation. This was especially the case when the pleasure was a " + "delicate one, as his pleasures mostly were; and on this occasion the " + "moment he looked forward to was so rare and exquisite in quality " + "that\x2014well, if he had timed his arrival in accord with the prima " + "donna's stage-manager he could not have entered the Academy at a more " + "significant moment than just as she was singing: \"He loves me\x2014he " + "loves me not\x2014&h&e& &l&o&v&e&s& &m&e!\" and sprinkling the falling " + "daisy petals with notes as clear as dew." + "\n"; } } }
Обратите внимание на знаки табуляции: они формируют отступ первой строки каждого абзаца, кроме первого. Оригинальный текст содержит несколько слов, выделенных курсивом, Я использовал амперсанды (как рассказывалось ранее), чтобы выделить эти несколько слов подчеркиванием вместо курсива. Текст содержит также несколько длинных тире.
Ниже приведена программа, которая форматирует этот текст в колонки. Каждая колонка требует вызова метода DrawString. В начальном варианте этой программы в качестве единиц измерения я использовал цицеро (pica). Цицеро равен 12 пт или (в компьютерной типографии) 1/6 дюйма. В этих единицах обычно измеряется ширина колонок в журналах и газетах. Я решил сделать колонки шириной 12 цицеро (т.е. 2 дюйма) с расстояниями между колонками в 1 цицеро. В то время когда эта глава писалась, преобразование в цицеро вызывало проблемы в методе DrawString, поэтому я изменил программу так, чтобы в качестве единиц измерения использовались пункты.
TextColumns.cs
//-------------------------------------------- // TextColumns.cs (C) 2001 by Charles Petzold //-------------------------------------------- using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Windows.Forms; class TextColumns: PrintableForm { public new static void Main() { Application.Run(new TextColumns()); } public TextColumns() { Text = "Edith Wharton's \"The Age of Innocence\""; Font = new Font("Times New Roman", 10); } protected override void DoPage(Graphics grfx, Color clr, int cx, int cy) { Brush brush = new SolidBrush(clr); int iChars, iLines; string str = AgeOfInnocence.Text; StringFormat strfmt = new StringFormat(); // Делаем пункты единицами измерения для преобразования размеров PointF[] aptf = { new PointF(cx, cy) }; grfx.TransformPoints(CoordinateSpace.Device, CoordinateSpace.Page, aptf); grfx.PageUnit = GraphicsUnit.Point; grfx.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, aptf); float fcx = aptf[0].X; float fcy = aptf[0].Y; // Свойства, флаги и позиции табуляции StringFormat strfmt.HotkeyPrefix = HotkeyPrefix.Show; strfmt.Trimming = StringTrimming.Word; strfmt.FormatFlags |= StringFormatFlags.NoClip; strfmt.SetTabStops(0, new float[] { 18 }); // Отображение текста for (int x = 0; x < fcx && str.Length > 0; x += 156) { RectangleF rectf = new RectangleF(x, 0, 144, fcy - Font.GetHeight(grfx)); grfx.DrawString(str, Font, brush, rectf, strfmt); grfx.MeasureString(str, Font, rectf.Size, strfmt, out iChars, out iLines); str = str.Substring(iChars); } } }
Обратите внимание на свойства, флаги и табуляции StringFormat. HotkeyPrefix.Show используется для подчеркивания слов, StringTrimming.Word — чтобы в конце колонок не обрезались слова, и StringFormatFlags.NoClip — чтобы строчки не обрезались внизу прямоугольника. Единственную позицию табуляции (отступ первой строки) я установил в 18 пт.
Цикл for повторяется для каждой колонки. Он продолжается до тех пор, пока не достигается правый край клиентской области или не выводится весь текст. Внутри цикла for высота прямоугольника вычисляется, как высота клиентской области за вычетом одной строки текста. Метод DrawString использует этот прямоугольник. Метод MeasureString определяет, сколько текста вывел метод DrawString. Метод Substring класса String подготавливает строку для следующей итерации цикла.
Вот что мы увидим на экране:
netlib.narod.ru | < Назад | Оглавление | Далее > |