Опубликован: 11.09.2006 | Уровень: специалист | Доступ: платный
Лекция 6:

Работа с печатью и изображениями

Первая область — полный размер страницы, обычно имеющий названия, например, формат A4 или A3. Информацию об этой области предоставляет свойство PageBounds класса PrintPageEventArgs. Его значением является объект Rectangle, свойства X и У которого равны 0, а свойства Width и Height предоставляют размеры бумаги по умолчанию, выраженные в сотых долях дюйма. Вторая область связана с конструктивными особенностями принтеров — большинство из них используют для печати площадь, меньшую размеров бумаги. Область печати определяется значением свойства VisibleClipBounds класса Graphics, представляемая объектом RectangleF. Свойства Х и У этого объекта имеют значения 0, а ее свойства Width и Height равны горизонтальному и вертикальному размерам области печати страницы. В третьей области помещается текст, она обычно определяется пользователем. В Microsoft Word, например, мы устанавливаем заданные значения полей. По умолчанию, эта область рассчитывается с отступом в один дюйм по периметру страницы 121 дюйм приблизительно равен 2,54 см.. Ее размер возвращается объектом Rectangle свойства MarginBounds.

В любом случае, области формируются объектами Rectangle или RectangleF, конструкторы которых могут иметь следующий вид:

Rectangle (int x, int y, int width, int height);

RectangleF(float x, float y, float width, float height);

Эти конструкторы отличаются только типом данных, формирующих структуру. Координатные оси области, представляющей собой прямоугольник, направлены следующим образом (рис. 6.3):

Расположение координатных осей и формирование объекта Rectangle

Рис. 6.3. Расположение координатных осей и формирование объекта Rectangle

Вернемся к нашему коду. В качестве области печати устанавливаем объект rectanglefFull:

if (graph.VisibleClipBounds.X<0) rectanglefFull = e.MarginBounds;
else
//Определяем   объект  rectanglefFull
rectanglefFull = new RectangleF(
//Устанавливаем координату  X  
e.MarginBounds.Left — (e.PageBounds.Width — graph.VisibleClipBounds.Width)/2, 
//Устанавливаем координату  Y
.MarginBounds.Top — (e.PageBounds.Height — graph.VisibleClipBounds.Height)/2, 
//Устанавливаем ширину области
e.MarginBounds.Width,
//Устанавливаем высоту области
e.MarginBounds.Height);

Область текста будет представлять собой копию области rectanglefFull c учетом высоты шрифта:

rectanglefText = RectangleF.Inflate(rectanglefFull, 0, -2*HeightFont);
//Определяем число строк
int NumDisplayLines = (int)Math.Floor(rectanglefText.Height/HeightFont);
//Устанавливаем высоту области
rectanglefText.Height = NumDisplayLines*HeightFont;

Элемент RichTextBox обладает свойством WordWrap, при установке значения True которого текст автоматически переносится на новую строку. Однако разбитый на строки текст может выйти за границы предназначенной для него области rectanglefText. Чтобы этого не произошло, мы создали экземпляр stringformat класса StringFormat, для которого теперь устанавливаем значение Trimming:

if (rtbText.WordWrap)
  {
    stringformat.Trimming = StringTrimming.Word;
  }
else
  {
    stringformat.Trimming = StringTrimming.EllipsisCharacter;
    stringformat.FormatFlags |=StringFormatFlags.NoWrap;
  }

Далее код достаточно простой, привожу его с комментариями:

//При печати выбранных страниц переходим к первой стартовой странице
while ((PageNumber<StartPage)&&(stringPrintText.Length>0))
    {
if(rtbText.WordWrap)
//Измеряем текстовые переменные, 
//формирующие печать,  и возвращаем число символов NumberSymbols
//и число строк NumberLines
graph.MeasureString(stringPrintText, font, rectanglefText.Size, stringformat, out NumberSymbols, out NumberLines);
else
    NumberSymbols = SymbolsInLines(stringPrintText, NumDisplayLines);
    stringPrintText = stringPrintText.Substring(NumberSymbols);
    //Увеличиваем число страниц 
    PageNumber++;
  }
//Если длина строки stringPrintText равняется нулю (нет текста для печати),
// останавливаем печать
    if (stringPrintText.Length==0)
      {
        e.Cancel = true;
        return;
      }
//Выводим (рисуем) текст для печати — stringPrintText, используем для этого шрифт font,
//кисть черного цвета  — Brushes.Black, область печати — rectanglefText,
//передаем строку  дополнительного форматирования stringformat
graph.DrawString(stringPrintText, font, Brushes.Black, rectanglefText, stringformat);
//Получаем текст для следующей страницы
  if (rtbText.WordWrap)
  graph.MeasureString(stringPrintText, font, rectanglefText.Size, stringformat, out NumberSymbols, out NumberLines);
  else
  NumberSymbols = SymbolsInLines(stringPrintText, NumDisplayLines);
  stringPrintText = stringPrintText.Substring(NumberSymbols);
//Очищаем объект stringformat, использованный для формирования полей.
    stringformat = new StringFormat();
//Добавляем  вывод на каждую страницу ее номера
    stringformat.Alignment = StringAlignment.Far;
    graph.DrawString("Страница" + PageNumber, font, Brushes.Black, rectanglefFull, stringformat);
    PageNumber++;
//Cнова проверяем, имеется ли текст для печати и номер страницы, заданной для печати
    e.HasMorePages=(stringPrintText.Length>0)&&(PageNumber<StartPage+NumPages);
//Для печати из окна предварительного просмотра  снова инициализируем переменные
    if(!e.HasMorePages)
    {
      stringPrintText = rtbText.Text;
      StartPage = 1;
      NumPages = printDialog1.PrinterSettings.MaximumPage;
      PageNumber = 1;
      }

      
    }
Листинг 6.4.

Мы закончили рассмотрение метода printDocument1_PrintPage. Достаточно существенным моментом здесь является то, что в коде реализована проверка наличия расстановки переносов в текстовом поле RichTextBox (свойство WordWrap). Метод MeasureString возвращает число выводимых символов. Для учета символов, не попадающих в область rectanglefText, добавляем метод SymbolsInLines:

int SymbolsInLines(string stringPrintText, int NumLines)
  {
    int index = 0;
    for (int i = 0; i< NumLines; i++)
    {
      index = 1+ stringPrintText.IndexOf('\n', index);
      if(index==0)
        return stringPrintText.Length;
    }
    return index;
  }

Создаем обработчики событий пунктов mnuPageSetup и mnuPrintPreview главного меню:

private void mnuPageSetup_Click(object sender, System.EventArgs e)
  {
    //Показываем диалог
    pageSetupDialog1.ShowDialog();
  }
    private void mnuPrintPreview_Click(object sender, System.EventArgs e)
  {
    //Инициализируем переменные
    printDocument1.DocumentName = Text;
    stringPrintText = rtbText.Text;
    StartPage = 1;
    NumPages = printDialog1.PrinterSettings.MaximumPage;
    PageNumber = 1;
    //Показываем диалог
    printPreviewDialog1.ShowDialog();
  }

В обработчике пункта меню mnuPrint определяем диапазон страниц – все страницы, ряд или выделенная область:

private void mnuPrint_Click(object sender, System.EventArgs e)
  {
    printDialog1.AllowSelection = rtbText.SelectionLength >0;
      if(printDialog1.ShowDialog()==DialogResult.OK)
    {
      printDocument1.DocumentName =Text;
      //Определяем диапазон страниц для печати
      switch(printDialog1.PrinterSettings.PrintRange)
      {
        //Выбраны все страницы
        case PrintRange.AllPages:
          stringPrintText = rtbText.Text;
          StartPage = 1;
          NumPages = printDialog1.PrinterSettings.MaximumPage;
          break;
        //Выбрана выделенная область
        case PrintRange.Selection:
          stringPrintText = rtbText.SelectedText;
          StartPage = 1;
          NumPages = printDialog1.PrinterSettings.MaximumPage;
          break;
        //Выбран ряд страниц
        case PrintRange.SomePages:
          stringPrintText = rtbText.Text;
          StartPage = printDialog1.PrinterSettings.FromPage;
          NumPages = printDialog1.PrinterSettings.ToPage - StartPage+1;
          break;
      }
      PageNumber = 1;
      //Вызываем встроенный метод для начала печати
      printDocument1.Print();
    }
    
  }
Листинг 6.5.

Запускаем приложение. Изменения, сделанные в диалоговом окне "Параметры страницы" (рис. 6.4), сохраняются, и в окне предварительного просмотра можно видеть подготовленную для печати страницу (рис. 6.5).

Настройка параметров страницы

Рис. 6.4. Настройка параметров страницы
Окно предварительного просмотра. На каждой странице выводится ее номер

увеличить изображение
Рис. 6.5. Окно предварительного просмотра. На каждой странице выводится ее номер

Из окна предварительного просмотра можно сразу перейти к печати. При выборе пункта печати доступен выбор ряда страниц или печать выделенного фрагмента (рис. 6.6).

Настройка параметров печати

Рис. 6.6. Настройка параметров печати

Все то, что мы видим на экране в диалоговом окне предварительного просмотра, теоретически должно отображаться точно так же и на бумаге. Однако при реализации собственной печати, наверное, хочется проверить все до самого конца — до вывода на бумагу. В самом конце разработки это действительно стоит сделать, но на промежуточных этапах работа с принтером может занять много времени. Облегчить задачу поможет программа pdfFactoryPro — при ее установке в системе появляется виртуальный принтер, позволяющий сохранять документы в формате pdf (рис. 6.7).

Вывод документа в окно виртуального принтера программы pdfFactoryPro

увеличить изображение
Рис. 6.7. Вывод документа в окно виртуального принтера программы pdfFactoryPro

В конце лекции приводится полный листинг приложения TextEditor, а исходный проект имеется на диске, прилагаемом к книге (Code\Glava6\TextEditor).

Елена Дьяконова
Елена Дьяконова

При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: 

Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll

Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан.

Затем:

Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll

Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз.

Александр Сороколет
Александр Сороколет

Свойство WindowState формы blank Maximized. Не открывается почемуто на всё окно, а вот если последующую форму бланк открыть уже на макс открывается :-/

Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000