Как распечатать ListView с большим содержимым в C#
Одна из трудностей при печати таких данных заключается в том, что на экране может храниться гораздо больше данных, чем на печатной странице, поэтому трудно вместить все. Предыдущий пример обрабатывает эти проблемы, предполагая, что все данные могут поместиться в доступной области , В частности, предполагается, что каждый элемент данных может вписываться в одну строку внутри текущей ширины столбца.
В этом примере используется другой подход. Это делает каждую часть данных подходящей в пределах ее ширины столбца, но позволяет элементам данных охватывать несколько строк, если это необходимо.
Основной подход для каждой строки состоит в том, чтобы нарисовать позиции строки в разрешенных ширинах столбцов и измерить объем вертикального пространства, который им нужен. После рисования всех элементов программа рисует вокруг них квадраты, которые высоки, чем элемент, который требует наибольшего вертикального пространства. Затем код начинает рисовать следующую строку данных ниже самого высокого элемента.
PrintMultiLineData метод расширения показано в следующем коде управляет процессом печати.
// Распечатываем данные ListView в указанном месте // позволяет передавать данные по нескольким строкам. public static void PrintMultiLineData(this ListView lvw, Point location, Graphics gr, Brush header_brush, Brush data_brush, Pen grid_pen) { const int x_margin = 5; const int y_margin = 3; float x = location.X; float y = location.Y; // Получите горизонтальное разрешение экрана. float screen_res_x; using (Graphics screen_gr = lvw.CreateGraphics()) { screen_res_x = screen_gr.DpiX; } // Коэффициент масштабирования для преобразования из пикселов экрана // к единицам печати (100 дюймов дюймов) float screen_to_printer = 100 / screen_res_x; // Получите ширину столбцов в единицах принтера. float[] col_wids = new float[lvw.Columns.Count]; for (int i = 0; i < lvw.Columns.Count; i++) col_wids[i] = (lvw.Columns[i].Width + 2 * x_margin) * screen_to_printer; int num_columns = lvw.Columns.Count; using (StringFormat string_format = new StringFormat()) { // Нарисуйте заголовки столбцов. string_format.Alignment = StringAlignment.Center; string_format.LineAlignment = StringAlignment.Center; var header_query = from ColumnHeader column in lvw.Columns select column.Text; DrawMultiLineItems(header_query.ToArray(), gr, lvw.Font, header_brush, grid_pen, x_margin, y_margin, x, ref y, col_wids, num_columns, string_format); // Нарисуйте данные. string_format.Alignment = StringAlignment.Near; foreach (ListViewItem item in lvw.Items) { var subitems_query = from ListViewItem.ListViewSubItem subitem in item.SubItems select subitem.Text; DrawMultiLineItems(subitems_query.ToArray(), gr, lvw.Font, data_brush, grid_pen, x_margin, y_margin, x, ref y, col_wids, num_columns, string_format); } } }
PrintMultiLineData - это метод расширения, который расширяет класс ListView. В качестве параметров требуется точка, указывающая верхний левый угол, где должен начинаться рисование, графический объект, на который нужно рисовать, и кисти и ручки для рисования.
Сначала метод получает горизонтальное разрешение экрана и использует его для расчета масштабного коэффициента для преобразования из пикселей, используемых для измерения ширины столбцов, на устройства печати. (Для получения дополнительной информации см. Мой предыдущий пост.
Затем код копирует ширину столбца элемента управления ListView в массив, добавляя дополнительную область для полей и масштабируя результат до единиц принтера.
Этот метод создает объект StringFormat для управления тем, как текст выравнивается во время печати, а затем начинает печать.
Метод использует запрос LINQ для выбора текста, отображаемого заголовками столбцов элемента управления ListView. Он передает массив этих текстовых значений, а также некоторые другие параметры, описанные вкратце, в метод DrawMultiLineItems для печати.
Затем для каждой строки данных код использует запрос LINQ для выбора текстовых значений, отображаемых подпозициями в этой строке. (Обратите внимание, что первый подпункт содержит текст основного элемента, поэтому вам не нужно рассматривать этот элемент как особый случай.) Затем код вызывает метод DrawMultiLineItems, передавая ему массив текстовых значений подпозиции плюс другие параметры.
Следующий код показывает метод DrawMultiLineItems.
// Нарисуйте элементы в строке. private static void DrawMultiLineItems(string[] items, Graphics gr, Font lvw_font, Brush header_brush, Pen grid_pen, float x_margin, float y_margin, float x0, ref float y0, float[] col_wids, int num_columns, StringFormat string_format) { float row_height = 0; float x = x0; for (int i = 0; i < num_columns; i++) { // Измерьте размер, необходимый для текста. float text_width = col_wids[i] - 2 * x_margin; SizeF layout_area = new SizeF(col_wids[i], 1000); SizeF row_size = gr.MeasureString(items[i], lvw_font, layout_area); if (row_height < row_size.Height) row_height = row_size.Height; // Нарисуйте текст. RectangleF rect = new RectangleF( x + x_margin, y0 + y_margin, text_width, row_size.Height); gr.DrawString(items[i], lvw_font, header_brush, rect, string_format); // Нарисуйте следующий столбец. x += col_wids[i]; } // Добавьте дополнительную комнату для вертикального края. row_height += 2 * y_margin; // Нарисуйте прямоугольники вокруг заголовков столбцов. x = x0; for (int i = 0; i < num_columns; i++) { //Нарисуйте коробку. RectangleF rect = new RectangleF( x, y0, col_wids[i], row_height); gr.DrawRectangle(grid_pen, rect); // Draw the next column. x += col_wids[i]; } // Подготовьтесь к следующей строке. y0 += row_height; }
Метод DrawMultiLineItems рисует одну строку элементов данных. Переменная row_height отслеживает наибольшую высоту, необходимую любому элементу в строке.
Для каждого столбца в строке код использует метод MeasureString объекта Graphics, чтобы узнать, сколько текста в столбце этого столбца требуется. Если для текста требуется больше вертикального пространства, чем row_height, код обновляет row_height. Затем код рисует текст элемента в требуемом пространстве.
После того, как он нарисовал текст для каждого элемента данных, код рисует поля вокруг элементов. Он просто перебирает элементы, рисующие поля, используя максимальную высоту row_height для каждой высоты окна.
После того, как он нарисовал все поля, код добавляет высоту строки к начальной координате y0, поэтому следующая строка начнется ниже этой.
Это улучшает предыдущий пример, но у него все еще есть некоторые проблемы. В частности, этот метод предполагает, что все данные могут помещаться на одну страницу, чтобы он не разбивал печать на несколько страниц по вертикали или по горизонтали. Такой вид печати будет зависеть от ваших конкретных потребностей, поэтому я не буду здесь его освещать. Вам нужна такая печать, вы можете изменить этот пример или отправить мне по электронной почте, если хотите, чтобы я проконсультировался за вас.
(Обратите внимание, что вы можете повторно использовать метод DrawMultiLineItems для рисования другой строки данных, где элементы могут иметь разную высоту.)