Программное создание графика (диаграммы) в VBA Excel с помощью метода Charts.Add на основе данных из диапазона ячеек на рабочем листе. Примеры.
Метод Charts.Add
В настоящее время на сайте разработчиков описывается метод Charts.Add2, который, очевидно, заменил метод Charts.Add. Тесты показали, что Charts.Add продолжает работать в новых версиях VBA Excel, поэтому в примерах используется именно он.
Синтаксис
Charts.Add ([Before], [After], [Count]) |
Charts.Add2 ([Before], [After], [Count], [NewLayout]) |
Параметры
Параметры методов Charts.Add и Charts.Add2:
Параметр | Описание |
---|---|
Before | Имя листа, перед которым добавляется новый лист с диаграммой. Необязательный параметр. |
After | Имя листа, после которого добавляется новый лист с диаграммой. Необязательный параметр. |
Count | Количество добавляемых листов с диаграммой. Значение по умолчанию – 1. Необязательный параметр. |
NewLayout | Если NewLayout имеет значение True, диаграмма вставляется с использованием новых правил динамического форматирования (заголовок имеет значение «включено», а условные обозначения – только при наличии нескольких рядов). Необязательный параметр. |
Если параметры Before и After опущены, новый лист с диаграммой вставляется перед активным листом.
Примеры
Таблицы
В качестве источников данных для примеров используются следующие таблицы:
Пример 1
Программное создание объекта Chart с типом графика по умолчанию и по исходным данным из диапазона «A2:B26»:
Sub Primer1() Dim myChart As Chart ‘создаем объект Chart с расположением нового листа по умолчанию Set myChart = ThisWorkbook.Charts.Add With myChart ‘назначаем объекту Chart источник данных .SetSourceData (Sheets(«Лист1»).Range(«A2:B26»)) ‘переносим диаграмму на «Лист1» (отдельный лист диаграммы удаляется) .Location xlLocationAsObject, «Лист1» End With End Sub |
Результат работы кода VBA Excel из первого примера:
Пример 2
Программное создание объекта Chart с двумя линейными графиками по исходным данным из диапазона «A2:C26»:
Sub Primer2() Dim myChart As Chart Set myChart = ThisWorkbook.Charts.Add With myChart .SetSourceData (Sheets(«Лист1»).Range(«A2:C26»)) ‘задаем тип диаграммы (линейный график с маркерами) .ChartType = xlLineMarkers .Location xlLocationAsObject, «Лист1» End With End Sub |
Результат работы кода VBA Excel из второго примера:
Пример 3
Программное создание объекта Chart с круговой диаграммой, разделенной на сектора, по исходным данным из диапазона «E2:F7»:
Sub Primer3() Dim myChart As Chart Set myChart = ThisWorkbook.Charts.Add With myChart .SetSourceData (Sheets(«Лист1»).Range(«E2:F7»)) ‘задаем тип диаграммы (пирог — круг, разделенный на сектора) .ChartType = xlPie ‘задаем стиль диаграммы (с отображением процентов) .ChartStyle = 261 .Location xlLocationAsObject, «Лист1» End With End Sub |
Результат работы кода VBA Excel из третьего примера:
Примечание
В примерах использовались следующие методы и свойства объекта Chart:
Компонент | Описание |
---|---|
Метод SetSourceData | Задает диапазон исходных данных для диаграммы. |
Метод Location | Перемещает диаграмму в заданное расположение (новый лист, существующий лист, элемент управления). |
Свойство ChartType | Возвращает или задает тип диаграммы. Смотрите константы. |
Свойство ChartStyle | Возвращает или задает стиль диаграммы. Значение нужного стиля можно узнать, записав макрос. |
Improve Article
Save Article
Like Article
Improve Article
Save Article
Like Article
Prerequisite: Python | Plotting charts in excel sheet using openpyxl module | Set – 1 Openpyxl is a Python library using which one can perform multiple operations on excel files like reading, writing, arithmetic operations and plotting graphs. Charts are composed of at least one series of one or more data points. Series themselves are comprised of references to cell ranges. Let’s see how to plot Scatter, Bubble, Pie, 3D Pie Chart on an excel sheet using openpyxl. For plotting the charts on an excel sheet, firstly, create chart object of specific chart class( i.e ScatterChart, PieChart etc.). After creating chart objects, insert data in it and lastly, add that chart object in the sheet object. Let’s see how to plot different charts using realtime data. Code #1 : Plot the Bubble Chart. Bubble charts are similar to scatter charts but use a third dimension to determine the size of the bubbles. Charts can include multiple series. For plotting the bubble chart on an excel sheet, use BubbleChart class from openpyxl.chart submodule.
Python3
import
openpyxl
from
openpyxl.chart
import
BubbleChart, Reference, Series
wb
=
openpyxl.Workbook()
sheet
=
wb.active
rows
=
[
("Number of Products", "Sales
in
USD", "Market share"),
(
14
,
12200
,
15
),
(
20
,
60000
,
33
),
(
18
,
24400
,
10
),
(
22
,
32000
,
42
),
]
for
row
in
rows:
sheet.append(row)
chart
=
BubbleChart()
xvalues
=
Reference(sheet, min_col
=
1
,
min_row
=
2
, max_row
=
5
)
yvalues
=
Reference(sheet, min_col
=
2
,
min_row
=
2
, max_row
=
5
)
size
=
Reference(sheet, min_col
=
3
,
min_row
=
2
, max_row
=
5
)
series
=
Series(values
=
yvalues, xvalues
=
xvalues,
zvalues
=
size, title
=
"
2013
")
chart.series.append(series)
chart.title
=
" BUBBLE
-
CHART "
chart.x_axis.title
=
" X_AXIS "
chart.y_axis.title
=
" Y_AXIS "
sheet.add_chart(chart, "E2")
wb.save("bubbleChart.xlsx")
Output: Code #2 : Plot the Scatter Chart Scatter, or xy charts are similar to some line charts. For plotting the Scatter chart on an excel sheet, use ScatterChart class from openpyxl.chart submodule.
Python3
import
openpyxl
from
openpyxl.chart
import
ScatterChart, Reference, Series
wb
=
openpyxl.Workbook()
sheet
=
wb.active
rows
=
[
("Number of Products", "Sales
in
USD", "Market share"),
(
14
,
12200
,
15
),
(
20
,
60000
,
33
),
(
18
,
24400
,
10
),
(
22
,
32000
,
42
),
]
for
row
in
rows:
sheet.append(row)
chart
=
ScatterChart()
xvalues
=
Reference(sheet, min_col
=
1
,
min_row
=
2
, max_row
=
5
)
yvalues
=
Reference(sheet, min_col
=
2
,
min_row
=
2
, max_row
=
5
)
size
=
Reference(sheet, min_col
=
3
,
min_row
=
2
, max_row
=
5
)
series
=
Series(values
=
yvalues, xvalues
=
xvalues,
zvalues
=
size, title
=
"
2013
")
chart.series.append(series)
chart.title
=
" SCATTER
-
CHART "
chart.x_axis.title
=
" X_AXIS "
chart.y_axis.title
=
" Y_AXIS "
sheet.add_chart(chart, "E2")
wb.save(" ScatterChart.xlsx")
Output: Code #3 : Plot the Pie Chart Pie charts plot data as slices of a circle with each slice representing the percentage of the whole. Slices are plotted in a clockwise direction with 0° being at the top of the circle. Pie charts can only take a single series of data. For plotting the Pie chart on an excel sheet, use PieChart class from openpyxl.chart submodule.
Python
import
openpyxl
from
openpyxl.chart
import
PieChart, Reference
wb
=
openpyxl.Workbook()
sheet
=
wb.active
datas
=
[
[
'Pie'
,
'Sold'
],
[
'Apple'
,
50
],
[
'Cherry'
,
30
],
[
'Pumpkin'
,
10
],
[
'Chocolate'
,
40
],
]
for
row
in
datas:
sheet.append(row)
chart
=
PieChart()
labels
=
Reference(sheet, min_col
=
1
,
min_row
=
2
, max_row
=
5
)
data
=
Reference(sheet, min_col
=
2
,
min_row
=
1
, max_row
=
5
)
chart.add_data(data, titles_from_data
=
True
)
chart.set_categories(labels)
chart.title
=
" PIE
-
CHART "
sheet.add_chart(chart, "E2")
wb.save(" PieChart.xlsx")
Output: Code #4: Plot the Bar Chart For plotting the 3D pie chart on an excel sheet, use PieChart3D class from openpyxl.chart submodule.
Python3
import
openpyxl
from
openpyxl.chart
import
PieChart3D, Reference
wb
=
openpyxl.Workbook()
sheet
=
wb.active
datas
=
[
[
'Pie'
,
'Sold'
],
[
'Apple'
,
50
],
[
'Cherry'
,
30
],
[
'Pumpkin'
,
10
],
[
'Chocolate'
,
40
],
]
for
row
in
datas:
sheet.append(row)
chart
=
PieChart3D()
labels
=
Reference(sheet, min_col
=
1
,
min_row
=
2
, max_row
=
5
)
data
=
Reference(sheet, min_col
=
2
,
min_row
=
1
, max_row
=
5
)
chart.add_data(data, titles_from_data
=
True
)
chart.set_categories(labels)
chart.title
=
"
3DPIE
-
CHART "
sheet.add_chart(chart, "E2")
wb.save("
3DPieChart
.xlsx")
Output:
Like Article
Save Article
Vote for difficulty
Current difficulty :
Medium
Создание диаграммы с диапазонами и фиксированное имя
Графики могут быть созданы путем непосредственной работы с объектом Series
который определяет данные диаграммы. Чтобы перейти к Series
без диаграммы exisitng, вы создаете ChartObject
на данном Worksheet
а затем получаете объект Chart
из него. Поверхность работы с объектом « Series
заключается в том, что вы можете установить Values
и XValues
, обратившись к объектам Range
. Эти свойства данных будут правильно определять Series
со ссылками на эти диапазоны. Недостатком этого подхода является то, что при настройке Name
не обрабатывается одно и то же преобразование; это фиксированное значение. Он не будет корректироваться с базовыми данными в исходном Range
. Проверка формулы SERIES
и очевидно, что имя исправлено. Это необходимо обработать, создав формулу SERIES
напрямую.
Код, используемый для создания диаграммы
Обратите внимание, что этот код содержит объявления дополнительных переменных для Chart
и Worksheet
. Они могут быть опущены, если они не используются. Они могут быть полезны, однако, если вы изменяете стиль или любые другие свойства диаграммы.
Sub CreateChartWithRangesAndFixedName()
Dim xData As Range
Dim yData As Range
Dim serName As Range
'set the ranges to get the data and y value label
Set xData = Range("B3:B12")
Set yData = Range("C3:C12")
Set serName = Range("C2")
'get reference to ActiveSheet
Dim sht As Worksheet
Set sht = ActiveSheet
'create a new ChartObject at position (48, 195) with width 400 and height 300
Dim chtObj As ChartObject
Set chtObj = sht.ChartObjects.Add(48, 195, 400, 300)
'get reference to chart object
Dim cht As Chart
Set cht = chtObj.Chart
'create the new series
Dim ser As Series
Set ser = cht.SeriesCollection.NewSeries
ser.Values = yData
ser.XValues = xData
ser.Name = serName
ser.ChartType = xlXYScatterLines
End Sub
Исходные данные / диапазоны и итоговая Chart
после Chart
кода
Обратите внимание, что формула SERIES
включает в себя "B"
для названия серии вместо ссылки на Range
который ее создал.
Создание пустой диаграммы
Отправной точкой для подавляющего большинства графического кода является создание пустой Chart
. Обратите внимание , что эта Chart
является предметом шаблона диаграммы по умолчанию , который является активным и не может на самом деле быть пустым (если шаблон был изменен).
Ключ к ChartObject
определяет его местоположение. Синтаксис вызова — ChartObjects.Add(Left, Top, Width, Height)
. После создания ChartObject
вы можете использовать его объект Chart
для фактического изменения диаграммы. ChartObject
ведет себя больше как Shape
чтобы расположить диаграмму на листе.
Код для создания пустой диаграммы
Sub CreateEmptyChart()
'get reference to ActiveSheet
Dim sht As Worksheet
Set sht = ActiveSheet
'create a new ChartObject at position (0, 0) with width 400 and height 300
Dim chtObj As ChartObject
Set chtObj = sht.ChartObjects.Add(0, 0, 400, 300)
'get refernce to chart object
Dim cht As Chart
Set cht = chtObj.Chart
'additional code to modify the empty chart
'...
End Sub
Результирующая диаграмма
Создание диаграммы путем изменения формулы SERIES
Для полного контроля над новым объектом Chart
и Series
(особенно для динамического названия Series
) вы должны прибегнуть к модификации формулы SERIES
напрямую. Процесс создания объектов Range
является простым, и основным препятствием является просто построение строки для формулы SERIES
.
Формула SERIES
принимает следующий синтаксис:
=SERIES(Name,XValues,Values,Order)
Это содержимое может быть предоставлено в виде ссылок или значений массива для элементов данных. Order
представляет собой серию позиций в диаграмме. Обратите внимание, что ссылки на данные не будут работать, если они не полностью соответствуют имени листа. Для примера рабочей формулы щелкните любую существующую серию и проверьте панель формул.
Код для создания диаграммы и настройки данных с использованием формулы SERIES
Обратите внимание, что построение строки для создания формулы SERIES
использует .Address(,,,True)
. Это гарантирует, что ссылка внешнего диапазона используется так, чтобы был включен полный адрес с именем листа. Вы получите сообщение об ошибке, если имя листа исключено .
Sub CreateChartUsingSeriesFormula()
Dim xData As Range
Dim yData As Range
Dim serName As Range
'set the ranges to get the data and y value label
Set xData = Range("B3:B12")
Set yData = Range("C3:C12")
Set serName = Range("C2")
'get reference to ActiveSheet
Dim sht As Worksheet
Set sht = ActiveSheet
'create a new ChartObject at position (48, 195) with width 400 and height 300
Dim chtObj As ChartObject
Set chtObj = sht.ChartObjects.Add(48, 195, 400, 300)
'get refernce to chart object
Dim cht As Chart
Set cht = chtObj.Chart
'create the new series
Dim ser As Series
Set ser = cht.SeriesCollection.NewSeries
'set the SERIES formula
'=SERIES(name, xData, yData, plotOrder)
Dim formulaValue As String
formulaValue = "=SERIES(" & _
serName.Address(, , , True) & "," & _
xData.Address(, , , True) & "," & _
yData.Address(, , , True) & ",1)"
ser.Formula = formulaValue
ser.ChartType = xlXYScatterLines
End Sub
Исходные данные и итоговая диаграмма
Обратите внимание, что для этой диаграммы имя серии правильно задано с диапазоном до нужной ячейки. Это означает, что обновления будут распространяться на Chart
.
Размещение диаграмм в сетке
Обычная работа с графиками в Excel — это стандартизация размера и компоновки нескольких диаграмм на одном листе. Если сделать это вручную, вы можете удерживать ALT при изменении размера или перемещении диаграммы, чтобы «придерживаться» границ ячеек. Это работает для пары диаграмм, но подход VBA намного проще.
Код для создания сетки
Этот код создаст сетку диаграмм, начинающихся с заданной (верхней, левой) позиции, с определенным количеством столбцов и определенным общим размером диаграммы. Графики будут размещены в том порядке, в котором они были созданы, и обернут вокруг края, чтобы сформировать новую строку.
Sub CreateGridOfCharts()
Dim int_cols As Integer
int_cols = 3
Dim cht_width As Double
cht_width = 250
Dim cht_height As Double
cht_height = 200
Dim offset_vertical As Double
offset_vertical = 195
Dim offset_horz As Double
offset_horz = 40
Dim sht As Worksheet
Set sht = ActiveSheet
Dim count As Integer
count = 0
'iterate through ChartObjects on current sheet
Dim cht_obj As ChartObject
For Each cht_obj In sht.ChartObjects
'use integer division and Mod to get position in grid
cht_obj.Top = (count int_cols) * cht_height + offset_vertical
cht_obj.Left = (count Mod int_cols) * cht_width + offset_horz
cht_obj.Width = cht_width
cht_obj.Height = cht_height
count = count + 1
Next cht_obj
End Sub
Результат с несколькими графиками
Эти снимки показывают исходную случайную компоновку диаграмм и результирующую сетку от запуска кода выше.
До
После
Диаграмма по выделенной ячейке
Предположим, что нам с вами требуется визуализировать данные из вот такой таблицы со значениями продаж автомобилей по разным странам в 2021 году (реальные данные, взятые отсюда, кстати):
Поскольку количество рядов данных (стран) велико, то попытка запихнуть их все сразу в один график приведёт либо к ужасной «спагетти-диаграмме», либо к построению отдельных диаграмм на каждый ряд, что весьма громоздко.
Изящным решением этой проблемы может стать построение диаграммы только по данным из текущей строки, т. е. строки, где стоит активная ячейка:
Реализовать такое очень легко — потребуется лишь две формулы и один крохотный макрос в 3 строки.
Шаг 1. Номер текущей строки
Первое, что нам потребуется — это именованный диапазон, вычисляющий номер строки на листе, где сейчас стоит наша активная ячейка. Открываем на вкладке Формулы — Диспетчер имен (Formulas — Name manager), жмём на кнопку Создать (Create) и вводим туда следующую конструкцию:
Здесь:
- Имя — любое подходящее имя для нашей переменной (в нашем случае это ТекСтрока)
- Область — здесь и далее нужно выбрать текущий лист, чтобы создаваемые имена были локальными
- Диапазон — тут используем функцию ЯЧЕЙКА (CELL), которая умеет выдавать кучу разных параметров для заданной ячейки, в том числе и нужный нам номер строки — за это отвечает аргумент «строка».
Шаг 2. Ссылка на заголовок
Для отображения выбранной страны в заголовке и легенде диаграммы, нам нужно получить ссылку на ячейку с её (страны) названием из первого столбца. Для этого создаём еще один локальный (т.е. Область = текущий лист, а не Книга!) именованный диапазон со следующей формулой:
Здесь функция ИНДЕКС выбирает из заданного диапазона (столбца А, где лежат наши страны-подписи) ячейку с номером строки, который мы до этого определили.
Шаг 3. Ссылка на данные
Теперь аналогичным образом давайте получим ссылку на диапазон со всеми данными по продажам из текущей строки, где стоит сейчас активная ячейка. Создаём ещё один именованный диапазон со следующей формулой:
Здесь третий аргумент равный нулю заставляет ИНДЕКС вернуть в качестве результата не отдельное значение, а всю строку.
Шаг 4. Подставляем ссылки в диаграмму
Теперь выделим шапку таблицы и первую строку с данными (диапазон ) и построим по ним диаграмму через Вставка — Диаграммы (Insert — Charts). Если выделить на диаграмме ряд с данными, то в строке формул отобразится функция РЯД (SERIES) — специальная функция, которую Excel автоматически использует при создании любой диаграммы, чтобы сослаться на исходные данные и подписи:
Аккуратно подменим в этой функции первый (подпись) и третий (данные) аргументы названиями наших диапазонов с шагов 2 и 3:
Диаграмма начнет отображать данные по продажам из текущей строки.
Шаг 5. Макрос пересчета
Остался последний штрих. Microsoft Excel пересчитывает формулы только при изменении данных на листе или при нажатии на клавишу F9, а мы хотим, чтобы пересчёт происходил при изменении выделения, т. е. при любом перемещении активной ячейки по листу. Для этого потребуется добавить в нашу книгу простой макрос.
Щёлкните правой кнопкой мыши по ярлычку листа с данными и выберите команду Исходный код (Source code). В открывшееся окно введём код макроса-обработчика события изменения выделения:
Как легко сообразить, всё, что он делает — это запускает пересчет листа при любом изменении положения активной ячейки.
Шаг 6. Подсветка текущей строки
Для наглядности, можно добавить ещё и правило условного форматирования для выделения цветом страны, которая сейчас отображается на графике. Для этого выделим таблицу и выберем Главная — Условное форматирование — Создать правило — Использовать формулу для определения форматируемых ячеек (Home — Conditional formatting — New rule — Use a formula to determine which cells to format):
Здесь формула проверяет для каждой ячейки в таблице совпадение её номера строки с тем номером, что хранится в переменной ТекСтрока, и если совпадение имеет место, то срабатывает заливка выбранным цветом.
Вот и всё — просто и красиво, правда?
Примечания
- На больших таблицах вся эта красота может тормозить — условное форматирование штука ресурсоёмкая, да и пересчёт на каждое выделение тоже может быть тяжеловат.
- Чтобы на графике не пропадали данные при случайном выделении ячейки выше или ниже таблицы, можно добавить в имя ТекСтрока дополнительную проверку вложенными функциями ЕСЛИ вида:
=ЕСЛИ(ЯЧЕЙКА(«строка»)<4;4;ЕСЛИ(ЯЧЕЙКА(«строка»)>20;20;ЯЧЕЙКА(«строка»)))
Ссылки по теме
- Подсветка заданных столбцов на диаграмме
- Как создать в Excel интерактивную диаграмму
- Координатное выделение
Время на прочтение
7 мин
Количество просмотров 8.9K
Доброго времени суток, уважаемые Хабровчане.
Я — начинающий разработчик SharePoint, и сегодня хотелось бы рассказать о решении одной интересной задачи. Нужно было сделать веб-часть для SharePoint, которая должна обрабатывать таблицу с данными, выводить график на экран, а так же уметь экспортировать таблицу и график в Microsoft Excel. Под катом мой способ решения, отрывки кода веб-части и ссылка на проект.
Теоретическое решение
1. Построение графика на странице.
Удобнее всего это делать с помощью MS Chart Control. Сам процесс достаточно прост (хотя, конечно, зависит от необходимого Вам вида графика). Единственное требование: первый столбец в таблице — это значения для оси X на графике.
2. Экспорт таблицы в Excel
Существует достаточно много способов экспорта, но я решил воспользоваться самым простым (на мой взгляд). Как известно, в Office 2007/2010 для файлов .docx, .xlsx, pptx используется формат Open XML, то есть эти файлы представляют из себя простой ZIP-архив с XML-файлами. Работать с ZIP-архивами просто, и таким образом можно записать данные напрямую в XML-файлы.
Однако оказалось, что записать табличку с названиями столбцов не так то просто. Excel записывает цифровые поля напрямую в sheet1.xml, а текстовые поля записывает в sharedStrings.xml, и в sheet1.xml идут только ссылки на них. К счастью, я нашел замечательный проект, который и реализует запись текстовых значений в xlsx-файл.
3. Экспорт графика в Excel
Естественно, простая вставка картинки с графиком не подойдет, это должен быть настоящий Excel’евский график. Гугл не подсказал мне ничего стоящего — либо не подходит, либо слишком сложно. Немного поработав c графиками в Excel’е, я заметил интересную особенность — если задать области данных, из которых график будет брать значения, а потом менять значение ячейки — график меняется автоматически. Идея пришла сама собой — создать пустой файл с графиком, затем записывать в него таблицу, потом так же с помощью редактирования XML указывать области данных для графика (в настоящем проекте у меня была динамическая таблица, поэтому размеры вычислялись в коде).
Практическое решение
Для примера я решил создать простую веб-часть с табличкой, двумя кнопками и графиком (изначально он скрыт). Так как нам нужно будет экспортировать названия столбцов таблицы, то необходимо вписать их первой строкой, и отключить ShowHeader.
DataTable dt = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 4;i++ )
dt.Columns.Add();
dt.Rows.Add(new string[] { "Номер недели", "Печеньки", "Чай", "Сахар" });
dt.Rows.Add(new string[] { "1", "17", "5", "8" });
dt.Rows.Add(new string[] { "2", "18", "4", "10" });
dt.Rows.Add(new string[] { "3", "15", "6", "9" });
dt.Rows.Add(new string[] { "4", "19", "7", "10" });
dt.Rows.Add(new string[] { "5", "13", "4", "7" });
GridView1.DataSource = dt;
GridView1.DataBind();
GridView1.Width = 300;
GridView1.ShowHeader = false;
}
Теперь делаем вывод графика. Учитываем, что данные начинаются со 2-й строки, а 1-й столбец — это значения для оси X.
Series[] series = new Series[dt.Columns.Count - 1];
for (int i = 0; i < series.Length; i++)
{
series[i] = new Series(dt.Columns[i + 1].ColumnName);
series[i].ChartType = SeriesChartType.Column;
for (int k = 1; k < dt.Rows.Count; k++)
series[i].Points.AddXY((double.Parse((string)dt.Rows[k][0])), double.Parse((string)dt.Rows[k][i + 1]));
Chart1.Series.Add(series[i]);
}
ChartArea chartArea = new ChartArea();
chartArea.AxisX.Minimum = double.Parse(dt.Rows[1][0].ToString());
chartArea.AxisX.Maximum = double.Parse(dt.Rows[dt.Rows.Count - 1][0].ToString());
Chart1.ChartAreas.Add(chartArea);
Chart1.Width = 300;
Chart1.Visible = true;
Приступим к экспорту. Первое что понадобится — шаблон (пустой файл с графиком). Создаем в Excel новую книгу, вставляем график, указываем области данных. Сохраняем, закрываем. Можно немного отредактировать напрямую через xml — удалить кэшированные значения, если таблица не фиксированного размера — то в качестве областей данных можно подставить свои числа (я так схитрил — потом просто через Replace заменяю эти числа на необходимые). Шаблон надо закинуть в корневую папку узла Sharepoint.
Используя вышеупомянутый проект, дорабатываем его под наши цели — сначала создадим копию 1й строки таблицы (названия столбцов). С помощью HashTable создадим ссылки на эти текстовые данные, а сами данные запишем в sharedStrings.xml.
public static ArrayList CreateStringTables(DataTable data, out Hashtable lookupTable)
{
ArrayList stringTable = new ArrayList();
lookupTable = new Hashtable();
foreach (DataRow row in data.Rows)
foreach (DataColumn column in data.Columns)
if (column.DataType == typeof(string))
{
string val = (string)row[column];
if (!lookupTable.Contains(val))
{
lookupTable.Add(val, stringTable.Count);
stringTable.Add(val);
}
}
return stringTable;
}
public static void WriteStringTable(Stream output, ArrayList stringTable)
{
using (XmlTextWriter writer = new XmlTextWriter(output, Encoding.UTF8))
{
writer.WriteStartDocument(true);
writer.WriteRaw("<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="" + stringTable.Count.ToString() + "" uniqueCount="" + stringTable.Count.ToString() + "">");
foreach (string str in stringTable)
{
writer.WriteRaw("<si><t>" + str + "</t></si>");
}
writer.WriteRaw("</sst>");
}
}
Теперь преобразуем таблицу данных в xml-код, попутно вставляя ссылки на текстовые данные. Записываем все в файл sheet1.xml.
public static void WriteWorksheetData(XmlTextWriter writer, DataTable dt, Hashtable lookupTable)
{
int rowsCount = dt.Rows.Count;
int columnsCount = dt.Columns.Count;
string relPos = RowIndexToName(0);
writer.WriteRaw("<row r="" + relPos + "" spans="1:" + columnsCount.ToString()+"">");
for (int column = 0; column < columnsCount; column++)
{
relPos = RowColumnToPosition(0, column);
writer.WriteRaw("<c r="" + relPos + "" t="s">");
string val = lookupTable[dt.Rows[0][column]].ToString();
writer.WriteRaw("<v>" + val + "</v>");
writer.WriteRaw("</c>");
}
writer.WriteRaw("</row>");
for (int row = 1; row < rowsCount; row++)
{
relPos = RowIndexToName(row);
writer.WriteRaw("<row r="" + relPos + "" spans="1:" + columnsCount.ToString() + "">");
for (int column = 0; column < columnsCount; column++)
{
relPos = RowColumnToPosition(row, column);
writer.WriteRaw("<c r="" + relPos + "">");
string val = dt.Rows[row][column].ToString();
writer.WriteRaw("<v>" + val + "</v>");
writer.WriteRaw("</c>");
}
writer.WriteRaw("</row>");
}
}
public static void WriteWorksheet(Stream output, DataTable dt, Hashtable lookupTable)
{
using (XmlTextWriter writer = new XmlTextWriter(output, Encoding.UTF8))
{
writer.WriteStartDocument(true);
writer.WriteRaw("<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">");
string lastCell = RowColumnToPosition(dt.Rows.Count - 1, dt.Columns.Count - 1);
writer.WriteRaw("<dimension ref="A1:" + lastCell + "" />");
writer.WriteRaw("<sheetViews>");
writer.WriteRaw("<sheetView tabSelected="1" workbookViewId="0" />");
writer.WriteRaw("</sheetViews>");
writer.WriteRaw("<sheetFormatPr defaultRowHeight="15" />");
writer.WriteRaw("<sheetData>");
WriteWorksheetData(writer, dt, lookupTable);
writer.WriteRaw("</sheetData>");
writer.WriteRaw("<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3" />");
writer.WriteRaw("<drawing r:id="rId1" />");
writer.WriteRaw("</worksheet>");
}
}
Осталось не забыть изменить области данных. Так как в начально проекте у меня могло быть не более пяти столбцов — я создал шаблон с пятью столбцами, а потом удалял лишние. Кол-во строк рассчитывается просто из таблицы.
public static void FieldEdit(Stream xmlFile, int rowsCount, int columnsCount)
{
XmlDocument document = new XmlDocument();
document.Load(xmlFile);
XmlNodeList xmlColumns = document.GetElementsByTagName("c:ser");
XmlNode xmlChart = xmlColumns[0].ParentNode;
for (int i = xmlColumns.Count - 1; i > columnsCount - 2; i--)
xmlColumns[i].ParentNode.RemoveChild(xmlColumns[i]);
XmlNodeList xmlRows = document.GetElementsByTagName("c:f");
for (int i = 0; i < xmlRows.Count; i++)
xmlRows[i].InnerText = xmlRows[i].InnerText.Replace("15", rowsCount.ToString());
MemoryStream ms = new MemoryStream();
document.Save(ms);
xmlFile.SetLength(ms.Length);
xmlFile.Position = 0;
document.Save(xmlFile);
ms.Close();
}
И вот наш файл готов! Выдаем его пользователю для сохранения/загрузки с помощью HttpContext.Current.Response.
public static void SendContent(byte[] fileContent, string outFileName)
{
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AddHeader("Content-Type", "application/force-download");
HttpContext.Current.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}", outFileName.Replace(" ", "_")));
HttpContext.Current.Response.AddHeader("Content-Length", fileContent.Length.ToString());
HttpContext.Current.Response.OutputStream.Write(fileContent, 0, fileContent.Length);
HttpContext.Current.Response.OutputStream.Flush();
HttpContext.Current.Response.OutputStream.Close();
HttpContext.Current.Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
Я не стал публиковать здесь весь код — он слишком большой, и весь проект (с подробными комментариями и пустым шаблоном) можно скачать здесь.
Примечания:
- Изначально проект делался для SP2007 в VS2008, и портировался на 2010 специально для Хабра. Поэтому использованы не все возможности 2010 версии.
- Код написан как пример, максимально просто, без ненужных (в данном случае) try-catch и т.д. Главное было показать идею, а не реализацию.
- Этот способ можно использовать не только с SharePoint, но это наиболее часто встречающаяся связка с Excel.
- Я только учусь, так что с удовольствием выслушаю Ваши советы и указания на мои ошибки.
Спасибо за внимание.