Автор:
Anatoly Lubarsky (
anatolylubarsky@hotmail.com)
Источник:
www.aspnetmania.com
Решив написать статью про построение графиков в ASP.NET, я понял, что одной статьи не хватит и запланировал серию из трёх статей. Данная статья, первая из серии, предлагает решение через OWC и генератор клиентского скрипта, так сказать введение в OWC. Здесь мы рассмотрим 2 типа графиков (круговая диаграмма и столбчато-линейная :)).
В запланированной второй статье будет показан пример более сложных диаграмм и будет добавлен слой бизнес-логики и class-wrapper для графиков. В третьей статье будет предложено решение построения сложной диаграммы с помощью System.Drawing и отображения с помощью HttpHandler - средствами чистого .NET.
OWC - Office Web Component это компонент windows для построения диаграмм в web. Даже во времена ASP он находился в тени, а свет в основном падал на компоненты сторонних разработчиков, которые в большинстве случаев не превосходили OWC по возможностям. Главное его достоинство в том что он позволяет очень быстро и сравнительно легко построить диаграммы для всего приложения. Бесплатен. Узнаваем юзерами, пользующимися MS-Office.
Недостатки - такие же как у любого клиентского компонента. Ограничен. Нужно, чтобы присутствовал на машине. Хотя наличие офиса у юзера необязательно - достаточно чтобы был windows. Поэтому лично я предпочитаю построение диаграмм средствами чистого .NET, об этом мы поговорим в другой статье.
Итак, допустим, есть фирма, в фирме агенты, которые заключают контракты на определённые суммы, требуется: нарисовать диаграмму, которая показывает, кто сколько контрактов заключил:
Процедура, которая принесёт нужные данные:
CREATE procedure P_GET_ALL_PERCENTS
as
select
A.AGENT_NAME,
count(C.CONTRACT_ID)
from
T_AGENT A join T_CONTRACT C on
A.AGENT_ID = C.AGENT_ID
group by
A.AGENT_NAME
GO
Рассмотрим, как выглядит страница:
///
/// PieChart
///
public class PieChart : Page
{
private void Page_Load(object sender, System.EventArgs e)
{
// ##################################################
// получим данные по-умолчанию из web.config
#region GetFromConfig
string sDefaultFont = ConfigurationSettings.AppSettings["DefaultFont"].ToString();
string sDefaultFontSize = ConfigurationSettings.AppSettings["DefaultFontSize"].ToString();
string sDefaultLegendFontSize = ConfigurationSettings.AppSettings["DefaultLegendFontSize"].ToString();
string sDefaultAxisFontSize = ConfigurationSettings.AppSettings["DefaultAxisFontSize"].ToString();
string sInnerFontSize = ConfigurationSettings.AppSettings["InnerFontSize"].ToString();
#endregion
// ##################################################
// получим данные из базы для PIE CHART
// используем для этого класс DataLayer,
// который осуществляет работу с базой
SqlDataReader reader = null;
DataLayer data = new DataLayer();
data.RunProc("P_GET_ALL_PERCENTS", out reader);
// кладём DataReader в ArrayList
#region PutReaderInArrayList
ArrayList rowList = new ArrayList();
while (reader.Read()) {
object[] values = new object[ reader.FieldCount];
reader.GetValues( values);
rowList.Add(values);
}
reader.Close();
#endregion
// здесь преобразуем ArrayList в delimited strings
// которые несут в себе данные
string strNames = "";
string strValues = "";
foreach (object[] row in rowList)
{
strNames += String.Format("\"{0}\",", row[0].ToString());
strValues += String.Format("{0},", row[1].ToString());
}
// legend and data
strNames = strNames.Remove(strNames.Length - 1, 1);
strValues = strValues.Remove(strValues.Length - 1, 1);
// генерация клиентского скрипта
// ##################################################
// ##################################################
// StringBuilder
StringBuilder sb = new StringBuilder("");
sb.Append("<script language=\"VBScript\">\n");
sb.Append("Sub Window_OnLoad()\n");
sb.Append("call Show_ChartSpacePie\n");
sb.Append("End Sub\n");
// генерируем код клиентской функции, которая
// работает с графиком
// ##################################################
#region GeneratePieChart
sb.Append("Sub Show_ChartSpacePie()\n");
sb.Append("dim oChart\n");
sb.Append("dim oSeries1, oSeries2, oSeries3\n");
sb.Append("dim oAxis1, oAxis2\n");
sb.Append("dim oConst\n");
sb.Append("ChartSpacePie.Clear\n");
sb.Append("Set oConst = ChartSpacePie.Constants\n");
sb.Append("' Create a new chart in the ChartSpace.\n");
sb.Append("Set oChart = ChartSpacePie.Charts.Add\n");
sb.Append("oChart.Type = oConst.chChartTypePie\n");
sb.Append("' Add a series of type Column.\n");
sb.Append("Set oSeries1 = oChart.SeriesCollection.Add\n");
sb.Append("oSeries1.Caption = \"C\"\n");
// наши данные
sb.Append("oSeries1.SetData oConst.chDimCategories, oConst.chDataLiteral, Array(" + strNames + ")\n");
sb.Append("oSeries1.SetData oConst.chDimValues, oConst.chDataLiteral, Array(" + strValues + ")\n");
sb.Append("With oSeries1.DataLabelsCollection.Add\n");
sb.Append(".Position = 1\n");
sb.Append(".Font.Name = \"" + sDefaultFont + "\"\n");
sb.Append(".Font.Size = " + sInnerFontSize + "\n");
sb.Append(".HasValue = False\n");
sb.Append(".HasPercentage = True\n");
sb.Append("End With\n");
sb.Append("' Display the legend.\n");
sb.Append("oChart.HasLegend = True\n");
sb.Append("oChart.Legend.Font.Size = " + sDefaultLegendFontSize + "\n");
sb.Append("oChart.Legend.Position = oConst.chLegendPositionRight\n");
sb.Append("' Display the title for the chart.\n");
sb.Append("oChart.HasTitle = True\n");
sb.Append("oChart.Title.Font.Name = \"" + sDefaultFont + "\"\n");
sb.Append("oChart.Title.Font.Size = " + sDefaultFontSize + "\n");
sb.Append("oChart.Title.Caption = \"Agents Contracts\"\n");
sb.Append("End Sub\n");
#endregion
// регистрируем скрипт
sb.Append("</script>\n");
if (!Page.IsClientScriptBlockRegistered("PieChart"))
Page.RegisterClientScriptBlock("PieChart", sb.ToString());
}
}
Вот и вся диаграмма.
Более сложный пример. Надо показать, на какую сумму определённый агент заключал контрактов каждый месяц и средняя сумма одного контракта в месяц.
Процедура, которая принесёт нужные данные:
CREATE procedure P_GET_AVG_BY_ID
(
@p_agent_id int
)
as
select
A.AGENT_ID,
A.AGENT_NAME,
C.MONTH_ID,
sum(C.CONTRACT_VALUE),
avg(C.CONTRACT_VALUE)
from
T_AGENT A join T_CONTRACT C on
A.AGENT_ID = C.AGENT_ID
where
A.AGENT_ID = @p_agent_id
group by
A.AGENT_ID,
A.AGENT_NAME,
C.MONTH_ID
order by
A.AGENT_ID,
C.MONTH_ID
Рассмотрим, как выглядит страница:
///
/// BarAvgChart
///
public class BarAvgChart : Page
{
private void Page_Load(object sender, System.EventArgs e)
{
// ##################################################
// получим данные по-умолчанию из web.config
#region GetFromConfig
string sDefaultFont = ConfigurationSettings.AppSettings["DefaultFont"].ToString();
string sDefaultFontSize = ConfigurationSettings.AppSettings["DefaultFontSize"].ToString();
string sDefaultLegendFontSize = ConfigurationSettings.AppSettings["DefaultLegendFontSize"].ToString();
string sDefaultAxisFontSize = ConfigurationSettings.AppSettings["DefaultAxisFontSize"].ToString();
string sInnerFontSize = ConfigurationSettings.AppSettings["InnerFontSize"].ToString();
#endregion
// ##################################################
// получим данные из базы для BAR AVG CHART
SqlDataReader reader = null;
DataLayer data = new DataLayer();
SqlParameter[] prams = { data.MakeInParam("@p_agent_id", SqlDbType.Int, 4, 1) };
data.RunProc("P_GET_AVG_BY_ID", prams, out reader);
// кладём DataReader в ArrayList
ArrayList rowList = new ArrayList();
while (reader.Read())
{
object[] values = new object[ reader.FieldCount];
reader.GetValues( values);
rowList.Add(values);
}
reader.Close();
#endregion
// здесь преобразуем ArrayList в delimited strings
// которые несут в себе данные
string strMonths = "";
string strValuesTotal = "";
string strValuesAvg = "";
string strNameAgent = "";
foreach (object[] row in rowList)
{
strMonths += String.Format("\"{0}\",", row[2].ToString());
strValuesTotal += String.Format("{0},", row[3].ToString());
strValuesAvg += String.Format("{0},", row[4].ToString());
strNameAgent = row[1].ToString();
}
// legend and data
strMonths = strMonths.Remove(strMonths.Length - 1, 1);
strValuesTotal = strValuesTotal.Remove(strValuesTotal.Length - 1, 1);
strValuesAvg = strValuesAvg.Remove(strValuesAvg.Length - 1, 1);
// ##################################################
// ##################################################
// StringBuilder
StringBuilder sb = new StringBuilder("");
sb.Append("<script language=\"VBScript\">\n");
sb.Append("Sub Window_OnLoad()\n");
sb.Append("call Show_ChartSpaceBarAvg\n");
sb.Append("End Sub\n");
// ##################################################
// генерируем код клиентской функции, которая
// работает с графиком
#region GenerateBarAvgChart
sb.Append("Sub Show_ChartSpaceBarAvg()\n");
sb.Append("dim oChart\n");
sb.Append("dim oSeries1, oSeries2, oSeries3\n");
sb.Append("dim oAxis1, oAxis2\n");
sb.Append("dim oConst\n");
sb.Append("ChartSpaceBarAvg.Clear\n");
sb.Append("Set oConst = ChartSpaceBarAvg.Constants\n");
sb.Append("'Create a new chart in the ChartSpace.\n");
sb.Append("Set oChart = ChartSpaceBarAvg.Charts.Add\n");
sb.Append("'Add a series of type Column.\n");
sb.Append("Set oSeries1 = oChart.SeriesCollection.Add\n");
sb.Append("oSeries1.Caption = \"Total\"\n");
// наши данные для суммы
sb.Append("oSeries1.SetData oConst.chDimCategories, oConst.chDataLiteral, Array(" + strMonths + ")\n");
sb.Append("oSeries1.SetData oConst.chDimValues, oConst.chDataLiteral, Array(" + strValuesTotal + ")\n");
sb.Append("oSeries1.Type = oConst.chChartTypeColumnStacked\n");
sb.Append("oSeries1.Interior.Color = RGB(176,196,222)\n");
sb.Append("With oSeries1.DataLabelsCollection.Add\n");
sb.Append(".Position = 1\n");
sb.Append(".HasValue = True\n");
sb.Append(".Font.Size = " + sDefaultAxisFontSize + "\n");
sb.Append("End With\n");
sb.Append("' Add a second series of type Line.\n");
sb.Append("Set oSeries2 = oChart.SeriesCollection.Add\n");
sb.Append("oSeries2.Caption = \"Average\"\n");
// наши данные для среднего арифметического
sb.Append("oSeries2.SetData oConst.chDimCategories, oConst.chDataLiteral, Array(" + strMonths + ")\n");
sb.Append("oSeries2.SetData oConst.chDimValues, oConst.chDataLiteral, Array(" + strValuesAvg + ")\n");
sb.Append("oSeries2.Type = oConst.chChartTypeLine\n");
sb.Append("oSeries2.Line.Color = RGB(178,38,38)\n");
sb.Append("With oSeries2.DataLabelsCollection.Add\n");
sb.Append(".Position = 1\n");
sb.Append(".HasValue = True\n");
sb.Append(".Font.Size = " + sDefaultAxisFontSize + "\n");
sb.Append("End With\n");
sb.Append("' Change the Min/Max, Numberformat and Gridlines for the value-axis of the first series.\n");
sb.Append("Set oAxis1 = oChart.Axes(oConst.chAxisPositionLeft)\n");
sb.Append("oAxis1.Scaling.Maximum = 50000\n");
sb.Append("oAxis1.Scaling.Minimum = 0\n");
sb.Append("oAxis1.NumberFormat = \"0\"\n");
sb.Append("oAxis1.Font.Size = " + sDefaultAxisFontSize + "\n");
sb.Append("Set oAxis2 = oChart.Axes(oConst.chAxisPositionBottom)\n");
sb.Append("oAxis2.Font.Size = " + sDefaultAxisFontSize + "\n");
sb.Append("oAxis1.HasMajorGridlines = False\n");
sb.Append("' Ungroup the series so that they can have separate value-axis scaling.\n");
sb.Append("' Display the legend.\n");
sb.Append("oChart.HasLegend = True\n");
sb.Append("oChart.Legend.Font.Size = " + sDefaultLegendFontSize + "\n");
sb.Append("oChart.Legend.Position = oConst.chLegendPositionRight\n");
sb.Append("' Display the title for the chart.\n");
sb.Append("oChart.HasTitle = True\n");
sb.Append("oChart.Title.Font.Size = " + sDefaultFontSize + "\n");
sb.Append("oChart.Title.Caption = \"" + strNameAgent + "\"\n");
sb.Append("End Sub\n");
#endregion
// регистрируем скрипт
sb.Append("</script>\n");
if (!Page.IsClientScriptBlockRegistered("BarAvgChart"))
Page.RegisterClientScriptBlock("BarAvgChart", sb.ToString());
}
}
Вот и всё. Легко и очень быстро.
Добавим в web.config:
<appsettings>
<add key="ConnectionString" value="server=localhost;database=DB_OWC;uid=sa;pwd=" />
<add key="DefaultFont" value="Verdana" />
<add key="DefaultFontSize" value="10" />
<add key="InnerFontSize" value="8" />
<add key="DefaultLegendFontSize" value="7" />
<add key="DefaultAxisFontSize" value="6" />
</appsettings>
В следующей статье я повышу уровень сложности для OWC. Ну а потом рассмотрим построение диаграмм средствами чистого .NET.