Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - Все статьи »

Обсудить на форуме Обсудить на форуме

Сложные графики и диаграммы в ASP.NET. Часть вторая - OWC через class-wrapper.

В предыдущей статье - введении в OWC - я показал на примерах принцип построения диаграмм при помощи клиентского owc. Работа велась против базы данных. В codebehind страницы лежала вся логика и в зависимости от параметров генерировался клиентский скрипт на VBScript который работал с owc.

Таким образом можно быстро строить диаграммы, достаточно приблизительно знать из каких объектов состоит диаграмма owc, и пару раз для тренировки написать обычный клиентский скрипт. Диаграмма OWC, состоит из нескольких объектов, поэтому коды столбчатой или линейной или круговой диаграммы похожи друг на друга. А вообще-то все заслуживающие внимания компоненты диаграмм в мире состоят из тех же объектов. Конечно везде есть отличия и свои навороты, но каждая диаграмма имеет сам график, легенду, оси координат, и порции данных.

Поэтому owc имеет Chart, Series, Axis, Legend.

Способ, описанный в первой статье не хорош, если нам надо производить диаграммы в промышленных масштабах, ну скажем 20 на одной странице, и скажем 50 таких страниц в приложении. Так же разработчик должен смотреть в будущее и как говорится держать палец на пульсе. На мой взгляд приложение должно быть future-friendly, к примеру если завтра придётся прикрутить приложение к альтернативному компоненту диаграмм, а то и рисовать все диаграммы самим (пример в следующей статье), то это должно пройти безболезненно.

Именно для этих целей я введу своего рода класс обёртку для owc. Поддержка альтернативных компонентов и собственных классов будет достигаться перегрузкой функций wrapper-a.

Что касается работы с данными, то примеры первой статьи подходят под определение 2-tier, ASP-NET --> DataLayer, то есть вся логика лежит в code-behind страницы. Устраним этот недостаток тоже. Введём дополнительный слой бизнес-логики.

Рассмотрим на примере:

Вы помните, у нас есть агенты, которые заключают контракты. Усложним задачу: каждая сделка залючается либо в европе, либо в африке, либо в америке, либо ещё где-то. Надо показать, на какую сумму определённый агент заключал контрактов каждый месяц, по каждому типу ну и общая сумма всех типов контрактов в месяц. (База данных была изменена)

В принципе, получить данные можно и одной процедурой, но я сделаю две, потому что так легче генерить диаграмму.

--------------------------
-- получаем типы
--------------------------
CREATE procedure P_GET_ALL_TYPES  

as

select 
   CONTRACT_TYPE_ID, 
   CONTRACT_TYPE_NAME 
from  
   T_CONTRACT_TYPE
GO


--------------------------
-- имея агента и тип 
-- контракта получаем
-- суммы контрактов
--------------------------

CREATE    procedure P_GET_PARTS_BY_ID 
(
   @p_agent_id int,
   @p_contract_type_id int
)
as

select
   A.AGENT_NAME, 
   C.MONTH_ID,
   CT.CONTRACT_TYPE_NAME,
   sum(coalesce(C.CONTRACT_VALUE,0)) as C_SUM
from  
   T_CONTRACT C join T_AGENT A on 
   C.AGENT_ID = A.AGENT_ID left join T_CONTRACT_TYPE CT on
   C.CONTRACT_TYPE_ID = CT.CONTRACT_TYPE_ID 
where 
   C.AGENT_ID = @p_agent_id and
   C.CONTRACT_TYPE_ID = @p_contract_type_id
group by
   A.AGENT_NAME,
   C.MONTH_ID,
   CT.CONTRACT_TYPE_NAME
order by 2

Теперь, следуя заранее разработанному плану, создадим класс Contract, вся логика будет лежать в этом классе, а страница будет абстрактно работать с объектами данного класса.

У класса 5 полей и по одной функции на каждую процедуру.

   public class Contract
   {
      private int _Agent_Id;
      private int _Contract_Type_Id;
      private string _Name;
      private string _Categories;
      private string _Values;

      #region PropertiesGetSet
      public int agentId 
      {
         get { return _Agent_Id; }
         set { _Agent_Id = value; }
      }
    
      public int contractTypeId 
      {
         get { return _Contract_Type_Id; }
         set { _Contract_Type_Id = value; }
      }
      
      public string Name 
      {
         get { return _Name; }
         set { _Name = value; }
      }

      public string Categories 
      {
         get { return _Categories; }
         set { _Categories = value; }
      }
      
      public string Values 
      {
         get { return _Values; }
         set { _Values = value; }
      }
      #endregion


      public Contract() { }

      
      // GetContractTypes
      // функция возвращает ArrayList
      // типов контрактов
       
      public ArrayList GetContractTypes()
      {
         SqlDataReader reader = null;
         DataLayer data = new DataLayer();
         data.RunProc("P_GET_ALL_TYPES", out reader);
         #region PutReaderInArrayList
         ArrayList rowList = new ArrayList();
         while (reader.Read()) 
         {
            object[] values = new object[ reader.FieldCount];
            reader.GetValues( values);
            rowList.Add(values);
         }
         reader.Close();
         #endregion 
         return rowList;
      }

      
      
      // GetCategoriesValues
      // зная агента и тип контракта (properties)
      // функция вернёт данные в остальные поля
      // класса
      
      public void GetCategoriesValues()
      {
          SqlDataReader readerItem = null;
          DataLayer dataItem = new DataLayer();
          SqlParameter[] pramsItem = { 
                                         dataItem.MakeInParam("@p_agent_id", SqlDbType.Int, 4, this.agentId), 
                                         dataItem.MakeInParam("@p_contract_type_id", SqlDbType.Int, 4, this.contractTypeId) 
                                     };
          dataItem.RunProc("P_GET_PARTS_BY_ID", pramsItem, out readerItem);
          #region PutReaderInArrayList
          ArrayList rowListItem = new ArrayList();
          while (readerItem.Read()) 
          {
             object[] values = new object[ readerItem.FieldCount];
             readerItem.GetValues( values);
             rowListItem.Add(values);
          }
          readerItem.Close();
          #endregion

          string strMonths = "";
          string strValuesTotal = "";
          string strName = "";
          foreach (object[] rowItem in rowListItem) 
          {
              strMonths += String.Format("\"{0}\",", rowItem[1].ToString());
              strValuesTotal += String.Format("{0},", rowItem[3].ToString());
              strName = rowItem[0].ToString();
          }
          if (strMonths.Length > 1)
          {
              strMonths = strMonths.Remove(strMonths.Length - 1, 1);
              strValuesTotal = strValuesTotal.Remove(strValuesTotal.Length - 1, 1);
          }
          this.Name = strName;
          this.Categories = strMonths;
          this.Values = strValuesTotal;
      }
   }

Добавив business-layer мы уже усилили приложение с объектной точки зрения и разгрузили presentation-layer.

Теперь напишем class-wrapper. Наша задача сделать страницу полностью абстрактной. Она не должна зависеть ни от обработки данных, ни от способа, которым мы создаём диаграмму, пусть это будет owc или какой другой способ.

После всех преобразований страница будет выглядеть примерно так:

      // Page_Load
      private void Page_Load(object sender, System.EventArgs e)
      {
         .....................................................
         .....................................................      
         // ##################################################
         // с помощью wrapper создаём и строим
         // диаграмму - BarParts, 
         // оси - Axis
         BarParts bc = new BarParts("ChartSpaceBarParts", "1", "True", "chLegendPositionRight", "True", sDefaultFontSize, "");
         StringBuilder sb = new StringBuilder("", 10000); 
         sb = bc.BuildChart();   

         Axis aVert = new Axis("1", "1", "chAxisPositionLeft", "20000", "0", "\"0\"", sDefaultAxisFontSize, "True");
         sb = aVert.BuildAxis(sb); 
         Axis aHorz = new Axis("1", "2", "chAxisPositionBottom", sDefaultAxisFontSize);
         sb = aHorz.BuildAxis(sb, 1); 

         
         // ##################################################
         // получаем типы контрактов с помощью объекта Contract
         ArrayList rowList = null;
         Contract cntr = new Contract();
         rowList = cntr.GetContractTypes(); 
    
         int iCounter = 1;
         string strName = "";
         foreach (object[] row in rowList) 
         {
            // получаем данные с помощью объекта Contract
            Contract cntrItem = new Contract(); 
            cntrItem.agentId = 1;
            cntrItem.contractTypeId = int.Parse(row[0].ToString());
            cntrItem.GetCategoriesValues(); 

            // создаём и строим порции данных - Series
            Series se = new Series("1", iCounter.ToString(), row[1].ToString(), "chChartTypeColumnStacked", sDefaultAxisFontSize, 
                        "True", "1", cntrItem.Categories, cntrItem.Values);
            sb = se.BuildSeries(sb);
            strName = cntrItem.Name; 
            iCounter += 1;
         }

         // достраиваем BarParts
         bc.titleCaption = strName;
         sb = bc.BuildChart(sb); 
           
         if (!Page.IsClientScriptBlockRegistered("BarParts")) 
            Page.RegisterClientScriptBlock("BarParts", sb.ToString()); 
      }  

Всё. Какая лёгкая страница получилась. Чтобы сварить кашу, нам кроме топора нужны ингридиенты. Их мы получим от class-wrapper.

Как можно заметить, на странице используются 3 класса:

BarChart - сама диаграмма
Series - порции данных
Axis - оси координат
 

В принципе у любой диаграммы если посмотреть абстрактно, присутствуют эти элементы. И всегда у каждого такого элемента определённый, один и тот же набор свойств.

Давайте рассмотрим эти классы применительно к owc:

    /*************************************************************
    ************************************************************* 
    *  CLASS BARPARTS 
    * 
    * 
    *************************************************************/
   // BarParts
   // класс описывающий саму диаграмму 
   public class BarParts 
   {
      // свойства 
      // _Space_Name - участок, в owc на одном участке 
      // может быть несколько чартов
      private string _Space_Name; 
      private string _Chart_Name;
      private string _HasLegend;
      private string _Legend_Position;
      private string _HasTitle;
      private string _Title_Font_Size;
      private string _Title_Caption;
       

      public string spaceName 
      {
         get { return _Space_Name; }
         set { _Space_Name = value; }
      }

      public string chartName 
      {
         get { return _Chart_Name; }
         set { _Chart_Name = value; }
      }

      public string hasLegend 
      {
         get { return _HasLegend; }
         set { _HasLegend = value; }
      }

      public string legendPosition 
      {
         get { return _Legend_Position; }
         set { _Legend_Position = value; }
      }

      public string hasTitle 
      {
         get { return _HasTitle; }
         set { _HasTitle = value; }
      }

      public string titleFontSize 
      {
         get { return _Title_Font_Size; }
         set { _Title_Font_Size = value; }
      }

      public string titleCaption 
      {
         get { return _Title_Caption; }
         set { _Title_Caption = value; }
      }
  

      #region Constructors
      public BarParts(string sSpaceName,
                      string sChartName,
                      string sHasLegend,
                      string sLegendPosition,
                      string sHasTitle,
                      string sTitleFontSize,
                      string sTitleCaption) 
      {
         this.spaceName = sSpaceName;
         this.chartName = sChartName;
         this.hasLegend = sHasLegend;
         this.legendPosition = sLegendPosition;
         this.hasTitle = sHasTitle;
         this.titleFontSize = sTitleFontSize;
         this.titleCaption = sTitleCaption;
      }
      #endregion

      
      // BuildChart
      // строим чарт
      // начало построения
      public StringBuilder BuildChart() 
      {
         StringBuilder sb = new StringBuilder("", 10000); 
         sb.Append("<script language=\"VBScript\">\n");            
         sb.Append("Sub Window_OnLoad()\n");
         sb.Append("call Show_" + this.spaceName + "\n");
         sb.Append("End Sub\n");
         sb.Append("Sub Show_" + this.spaceName + "()\n");
         sb.Append("Dim oConst\n");
         sb.Append(this.spaceName + ".Clear\n");
         sb.Append("Set oConst = " + this.spaceName + ".Constants\n");
         sb.Append("Set oChart" + this.chartName + " = " + this.spaceName + ".Charts.Add\n");
         return sb;
      }

      
      // BuildChart
      // строим чарт
      // достройка
      public StringBuilder BuildChart(StringBuilder sb) 
      {
         sb.Append("oChart" + this.chartName + ".HasLegend = " + this.hasLegend + "\n");
         sb.Append("oChart" + this.chartName + ".Legend.Position = oConst." + this.legendPosition + "\n");
         sb.Append("oChart" + this.chartName + ".HasTitle = " + this.hasTitle + "\n");
         sb.Append("oChart" + this.chartName + ".Title.Font.Size = " + this.titleFontSize + "\n");
         sb.Append("oChart" + this.chartName + ".Title.Caption = \"" + this.titleCaption + "\"\n");
         sb.Append("End Sub\n");
         sb.Append("\n");
         return sb;
      }
   }


   /***************************************************************
    ***************************************************************
    *  CLASS SERIES
    * 
    * 
    ***************************************************************/
   // Series
   // класс описывающий порции данных 
   public class Series 
   {
      // свойства 
      // _Chart_Name - какому чарту принадлежит порция 
      private string _Chart_Name;
      private string _Series_Name;
      private string _Caption;     
      private string _Type;        
      private string _Font_Size;   
      private string _HasValue;
      private string _Position;
      private string _Categories;
      private string _Values;
      
      public string chartName 
      {
         get { return _Chart_Name; }
         set { _Chart_Name = value; }
      } 

      public string seriesName 
      {
         get { return _Series_Name; }
         set { _Series_Name = value; }
      } 

      public string Caption 
      {
         get { return _Caption; }
         set { _Caption = value; }
      }

      public string Type 
      {
         get { return _Type; }
         set { _Type = value; }
      }

      public string fontSize 
      {
         get { return _Font_Size; }
         set { _Font_Size = value; }
      }
      
      public string hasValue 
      {
         get { return _HasValue; }
         set { _HasValue = value; }
      }
      
      public string Position 
      {
         get { return _Position; }
         set { _Position = value; }
      }

      public string Categories 
      {
         get { return _Categories; }
         set { _Categories = value; }
      }

      public string Values 
      {
         get { return _Values; }
         set { _Values = value; }
      } 
         
             
      #region Constructors
      public Series(string sChartName,
                    string sSeriesName,
                    string sCaption,
                    string sType,
                    string sFontSize,
                    string sHasValue,
                    string sPosition,
                    string sCategories,
                    string sValues) 
      {
         this.chartName = sChartName;
         this.seriesName = sSeriesName;
         this.Caption = sCaption;
         this.Type = sType;
         this.fontSize = sFontSize;
         this.hasValue = sHasValue;
         this.Position = sPosition;
         this.Categories = sCategories;
         this.Values = sValues;
      }
      #endregion


      // BuildSeries
      // строим порцию данных
      public StringBuilder BuildSeries(StringBuilder sb) 
      {
         sb.Append("Set oSeries" + this.seriesName + " = oChart" + this.chartName + ".SeriesCollection.Add\n");
         sb.Append("oSeries" + this.seriesName + ".Caption = \"" + this.Caption + "\"\n");
         
         sb.Append("oSeries" + this.seriesName + ".SetData oConst.chDimCategories, oConst.chDataLiteral, Array(" + 
                  this.Categories + ")\n");
         sb.Append("oSeries" + this.seriesName + ".SetData oConst.chDimValues, oConst.chDataLiteral, Array(" + this.Values + ")\n");
             
         sb.Append("oSeries" + this.seriesName + ".Type = oConst." + this.Type + "\n");
         sb.Append("With oSeries" + this.seriesName + ".DataLabelsCollection.Add\n");
         sb.Append(".Font.Size = " + this.fontSize + "\n");
         sb.Append(".HasValue = " + this.hasValue + "\n");
         sb.Append(".Position = " + this.Position + "\n");
         sb.Append("End With\n");
         return sb;
      }
   }  


   /***************************************************************
    ***************************************************************
    *  CLASS AXIS
    * 
    * 
    ***************************************************************/
   // Axis
   // класс описывающий оси координат 
   public class Axis 
   {
      // свойства 
      // _Chart_Name - какому чарту принадлежит ось координат 
      private string _Chart_Name;
      private string _Axis_Name;
      private string _Axis_Position;    
      private string _Scaling_Maximum;   
      private string _Scaling_Minimum;   
      private string _Number_Format;
      private string _Font_Size;
      private string _Has_Major_Gridlines;
        
            
      public string chartName 
      {
        get { return _Chart_Name; }
        set { _Chart_Name = value; }
      } 

      public string axisName 
      {
        get { return _Axis_Name; }
        set { _Axis_Name = value; }
      } 

      public string axisPosition 
      {
        get { return _Axis_Position; }
        set { _Axis_Position = value; }
      }

      public string scalingMaximum 
      {
        get { return _Scaling_Maximum; }
        set { _Scaling_Maximum = value; }
      }

      public string scalingMinimum 
      {
        get { return _Scaling_Minimum; }
        set { _Scaling_Minimum = value; }
      }
    
      public string numberFormat 
      {
        get { return _Number_Format; }
        set { _Number_Format = value; }
      }
    
      public string fontSize 
      {
        get { return _Font_Size; }
        set { _Font_Size = value; }
      }

      public string hasMajorGridlines 
      {
        get { return _Has_Major_Gridlines; }
        set { _Has_Major_Gridlines = value; }
      }
      #endregion


      #region Constructors
      public Axis(string sChartName,
                  string sAxisName,
                  string sAxisPosition,    
                  string sScalingMaximum,   
                  string sScalingMinimum,   
                  string sNumberFormat,
                  string sFontSize,
                  string sHasMajorGridlines) 
      {
         this.chartName = sChartName;
         this.axisName = sAxisName;
         this.axisPosition = sAxisPosition;
         this.scalingMaximum = sScalingMaximum;
         this.scalingMinimum = sScalingMinimum;
         this.numberFormat = sNumberFormat;
         this.fontSize = sFontSize;
         this.hasMajorGridlines = sHasMajorGridlines;
      }


      public Axis(string sChartName,
                  string sAxisName,
                  string sAxisPosition,    
                  string sFontSize) 
      {
         this.chartName = sChartName;
         this.axisName = sAxisName;
         this.axisPosition = sAxisPosition;
         this.fontSize = sFontSize;
      }
      #endregion


      // BuildAxis
      // строим ось координат (Y)
      public StringBuilder BuildAxis(StringBuilder sb) 
      {
         sb.Append("Set oAxis" + this.axisName + " = oChart" + this.chartName + ".Axes(oConst." + this.axisPosition + ")\n");
         sb.Append("oAxis" + this.axisName + ".Font.Size = " + this.fontSize + "\n");
         sb.Append("oAxis" + this.axisName + ".Scaling.Maximum = " + this.scalingMaximum + "\n");
         sb.Append("oAxis" + this.axisName + ".Scaling.Minimum = " + this.scalingMinimum + "\n");
         sb.Append("oAxis" + this.axisName + ".NumberFormat = " + this.numberFormat + "\n");
         sb.Append("oAxis" + this.axisName + ".HasMajorGridlines = " + this.hasMajorGridlines + "\n");
         return sb;
      }


      // BuildAxis
      // строим ось координат (X)
      public StringBuilder BuildAxis(StringBuilder sb, int mode) 
      {
         sb.Append("Set oAxis" + this.axisName + " = oChart" + this.chartName + ".Axes(oConst." + this.axisPosition + ")\n");
         sb.Append("oAxis" + this.axisName + ".Font.Size = " + this.fontSize + "\n");
         return sb;
      }
   }  

Теперь мы можем пользоваться этими классами, чтобы генерить диаграммы для owc, более того, слегка изменив я сгенерил подобную диаграмму под небезызвестный MSChart, и не сомневаюсь, что легко подогнать данную схему под любой сторонний компонент, работающий с диаграммами.

Кроме того, становится понятным внутреннее строение диаграмм, и если кто-то захочет сделать свои клиентские или серверные компоненты для постройки диаграмм - понятно с чего начинать.

Вот и всё.

В следующей статье рассмотрим построение диаграмм средствами чистого .NET.

Код и database скрипты прилагаются.


Текст примеров данной статьи можно выкачать здесь


Может пригодится:


Автор: Anatoly Lubarsky
Прочитано: 5723
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (0)

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру