Сообщений 8    Оценка 70 [+1/-1]         Оценить  
Система Orphus

Virtual Expert

Как создать виртуального специалиста

Автор: Сергей Беляков
Источник: RSDN Magazine #1-2010
Опубликовано: 02.03.2010
Исправлено: 10.12.2016
Версия текста: 2.1
Предисловие
Microsoft Chart Controls
Самый простой график
Улучшенный график
Microsoft OLE DB Provider for Visual FoxPro
Формируем объект DataTable на основе dbf-файла
DataTable - источник данных для Microsoft Chart Controls
DataGridView
Минимальная функциональность
Улучшаем внешний вид
Добавляем функциональности
Прямой доступ к базе данных 1С7.7
Создаем библиотеку классов
Используем Browse для просмотра документов – "Расходная накладная"
Строим график, используя данные документов – "Платежное поручение"
Сохранение данных
Частный способ сохранения данных
Универсальный способ сохранения данных
ReportViewer
Как сделать отчет для объекта DataTable
Делаем привязку к форме

Предисловие

Хочу предложить вашему вниманию статью, посвященную созданию первого специалиста – “Маркетолога”. Что он делает? Анализирует продажи и делает прогноз по каждой товарной позиции или по группам товаров. Как и обычный специалист, исходные данные он берет из бухгалтерских программ 1С7, 1С8 или из файлов популярных баз данных. Используя методы статистического анализа, “Маркетолог” строит прогноз на заданное количество интервалов времени. При этом прогнозируется количество, цена и сумма продаж по каждой товарной позиции.

Как он это делает? На основании данных за прошлый период он подбирает прогнозную функцию с минимальным среднеквадратичным отклонением. “Маркетолог” может выполнить эту работу самостоятельно и выдать готовую таблицу с прогнозными значениями, или может это сделать под контролем пользователя. Пользователь при этом видит графики прогнозных функций и исходные данные, на основании которых они построены. Конечно, всегда есть возможность изменить прогнозные значения, опираясь на свой собственный опыт продаж, или согласиться с тем, что предлагает “Маркетолог”.

Поскольку все примеры в статье приведены на C#, вам необходимо знать этот язык. Сначала мы познакомимся с компонентом Microsoft Chart Controls, потому что “Маркетолог”, которого мы создаем, должен уметь профессионально строить графики.

Microsoft Chart Controls

Требования: обязательное наличие .NET Framework 3.5 SP1.

Загрузка: для .NET Framework 3.5 доступен в виде отдельного компонента. Кроме того, имеется локализация Language Pack и интеграция с Microsoft Visual Studio 2008 Add-on for MVS 2008.

Установка: начните с Microsoft .NET Framework 3.5 Service Pack 1, если он еще не установлен. Затем проинсталлируйте Microsoft Chart Controls и в последнию очередь локализацию и интеграцию.

На сайте www.microsoft.com для Microsoft Chart Controls имеется документация и более 200 примеров с исходными кодами на C#. Но, во-первых, документация на английском, а, во-вторых, все примеры собраны в один проект и разобраться “с ходу” будет сложно. Я предлагаю начать с самой простой программы, и последовательно наращивать функциональность. Если вы работаете с Microsoft Visual Studio, выберите шаблон "Пустой проект" и добавьте в него "Файл с текстом программы". Кроме того, в свойствах проекта поменяйте тип выходных данных на "Приложение Windows".

Самый простой график


Приступим к написанию программы. В обозревателе решений (solution explorer) добавьте ссылки (references) на:

Ниже приведен листинг программы:

        using System;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;

namespace Graph
{
  class Graph : Form
  {
    public Graph()
    {
      // Главная форма - свойства
      Text = "Graph0";
      ClientSize = new Size(700, 400);

      // MicrosoftChart - свойства
      Chart myChart = new Chart();
      myChart.Parent = this;
      myChart.Left = 10;
      myChart.Top = 10;
      myChart.Width = (ClientSize.Width - 20);
      myChart.Height = (ClientSize.Height - 20);

      // Область в которой будет построен график// (Их может быть несколько)
      ChartArea myChartArea = new ChartArea();
      myChartArea.Name = "myChartArea";
      myChart.ChartAreas.Add(myChartArea);

      // График (Их может быть несколько)
      Series mySeries1 = new Series();
      mySeries1.ChartType = SeriesChartType.Spline;
      mySeries1.ChartArea = "myChartArea";
      myChart.Series.Add(mySeries1);

      // Исходные данные для графикаdouble[] yval1 = { 5, 6, 4, 6, 3 };
      string[] xval = { "Январь", "Февраль", "Март", "Апрель", "Май" };

      mySeries1.Points.DataBindXY(xval, yval1);
    }

    publicstaticvoid Main()
    {
      Graph graph0 = new Graph();
      graph0.ShowDialog();
    }
  }
}

Теперь откомпилируйте и загрузите программу. Если все в порядке, продолжим – добавим нашему графику функциональности и "красоты".

Улучшенный график


Изменим фон:

myChart.BackColor = Color.MistyRose;
myChart.BackGradientStyle = GradientStyle.DiagonalLeft;

"По-современному" оформим границы:

myChart.BorderSkin.SkinStyle = BorderSkinStyle.Sunken;
myChart.BorderSkin.PageColor = this.BackColor;

Чтобы выделить график, сделаем линии сетки бледнее:

myChartArea.AxisX.MajorGrid.LineColor = SystemColors.ControlLight;
myChartArea.AxisY.MajorGrid.LineColor = SystemColors.ControlLight;

Добавим второй график:

Series mySeries2 = new Series();
mySeries2.ChartType = SeriesChartType.Point;
mySeries2.ChartArea = "myChartArea";
myChart.Series.Add(mySeries2);

double[] yval2 = { 4, 7, 3, 5, 5 };

mySeries2.Points.DataBindXY(xval, yval2);

Ниже приведен полный листинг программы. Более ярким цветом выделены добавленные операторы:

        using System;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;

namespace Graph
{
  class Graph : Form
  {
    public Graph()
    {
      // Главная форма - свойства
      Text = "Graph1";
      ClientSize = new Size(700, 400);

      // MicrosoftChart - свойства
      Chart myChart = new Chart();
      myChart.Parent = this;
      myChart.Left = 10;
      myChart.Top = 10;
      myChart.Width = (ClientSize.Width - 20);
      myChart.Height = (ClientSize.Height - 20);

      // ФонmyChart.BackColor = Color.MistyRose;      myChart.BackGradientStyle = GradientStyle.DiagonalLeft;// Оформление границmyChart.BorderSkin.SkinStyle = BorderSkinStyle.Sunken;      myChart.BorderSkin.PageColor = this.BackColor;// Область в которой будет построен график// (Их может быть несколько)
      ChartArea myChartArea = new ChartArea();
      myChartArea.Name = "myChartArea";
      myChart.ChartAreas.Add(myChartArea);

      // Линии сеткиmyChartArea.AxisX.MajorGrid.LineColor = SystemColors.ControlLight;      myChartArea.AxisY.MajorGrid.LineColor = SystemColors.ControlLight;// Первый график
      Series mySeries1 = new Series();
      mySeries1.ChartType = SeriesChartType.Spline;
      mySeries1.ChartArea = "myChartArea";
      myChart.Series.Add(mySeries1);

      // Второй графикSeries mySeries2 = new Series();      mySeries2.ChartType = SeriesChartType.Point;      mySeries2.ChartArea = "myChartArea";      myChart.Series.Add(mySeries2);// Исходные данные для графиковdouble[] yval1 = { 5, 6, 4, 6, 3 };
      double[] yval2 = { 4, 7, 3, 5, 5 };string[] xval = { "Январь", "Февраль", "Март", "Апрель", "Май" }; 

      mySeries1.Points.DataBindXY(xval, yval1);
      mySeries2.Points.DataBindXY(xval, yval2);
     }

     publicstaticvoid Main()
    {
      Graph graph1 = new Graph();
      graph1.ShowDialog();
    }
  }
}

Запускайте программу. Все хорошо, но хочется чего-то еще? А вот теперь можно обратиться к примерам, и все, что вас в них заинтересует, добавить в вашу программу.

График без данных не построишь, а наш "Специалист" не может вручную завести данные в компьютер (он же виртуальный). Во следующем разделе мы будем учить его брать данные из файлов.

Microsoft OLE DB Provider for Visual FoxPro

Загрузка: Драйвер для доступа к базам данных FoxPro можно свободно скачать с сайта www.microsoft.com.

Установка: в результате установки помимо драйвера – VfpOleDB.dll на диск записывается база данных - C:\Program Files\Microsoft Visual FoxPro OLE DB Provider\Samples\Northwind.

В локальной версии 1С 7.7 данные хранятся в dbf–файлах. Чтобы разобраться, как прочитать такой файл из программы на C#, необходимо изучить ADO.NET либо просто взять готовый шаблон. Такой шаблон я и хочу вам предложить. Если вы работаете с Microsoft Visual Studio, выберите шаблон "Пустой проект" и добавьте в него "Файл с текстом программы". Кроме того, в свойствах проекта поменяйте тип выходных данных на "Приложение Windows".

Формируем объект DataTable на основе dbf-файла

В обозревателе решений (solution explorer) добавьте ссылки (references) на:

Ниже приведен листинг программы:

        using System;
using System.Data;
using System.Windows.Forms;
using System.Data.OleDb;
using System.Xml;

namespace FoxPro
{
  publicclass MyConnect
  {
     const String DataProvider = @"vfpoledb.1"; // Провайдер// Создать OleDbConnectionpublic OleDbConnection OleDbConn(string DataPath)
    {
      // Для того, чтобы создать OLE – соединение с локальной базой данных,// необходимо указать 2 параметра – провайдера и путь к базе данных.// Для наглядности я воспользовался построителем строк подключения – // OleDbConnectionStringBuilder.
      OleDbConnectionStringBuilder bldr = new OleDbConnectionStringBuilder ();
      bldr.DataSource = DataPath; // Указываем путь
      bldr.Provider = DataProvider; // Указываем провайдера// Создаем подключение к источнику данных
      OleDbConnection myConn = new OleDbConnection (bldr.ConnectionString);
      return myConn;
    }

    // Прочитать dbf-файл в DataTablepublic DataTable CopyDataTable(OleDbConnection OleDbConn, string myDoc)
    {
      // Класс, необходимый для задания оператора SQL и источника данных
      OleDbCommand cmd = new OleDbCommand();
      cmd.CommandText = @"SELECT * FROM " + myDoc; // Задаем оператор SQL
      cmd.Connection = OleDbConn; // Задаем источник данных// Объект OleDbDataAdapter выполняет функцию моста       // между DataTable и источником данных.
       OleDbDataAdapter da = new OleDbDataAdapter ();
       da.SelectCommand = cmd;

       DataTable tbl = new DataTable();
       // Обеспечивает он такой мост с помощью метода Fill.
       da.Fill(tbl);
       return tbl;
    }
  }

  class FoxPro
  {
    publicstaticvoid Main()
    {
      MyConnect mycon = new MyConnect();
      // Создаем соединение с базой данных
      OleDbConnection OleDbCon = mycon.OleDbConn
        (@"C:\Program Files\Microsoft Visual FoxPro OLE DB Provider\Samples\Northwind\");
      // Открываем его
      OleDbCon.Open();
      // Копируем products.dbf в объект DataTable
      DataTable products = mycon.CopyDataTable(OleDbCon, "products");
      // Закрываем соединение
      OleDbCon.Close();
    }
  }
}

В самом начале программы мы создаем класс MyConnect для того, чтобы в дальнейшем упростить работу с dbf–таблицами. У него всего 2 метода:

  1. OleDbConn(string DataPath) – возвращающий соединение с базой данных по указанному пути – DataPath.
  2. CopyDataTable(OleDbConnection OleDbConn, string myDoc) – возвращающий объект DataTable (таблицу, с которой уже может работать C#), который является копией dbf–файла – myDoc.

Откомпилируйте и загрузите программу. На экран ничего не выводится и поэтому кажется, что ничего не происходит. Далее я расскажу, как сделать универсальный просмотрщик для DataTable, а пока давайте построим график, используя данные, полученные из dbf-файла.

DataTable - источник данных для Microsoft Chart Controls


Подробное описание класса Graph приведено выше. Сейчас мы обсудим только внесенные в программу изменения:

В обозреватель решений необходимо добавить ссылки на:

System.Drawing
System.Windows.Forms.DataVisualization

Соответственно в программу добавляем директивы using для этих пространств имен:

        using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;

В конструктор класса Graph добавляем 3 параметра:

DataTable tblgraph – источник данных,
string X – поле, которое будет отображаться по оси X,
string Y – поле, которое будет отображаться по оси Y.

Меняем исходные данные для графика:

        double[] yval1 = { 5, 6, 4, 6, 3 };
string[] xval = { "Январь", "Февраль", "Март", "Апрель", "Май" };
mySeries1.Points.DataBindXY(xval, yval1);

на

myChart.DataSource = tblgraph;
mySeries1.XValueMember = X;
mySeries1.YValueMembers = Y;

Ниже приведены добавленные операторы:

        ...
        using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;

namespace FoxPro
{
...
  class Graph : Form
  {
    public Graph(DataTable tblgraph, string X, string Y)
    {
...
      myChart.DataSource = tblgraph;

...
      myChart.BorderSkin.SkinStyle = BorderSkinStyle.Sunken;
...
      mySeries1.XValueMember = X;
      mySeries1.YValueMembers = Y;
...
    }
  }

  class FoxPro
  {
    publicstaticvoid Main()
    {
...
      // Создаем объект graph
      Graph graph = new Graph(products, "productname", "unitsinstock");
      // и выводим его на экран
      graph.ShowDialog();
...
    }
  }
}

Запускайте программу и, если работает, давайте подведем итоги. В этой статье мы научились получать данные из dbf-файлов и отображать эти данные на графике.

Я уже говорил, что пользователь будет видеть графики прогнозных функций и исходные данные, на основании которых они построены. Значит, "Маркетолог" должен уметь предоставлять данные не только в виде графиков, но и в виде таблиц.

DataGridView

В разделе “Microsoft OLE DB Provider for Visual FoxPro” мы не смогли посмотреть объект DataTable, созданный из dbf-файла. Для этого необходим универсальный "просмотрщик" с единственным аргументом – источником данных в виде объекта DataTable. Он будет особенно полезен, если вам, как и мне, приходится часто в работе использовать SQL-запросы. Почему? Потому что со сложными запросами никогда наверняка не знаешь, что должно получиться в результате, и посмотреть на то, что получилось, лишним не будет. Но об этом позже, а пока, если вы работаете с Microsoft Visual Studio, выберите шаблон "Пустой проект" и добавьте в него "Файл с текстом программы". Кроме того, в свойствах проекта поменяйте тип выходных данных на "Приложение Windows".

Минимальная функциональность


В обозревателе решений добавьте ссылки на:

Описание класса MyConnect и порядок работы приведены выше. В нижеприведенном листинге я указал только вновь добавленные операторы:

        using System;
using System.Data;
using System.Windows.Forms;
using System.Data.OleDb;
using System.Xml;

namespace Viewer
{
...
  publicclass Browse : Form
  {
    public Browse(DataTable tbl)
    {
       // Browse форма - свойства
       Text = "Viewer0";

       // dataGridView - свойства
       DataGridView dataGridView = new DataGridView();
       dataGridView.Parent = this;

       // указываем источник данных
       dataGridView.DataSource = tbl;
    }
  }

  class Viewer 
  {
     publicstaticvoid Main()
    {
...
       // Создаем объект browse
       Browse browse = new Browse(products);
       // и выводим его на экран
       browse.ShowDialog();

...
    }
  }
}

Компилируем, запускаем и смотрим, что получилось. Не правда ли – жуткое зрелище, поэтому давайте постепенно доводить программу до "ума".

Улучшаем внешний вид


Для начала зададим клиентскую область формы и определим положение и размеры объекта DataGridView относительно этой области. Для этого в обозреватель решений следует добавить ссылку на сборку System.Drawing, а в программу – директиву using System.Drawing:

...
using System.Drawing;

namespace Viewer
{
...
  publicclass Browse : Form
  {
    public Browse(DataTable tbl)
    {
...
       // Задаем размер клиентской области формы
       ClientSize = new Size(700, 400);
       // Располагаем форму в центре экрана
       StartPosition = FormStartPosition.CenterScreen;

...
       // Задаем размер таблицы относительно клиентской области формы
       dataGridView.Height = ClientSize.Height - 20;
       dataGridView.Width = ClientSize.Width - 20;
       // Задаем положение таблицы относительно левого верхнего угла формы
       dataGridView.Left = 10;
       dataGridView.Top = 10;
...
    }
  }
...
}

После запуска программы попробуйте изменить размер окна. Плохо, что DataGridView при этом не меняется.

Добавляем функциональности

Чтобы это исправить, нужно предусмотреть обработку события Resize. Кроме того, вы, наверное, заметили, что ширина колонок не соответствует ширине полей таблицы. При просмотре данных это не очень удобно, поэтому я добавил оператор, который автоматически подбирает ширину колонок:


...
namespace Viewer
{
...
  publicclass Browse : Form
  {
     internalprotected DataGridView dataGridView;

     public Browse(DataTable tbl)
    {
...
       // Browse форма - методы
       Resize += new EventHandler(BrowseResize);

       // dataGridView - свойства
       dataGridView = new DataGridView();
...
       // Подбираем в цикле ширину всех колонокforeach (DataColumn col in tbl.Columns)
       dataGridView.Columns[col.Caption].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
    }

     // Реакция формы на изменение размераvoid BrowseResize(object sender, EventArgs e)
    {
       // Подгоняем размер таблицы под клиентскую область формы
       dataGridView.Height = ClientSize.Height - 20;
       dataGridView.Width = ClientSize.Width - 20;
    }
  }
...
}

Запускайте – будет работать. Чтобы не переписывать один и тот же код по несколько раз, чуть дальше мы из уже рассмотренных классов сформируем библиотеку, а затем я вас научу читать данные из 1С 7.7.

Прямой доступ к базе данных 1С7.7

Большая часть российских предприятий для автоматизации бухгалтерского учета и экономических расчетов использует программу 1С. Эта программа предлагает вполне достаточную базовую функциональность, но никогда не сможет удовлетворить ВСЕ пожелания пользователей. Поэтому, если в программе, написанной на C#, научиться напрямую читать данные из баз 1С, то всю недостающую функциональность вы сможете сами реализовать в своих программах.

Выше мы создали 3 класса:

Создаем библиотеку классов

Если вы работаете с Microsoft Visual Studio, выберите шаблон "Пустой проект", укажите название проекта – "Tools" и добавьте в него "Файл с текстом программы". Кроме того, в свойствах проекта поменяйте тип выходных данных на "Библиотека классов". Не забудьте в обозреватель решений добавить ссылки на:

Все классы и методы я оставил без изменений, кроме метода CopyDataTable(OleDbConnection OleDbConn, string myDoc). В новой версии он стал более универсальным, так как теперь ему передается не название dbf-файла, а сторока SQL-запроса:

        using System;
using System.Data;
using System.Windows.Forms;
using System.Drawing;
using System.Xml;
using System.Data.OleDb;
using System.Windows.Forms.DataVisualization.Charting;

namespace tools
{
  publicclass MyConnect
  {
...
    // Прочитать dbf-файл в DataTablepublic DataTable CopyDataTable(OleDbConnection OleDbConn, string mySQL)
    {
      // Класс, необходимый для задания оператора SQL и источника данных
      OleDbCommand cmd = new OleDbCommand();
      cmd.CommandText = @mySQL; // Задаем оператор SQL
      cmd.Connection = OleDbConn; // Задаем источник данных// Объект OleDbDataAdapter выполняет функцию моста       // между DataTable и источником данных.
      OleDbDataAdapter da = new OleDbDataAdapter();
      da.SelectCommand = cmd;

      DataTable tbl = new DataTable();
      // Обеспечивает он такой мост с помощью метода Fill.
      da.Fill(tbl);
      return tbl;
    }
  }
...
}

Постройте решение ("Build Solution") и проверьте в папке ...\bin\Debug\ наличие файла Tools.dll. Библиотека готова, и можно приступить к работе с базой 1С7.7. Она существует в двух версиях: локальная и SQL. Весь изложенный материал применим к обеим из них, разница лишь в методе подключения к базе данных. Например, для подключения к SQL-базе можно вместо метода OleDbConn(string DataPath) использовать следующий метод:

        public SqlConnection SqlConn(string DataPath)
    {
      SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
      bldr.DataSource = @".\SQLEXPRESS";
      bldr.InitialCatalog = DataPath;
      bldr.IntegratedSecurity = true;
      bldr.ConnectTimeout = 30;
      bldr.UserInstance = true;
      SqlConnection myConn = new SqlConnection(bldr.ConnectionString);
      return myConn;
    }

В локальной версии база данных 1С7.7 состоит из совокупности файлов формата Dbase. Информация о назначении и структуре каждого файла указана в файле 1Cv7.dd. Обычно он находится там же, где и база данных. Как это выглядит, разберем на примере документа "Расходная накладная":

#===============================================================================
#==TABLE no 172  : Документ РасходнаяНакладная
# Name  |Descr             |Type[A/S/U]|DBTableName|ReUsable
T=DH294   |Документ РасходнаяНакладная   |A      |DH294    |1
#-----Fields-------
# Name    |Descr         |Type|Length|Precision
F=IDDOC   |ID Document's     |C   |9   |0 – код документаF=SP277   |(P)Контрагент     |C   |9   |0 – код контрагента
F=SP278   |(P)Договор      |C   |9   |0
...

#===============================================================================
#==TABLE no 173  : Документ (Мн.ч.) РасходнаяНакладная
# Name  |Descr             |Type[A/S/U]|DBTableName|ReUsable
T=DT294   |Документ (Мн.ч.) РасходнаяНакл|A      |DT294    |1
#-----Fields-------
# Name    |Descr         |Type|Length|Precision
F=IDDOC   |ID Document's     |C   |9   |0 – код документа
F=LINENO  |LineNo        |N   |4   |0
F=SP283   |(P)Товар      |C   |9   |0 – код товараF=SP284   |(P)Количество     |N   |15  |3F=SP285   |(P)Цена       |N   |16  |2F=SP286   |(P)Сумма      |N   |16  |2F=SP287   |(P)НДС        |N   |16  |2
F=SP289   |(P)НП         |N   |16  |2
...

Видно, что хранение информации организовано по канонам СУБД:

Поэтому нам еще потребуется информация из трех справочников:

#===============================================================================
#==TABLE no 3    : Журналы
# Name  |Descr             |Type[A/S/U]|DBTableName|ReUsable
T=1SJOURN |Журналы             |A      |1SJOURN  |1
#-----Fields-------
# Name    |Descr         |Type|Length|Precision
F=IDJOURNAL |ID of Journal     |C   |4   |0
F=IDDOC   |ID Document     |C   |9   |0 – код документа
F=IDDOCDEF  |ID Def Document   |C   |4   |0
F=APPCODE   |Application code  |N   |3   |0
F=DATE    |date        |D   |8   |0
F=TIME    |Time        |C   |6   |0
F=DNPREFIX  |Prefix Document No  |C   |18  |0
F=DOCNO   |Document No     |C   |10  |0
F=CLOSED  |Flag Document is Clo|N   |1   |0
...

#===============================================================================
#==TABLE no 26   : Справочник Контрагенты
# Name  |Descr             |Type[A/S/U]|DBTableName|ReUsable
T=SC133   |Справочник Контрагенты    |A      |SC133    |1
#-----Fields-------
# Name    |Descr         |Type|Length|Precision
F=ID    |ID object       |C   |9   |0 – код контрагента
F=PARENTID  |ID parent obj     |C   |9   |0
F=CODE    |object code     |C   |8   |0
F=DESCR   |object description  |C   |30  |0 – название контрагента
F=ISFOLDER  |Flag - Is Line - Fol|N   |1   |0
...

#===============================================================================
#==TABLE no 33   : Справочник Номенклатура
# Name  |Descr             |Type[A/S/U]|DBTableName|ReUsable
T=SC156   |Справочник Номенклатура     |A      |SC156    |1
#-----Fields-------
# Name    |Descr         |Type|Length|Precision
F=ID    |ID object       |C   |9   |0 – код товара
F=PARENTID  |ID parent obj     |C   |9   |0
F=CODE    |object code     |C   |9   |0
F=DESCR   |object description  |C   |50  |0 – название товара
F=ISFOLDER  |Flag - Is Line - Fol|N   |1   |0
...

Всю необходимую информацию для написания SQL-запроса к базе данных 1С7.7 мы собрали. Формируем документ "Расходная накладная":

        SELECT _1sjourn.docno, _1sjourn.date, sc133.descr AS Client, "sc156.descr AS Good,
     dt294.sp284 AS Number, dt294.sp285 AS Price, dt294.sp286 AS Summa,
     dt294.sp287 AS NDS, dt294.sp290 AS Total
  FROM dh294
   INNERJOIN 1sjourn _1sjourn ON dh294.iddoc = _1sjourn.iddoc
   INNERJOIN sc133 ON dh294.sp277 = sc133.id
   INNERJOIN dt294 ON dh294.iddoc = dt294.iddoc
   INNERJOIN sc156 ON dt294.sp283 = sc156.id

Теперь перейдем к созданию исполняемой программы. Выберите шаблон "Пустой проект", укажите название проекта – "1С77" и добавьте в него "Файл с текстом программы". Кроме того, в свойствах проекта поменяйте тип выходных данных на "Приложение Windows".

Используем Browse для просмотра документов – "Расходная накладная"


В обозревателе решений указываем ссылки на:

Кроме того, надо добавить ссылку на нашу библиотеку Tools. Разница лишь в том, что в окне "Добавить ссылку" надо вместо вкладки ".NET" выбрать вкладку "Обзор" и найти Tools.dll. Ниже приведен листинг программы:

        using System;
using System.Data;
using System.Data.OleDb;
using tools;

namespace _1C77
{
  publicclass SQL1C
  {
     // Документу из 1С приводим в соответствие SQL запросpublicstring SQL1C77(string NameDoc)
     {
      string StringSQL = "SELECT * FROM " + NameDoc;
      switch (NameDoc)
      {
        case"РасходнаяНакладная":
          StringSQL = "SELECT _1sjourn.docno, _1sjourn.date, sc133.descr" +          "AS Client, sc156.descr AS Good, " +
          "dt294.sp284 AS Number, dt294.sp285 AS Price, " +
          "dt294.sp286 AS Summa, dt294.sp287 AS NDS, " +           "dt294.sp290 AS Total FROM dh294 " +
          "INNER JOIN 1sjourn _1sjourn ON dh294.iddoc = _1sjourn.iddoc " +
          "INNER JOIN sc133 ON dh294.sp277 = sc133.id " +
          "INNER JOIN dt294 ON dh294.iddoc = dt294.iddoc " +
          "INNER JOIN sc156 ON dt294.sp283 = sc156.id";
          break;
      }
      return StringSQL;
    }
  }

  class _1C77
  {
    publicstaticvoid Main()
    {
      MyConnect mycon = new MyConnect();
      // Создаем соединение с базой данных
      OleDbConnection OleDbCon = 
        mycon.OleDbConn(@"C:\Program Files\1Cv77\1SBDemo\");
      // Открываем его
      OleDbCon.Open();

      SQL1C mySQL1C = new SQL1C();
      // Выбираем SQL запрос для Расходной накладнойstring SQL1C1 = mySQL1C.SQL1C77("РасходнаяНакладная");
      // Указывая соединение с базой данных и строку SQL,       // получаем объект DataTable
       DataTable Doc1C1 = mycon.CopyDataTable(OleDbCon, SQL1C1);

       // Создаем объект browse
       Browse browse = new Browse(Doc1C1);
       // и выводим его на экран
       browse.ShowDialog();

       // Закрываем соединение
       OleDbCon.Close();
     }
  }
}

Принцип, думаю, понятен, и теперь вы сами сможете организовать запрос к любому документу в базе 1С7.7. В заключение рассмотрим пример использования созданной нами библиотеки (класс Graph) и открытия другого документа 1С "Платежного поручения":

Строим график, используя данные документов – "Платежное поручение"


...
namespace _1C77
{
  publicclass SQL1C
  {
    // Документу из 1С приводим в соответствие SQL запросpublicstring SQL1C77(string NameDoc)
    {
      string StringSQL = "SELECT * FROM " + NameDoc;
      switch (NameDoc)
      {
        case"РасходнаяНакладная":
          StringSQL = "SELECT _1sjourn.docno, _1sjourn.date, " + 
          "sc133.descr AS Client, sc156.descr AS Good, " +
          "dt294.sp284 AS Number, dt294.sp285 AS Price, " +
          "dt294.sp286 AS Summa, dt294.sp287 AS NDS, " +
          "dt294.sp290 AS Total FROM dh294 " +
          "INNER JOIN 1sjourn _1sjourn ON dh294.iddoc = _1sjourn.iddoc " +
          "INNER JOIN sc133 ON dh294.sp277 = sc133.id " +
          "INNER JOIN dt294 ON dh294.iddoc = dt294.iddoc " +
          "INNER JOIN sc156 ON dt294.sp283 = sc156.id";
          break;
        case"ПлатежноеПоручение":
          StringSQL = "SELECT _1sjourn.docno, _1sjourn.date, " +
          "sc133.descr AS Client, dh12070.sp12059 AS Summa, " +
          "dh12070.sp12061 AS NDS FROM dh12070 " +
          "INNER JOIN 1sjourn _1sjourn ON dh12070.iddoc = _1sjourn.iddoc " +
          "INNER JOIN sc133 ON dh12070.sp12058 = sc133.id";
          break;
      }
      return StringSQL;
    }
  }

  class _1C77
  {
    publicstaticvoid Main()
    {
...
      // Выбираем SQL-запрос для Платежного порученияstring SQL1C2 = mySQL1C.SQL1C77("ПлатежноеПоручение");
...

      // Создаем объект graph
      Graph graph = new Graph(Doc1C2, "date", "summa");
      // и выводим его на экран
      graph.ShowDialog();

...
    }
  }
}

Итак, вы научились получать данные из базы данных 1С7.7, посмотрели их в DataGridView и построили на основе этих данных график. Для полного комплекта не хватает научиться:

Разберем универсальный метод сохранения объекта DataTable в dbf-файл.

Сохранение данных

В разделе “Microsoft OLE DB Provider for Visual FoxPro” мы рассмотрели класс MyConnect, позволяющий создать подключение к БД и скопировать dbf–файл в объект DataTable.

Затем в разделе “Прямой доступ к базе данных 1С7.7” мы переделали метод CopyDataTable, сделав его более универсальным, и поместили класс MyConnect в созданную библиотеку –Tools.dll.

Теперь мы добавим в класс MyConnect еще один метод, SaveDbf(OleDbConnection myConn, DataTable tbl, string myDoc), который будет сохранять любой объект DataTable в dbf–файл с заданным названием myDoc. Как обычно, сначала мы рассмотрим самый простой вариант метода, затем будем делать его универсальным.

Частный способ сохранения данных

Рассмотрим вариант метода с использованием SQL-запросов. Сначала создадим пустой файл с заданной структурой полей, а затем построчно его заполним информацией из DataTable. Откройте исходную текст библиотеки классов Tools и в класс MyConnect добавьте новый метод:

        // Сохранение в dbf файле
        public
        void SaveDbf(OleDbConnection myConn, DataTable tbl, string myDoc)
{
  // Строка SQL-запроса на создание пустого dbf-файла с заданной структуройstring strCreate = "CREATE TABLE " + myDoc + "(Good char(50) NULL, " +
    "Number numeric(11) NULL, Price numeric(14,2) NULL, " +
    "Summa numeric(14,2) NULL, Date dbdate NULL)";
  // Класс, необходимый для задания оператора SQL и источника данных
  OleDbCommand myComC = new OleDbCommand(strCreate, myConn);
  // Выполняет оператор SQL
  myComC.ExecuteNonQuery(); // Создает на диске файл// Заполняем созданный файл данными из DataTableforeach (DataRow row in tbl.Rows) // Цикл по записям
  {
    // Строка SQL-запроса на добавление заполненной данными записи в dbf-файлstring strInsert = "INSERT INTO " + myDoc + "(Number, " +
      "Price, Summa, Good, Date) VALUES (?, ?, ?, ?, ?)";
    // Класс, необходимый для задания оператора SQL и источника данных
    OleDbCommand myComI = new OleDbCommand(strInsert, myConn);
    // Вместо знаков вопроса в строку SQL-запроса подставляются выражения:
    myComI.Parameters.AddWithValue("@Number", row["Number"]);
    myComI.Parameters.AddWithValue("@Price", row["Price"]);
    myComI.Parameters.AddWithValue("@Summa", row["Summa"]);
    myComI.Parameters.AddWithValue("@Good", row["Good"]);
    myComI.Parameters.AddWithValue("@Date", row["Date"]);
    // Выполняет оператор SQL
    myComI.ExecuteNonQuery(); //Добавляет строку
  }
}

Перекомпилируйте Tools.dll и откройте проект "1С77", рассмотренный в разделе “Прямой доступ к базе данных 1С7.7”. В этом проекте мы на основе данных 1С7.7 создали объект DataTable. Сейчас мы добавим в этот проект строку, позволяющую сохранить полученный DataTable в dbf-файл:

...
namespace _1C77
{
...
  class _1C77
  {
     publicstaticvoid Main()
     {
...
       // Выбираем SQL запрос для Расходной накладнойstring SQL1C1 = mySQL1C.SQL1C77("РасходнаяНакладная");
...
       mycon.SaveDbf(OleDbCon, Doc1C1, "Shipment");

...
     }
  }
}

Компилируйте, запускайте – будет работать. В каталоге, который вы указали в параметре метода OleDbConn класса MyConnect (у меня это C:\Program Files\1Cv77\1SBDemo), должен появиться файл Shipment.dbf.

Универсальный способ сохранения данных

Сейчас мы рассмотрим метод сохранения данных из любого объекта DataTable. Для этого необходимо автоматизировать формирование двух SQL-строк – CREATE TABLE... и INSERT INTO... В библиотеке Tools.dll поменяйте метод SaveDbf на:

        // Сохранение в dbf-файле
        public
        void SaveDbf(OleDbConnection myConn, DataTable tbl, string myDoc)
{
  int MaxLenght; // Максимальная длина поля// Начало строки SQL-запроса на создание dbf-файлаstring strCreate = "CREATE TABLE " + myDoc + " (";
  // Начало строки SQL-запроса на добавление записи в dbf-файлstring strInsert1 = "INSERT INTO " + myDoc + " (";
  // Конец строки SQL-запроса на добавление записи в dbf-файлstring strInsert2 = " VALUES (";
  // Строка, последовательно добавляющая в SQL-запрос   // название, тип и длину всех полейstring strAdd;

  // Последовательно анализируем поля DataTable// Дописываем строку SQL-запроса на создание dbf-файлаforeach (DataColumn column in tbl.Columns) // Цикл по полям
  {
    switch (column.DataType.ToString())
    {
      case"System.Boolean":
        strAdd = " logical, ";
        break;
      case"System.DateTime":
        strAdd = " dbdate, ";
        break;
      case"System.Decimal":
        strAdd = " numeric(16,2), ";
        break;
      default:
        MaxLenght = 0;
        // Во всех остальных случаях считаем поле символьным// В цикле определяем максимальную длину поляforeach (DataRow row in tbl.Rows) // Цикл по записям
        {
          if (MaxLenght <
 row[column.ColumnName.ToString()].ToString().Trim().Length)
            MaxLenght = 
              row[column.ColumnName.ToString()].ToString().Trim().Length;
        }
        strAdd = " char(" + MaxLenght.ToString() + "), ";
        break;
    }
    // К строке SQL-запроса на создание dbf-файла добавляем     // название поля, тип и длину
    strCreate = strCreate + column.ColumnName.ToString() + strAdd;
    // К строкам SQL-запроса на добавление записи в dbf-файл     // добавляем названия полей
    strInsert1 = strInsert1 + column.ColumnName.ToString() + ", ";
    strInsert2 = strInsert2 + "?, "; // ? - символ параметра в SQL-запросах
  }
  // Удаляем 2 последних лишних символа
  strCreate = strCreate.Remove(strCreate.Length - 2);
  // Добавляем закрывающую скобку
  strCreate = strCreate + ")";
  strInsert1 = strInsert1.Remove(strInsert1.Length - 2);
  strInsert1 = strInsert1 + ")";
  strInsert2 = strInsert2.Remove(strInsert2.Length - 2);
  strInsert2 = strInsert2 + ")";
  string strInsert = strInsert1 + strInsert2;

  // Класс, необходимый для задания оператора SQL и источника данных
  OleDbCommand myComC = new OleDbCommand(strCreate, myConn);
  // Выполняет оператор SQL
  myComC.ExecuteNonQuery(); // Создает на диске файл// Заполняем созданный файл данными из DataTableforeach (DataRow row in tbl.Rows) // Цикл по записям
  {
    OleDbCommand myComI = new OleDbCommand(strInsert, myConn);
    // в цикле добавляем параметр, задав его имя и значение.foreach (DataColumn column in tbl.Columns) // Цикл по полям
    {
      myComI.Parameters.AddWithValue(
        column.ColumnName.ToString(), row[column.ColumnName.ToString()]);
    }
    myComI.ExecuteNonQuery(); //Добавляет строку
  }
}

Для определения типа и размера колонок объекта DataTable создаем двойной цикл по полям и строкам таблицы. При использовании таблиц с большим количеством строк или большим количеством символьных полей данный вариант метода SaveDbf будет "тормозить". Поэтому его можно и нужно совершенствовать дальше. Если есть "изящные" идеи – пишите мне на virtualexpert@mail.ru. Буду очень рад пообщаться.

Вновь перекомпилируйте Tools.dll и откройте проект "1С77". Сохраним и расходные накладные, и платежные поручения:

...
namespace _1C77
{
...
  class _1C77
  {
    publicstaticvoid Main()
    {
...
      // Выбираем SQL запрос для Платежного порученияstring SQL1C2 = mySQL1C.SQL1C77("ПлатежноеПоручение");
      // Указывая соединение с базой данных и строку SQL,       // получаем объект DataTable
      DataTable Doc1C2 = mycon.CopyDataTable(OleDbCon, SQL1C2);

      mycon.SaveDbf(OleDbCon, Doc1C2, "Payment");

...
    }
  }
}

Только что рассмотренный метод сохранения объекта DataTable в dbf-файл нам пригодится ниже, при работе над компонентом ReportViewer.

ReportViewer

Как сделать отчет для объекта DataTable

Сначала пойдем проторенной дорогой - в Visual Studio в качестве проекта выберем "Приложение для отчетов". При этом запускается «мастер отчетов», который предлагает выбрать источник данных. В качестве поставщика данных выбираем "Microsoft OLE DB Provider for Visual FoxPro", а в поле "Имя сервера или файла" указываем каталог, в котором сохранили (в предыдущем разделе “Сохранение данных”) файлы shipment.dbf и payment.dbf (у меня это был C:\Program Files\1Cv77\1SBDemo\). Затем выбираем тип отчета - «табличный», макет таблицы – «блок» и стиль – «сланец». Результат работы мастера показан на картинке:


Все богатство кода, сгенерированного мастером, можно уложить в 4 строки:

ReportViewer reportviewer = new ReportViewer();
reportviewer.LocalReport.ReportPath = @"...\Report1.rdlc";
reportviewer.LocalReport.DataSources.Add(new ReportDataSource("DataSet1_shipment", tbl));
reportviewer.RefreshReport();

В первой строке создается собственно сам объект – ReportViewer. Во второй строке указан путь к макету отчета (файлу с расширением .rdlc). В третьей строке указаны источник данных – tbl (объект DataTable) и название источника данных в макете отчета – DataSet1_shipment (см. картинку ниже). И, наконец, в последней строке активизируется созданный объект ReportViewer.


Не верите? В проект "1С77", рассмотренный нами в разделе “Прямой доступ к базе данных 1С7.7” добавьте класс, состоящий из этих операторов, а в обозреватель решений – ссылку на Microsoft.ReportViewer.WinForms. Ниже приведен листинг программы:

...
using Microsoft.Reporting.WinForms;
using System.Windows.Forms;

namespace _1C77
{
...
  publicclass report : Form
  {
    public report(DataTable tbl)
    {
      ReportViewer reportviewer = new ReportViewer();
      reportviewer.Parent = this;
      // Указываем путь к макету отчета – файлу с расширением .rdlc
      reportviewer.LocalReport.ReportPath = @"...\Report1.rdlc";
      // Добавляем источник данных// tbl – объект DataTable с данными// DataSet1_shipment – название источника данных в макете отчета
      reportviewer.LocalReport.DataSources.Add(
        new ReportDataSource("DataSet1_shipment", tbl));
      reportviewer.RefreshReport();
    }
  }

  class _1C77
  {
    publicstaticvoid Main()
    {
...
       // Создаем объект myReport
       report myReport = new report(Doc1C1);
       // и выводим его на экран
       myReport.ShowDialog();

...
    }
  }
}

Хотелось бы сказать, что самый быстрый способ сделать отчет для объекта DataTable – следующий:

Делаем привязку к форме


Чтобы привязать отчет к форме, зададим клиентскую область и определим положение и размеры объекта ReportViewer относительно этой области. Кроме того, нужно предусмотреть обработку события Resize. Для этого в обозреватель решений следует добавить ссылку на сборку System.Drawing, а в программу – директиву using System.Drawing:

...
using System.Drawing;

namespace _1C77
{
...
  publicclass report : Form
  {
    internalprotected ReportViewer reportviewer;

    public report(DataTable tbl)
    {
      // Задаем размер клиентской области формы
      ClientSize = new Size(700, 400);
      // Располагаем форму в центре экрана
      StartPosition = FormStartPosition.CenterScreen;

      reportviewer = new ReportViewer();
      reportviewer.Parent = this;

      // Задаем размер отчета относительно клиентской области формы
      reportviewer.Height = ClientSize.Height - 20;
      reportviewer.Width = ClientSize.Width - 20;
      // Задаем положение отчета относительно левого верхнего угла формы
      reportviewer.Left = 10;
      reportviewer.Top = 10;

...

      // Методы
      Resize += new EventHandler(ReportResize);
    }

    // Реакция формы на изменение размераvoid ReportResize(object sender, EventArgs e)
    {
      // Подгоняем размер отчета под клиентскую область формы
      reportviewer.Height = ClientSize.Height - 20;
      reportviewer.Width = ClientSize.Width - 20;
    }
  }

  class _1C77
  {
    publicstaticvoid Main()
    {
...
    }
  }
}

Сам макет отчета я переделал на свой вкус. Этим разделом мы заканчиваем рассмотрение способов представления информации. Напомню, наш “Маркетолог” должен уметь представлять информацию в виде графиков, таблиц и отчетов. Кроме того, мы научили его брать информацию из dbf–файлов и баз данных 1С 7. В следующей статье изучим, как можно напрямую обратиться к базе данных 1С 8.


Эта статья опубликована в журнале RSDN Magazine #1-2010. Информацию о журнале можно найти здесь
    Сообщений 8    Оценка 70 [+1/-1]         Оценить