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

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

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

Сложные графики и диаграммы в ASP.NET. Часть четвёртая – ChartSpace.

Введение

В предыдущих статьях серии рассматривались способы построения графиков через компоненты Windows, в частности owc, и System.Drawing. Я надеюсь, Вам они понравились. Особенно хороша 3-я часть. В рамках данной статьи мы продолжаем знакомить читателей с диаграммами и предлагаем ещё один способ. В этот раз Вам не потребуется много времени на разработку, а набор диаграмм, который Вы получаете, такой же как в MS Office 2003.

Для работы нам понадобится VS.NET любой версии и MS Office 2003 в полном составе «загружать всё с моего компьютера». На хостовый сервер мы перенесём все необходимые библиотеки, так что там устанавливать Office не потребуется. Разработчики компонента утверждают, что предпочтительно использовать Windows версии XP или выше, иначе могут быть проблемы с тем, что объёмные диаграммы отобразятся как плоские. Вообще, основная масса компонентов Microsoft сделана в основном для VB и в меньшей степени для C# и других языков; библиотека классов ChartSpace – не исключение. Тем не менее, следуя традиции этой серии статей, я приведу примеры на C#. Основное отличие от VB в том, что там работают все возможности, для прочих же языков оставлены лишь главные из них – все методы (процедуры и функции) в C# имеют только одну перегрузку с обязательным указанием всех параметров. Поэтому коды, переведённые дословно из C# в VB будут работать, но не наоборот. В Интернете и у Вас на компьютере есть документация для работы с этим компонентом, но опять-таки только на VB, поэтому не удивляйтесь, если используя ту или иную возможность, вы столкнётесь с проблемами (файл OWCVBA11.CHM можно найти в папке, имеющей примерный путь C:\Program Files\Common Files\Microsoft Shared\Web Components\11\1049; если он отсутствует, то Вам придётся переставить MS Office 2003 в режиме «загружать всё с моего компьютера», см. рис. 1.). В любом случае терпение и труд всё перетрут ;-)

Рис. 1. Обратите внимание на установку MS Office 2003.

Существует два способа работы с диаграммами – через сохранение файла с картинкой на диске и через HTTP-обработчик (интерфейс IHttpHandler, далее этот обработчик мы будем называть HTTPHandler-ом). Второй способ можно упростить, если Вы используете элемент управления DynamicImage (динамическую картинку можно нацелить не на Url, а прямо на поток байтов). Предполагается, что этот элемент управления появится в ASP.NET 2 (не смотря на то, что во 2-й бета-версии его нет). В любом случае этот элемент управления можно скачать с сайта журнала MSDN Magazine по адресу: http://download.microsoft.com/download/1/6/4/164c2a20-aeb0-460f-907d-985d83e86bd4/CuttingEdge0404.exe - это зазипованный архив проекта, содержащий DynamicImage для ASP.NET 1.1.

Рассмотрим 1-й способ - через сохранение файла с картинкой на диске.

Создание диаграммы будем проводить по шагам:

  1. В проекте добавим ссылку на компонент диаграммы.
  2. На Вэб-страницу добавим картинку.
  3. Создадим запрос на выборку к базе данных, результаты которого отобразим на диаграмме.
  4. Экспортируем изображение диаграммы в файл и нацелим картинку на этот файл.

А теперь рассмотрим более подробно на примере.

  1. В проекте добавим ссылку на компонент диаграммы.

Добавим ссылку на компонент в References (см. рисунки)

Рис. 2. Область Solution Explorer.

Рис. 3. Как добавить ссылку.

Рис. 4. Что добавлять.

Рис. 5. Как это выглядит теперь.

  1. На Вэб-страницу добавим картинку Image1.

3, 4. Создадим запрос на выборку к базе данных, результаты которого отобразим на диаграмме. И экспортируем изображение диаграммы в файл и нацелим картинку на этот файл.

 using System; 
 using System.Collections; 
 using System.ComponentModel; 
 using System.Data; 
 using System.Drawing; 
 using System.Web; 
 using System.Web.SessionState; 
 using System.Web.UI; 
 using System.Web.UI.WebControls; 
 using System.Web.UI.HtmlControls; 
 namespace WebApplication2 
 { 
    /// <summary> 
    /// Summary description for WebForm1. 
    /// </summary> 
    public class WebForm1 : System.Web.UI.Page 
    { 
       //var 
       protected System.Web.UI.WebControls.Image Image1; 
       //end var 
       // Блок инициализации страницы 
       #region Web Form Designer generated code 
       override protected void OnInit(EventArgs e) 
       { 
          // 
          // CODEGEN: This call is required by the ASP.NET Web Form Designer. 
          // 
          InitializeComponent(); 
          base.OnInit(e); 
       } 
       /// <summary> 
       /// Required method for Designer support - do not modify 
       /// the contents of this method with the code editor. 
       /// </summary> 
       private void InitializeComponent() 
       {   
          this.Load += new System.EventHandler(this.Page_Load); 
       } 
       #endregion 
       // Процедура загрузки страницы 
       private void Page_Load(object sender, System.EventArgs e) 
       { 
          // Put user code to initialize the page here 
          //var 
          Int32 iConst=
            Convert.ToInt32(Microsoft.Office.Interop.Owc11.ChartSpecialDataSourcesEnum.chDataBound); 
          Microsoft.Office.Interop.Owc11.ChartSpaceClass ChartSpace1=
            new Microsoft.Office.Interop.Owc11.ChartSpaceClass(); 
          Microsoft.Office.Interop.Owc11.ChartDimensionsEnum chConstants=
            new Microsoft.Office.Interop.Owc11.ChartDimensionsEnum(); 
          //end var 
          //активизация заголовка диаграммы 
          ChartSpace1.HasChartSpaceTitle=true; 
          //создание заголовка 
          ChartSpace1.ChartSpaceTitle.Caption="Таблица tabNumB"; 
          //создание строки соединения с базой данных 
          ChartSpace1.ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=True;User ID=" +
           "ИмяПользователя;Data Source=ИмяИлиIP-адресСервераSQL;Use Procedure for Prepare=1;" +
           "Auto Translate=True;Packet Size=4096;Workstation ID=ИмяИлиIP-адресХостовогоСервера" +
           "(необязательный параметр);Use Encryption for Data=False;Tag with column collation when" +
           " possible=False;Initial Catalog=ИмяКаталогаБазыДанных;password=Пароль"; 
          //создание запроса на выборку 
          ChartSpace1.CommandText="SELECT dbo.tabNumB.NameMax AS [Наименование], dbo.tabNumB.Id " +
           "AS [Число] FROM dbo.tabNumB;"; 
          //определяем область категорий 
          ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimCategories,
            iConst,"Наименование"); 
          //определяем область значений 
          ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimValues,
            iConst,"Число"); 
          //определяем формат значений 
          ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimFormatValues,
            iConst,"Число"); 
          //отключаем изображение кнопок 
          ChartSpace1.DisplayFieldButtons=false; 
          //устанавливаем тип диаграммы 
          ChartSpace1.Charts[0].Type=
            Microsoft.Office.Interop.Owc11.ChartChartTypeEnum.chChartTypeColumnClustered; 
          //сгенерируем имя файла, равное числу тиков нетовских часов - длинное целое число 
          String sFileName=DateTime.Now.Ticks.ToString()+".gif"; 
          //установим путь для записи файла 
          String sPath="C:\\Inetpub\\wwwroot\\"+sFileName; 
          //а так выглядит Url 
          String sUrl="http://localhost/"+sFileName; 
          //запись файла с картинкой в формате gif 
          ChartSpace1.ExportPicture(sPath,"gif",300,300); 
          //ну и нацеливаем картинку на Url 
          Image1.ImageUrl=sUrl; 
       } 
    } 
 }

Не правда ли простенький код? А вот какой результат нас ожидает:

Рис. 6. Конечный результат

Остаётся сравнить этот график с результатом запроса SQL:

Рис. 7. Результаты запроса на выборку совпали с диаграммой.

Комментарии к этому способу.

  1. Каждый раз сохраняется новый файл изображения, поэтому Вам придётся предусмотреть способ удаления старых файлов. Я бы предложил создать пакетный файл delgif.bat, содержащий команду MS-DOS «del *.gif» и прописать его в расписании хостового сервера с запуском в 3 часа ночи (в это время все уже спят, но никто ещё не проснулся).
  2. Вы можете спросить, зачем вдруг понадобилось каждый раз генерить новое имя файла, ведь по идее лучше было бы указать какое-то одно имя файла, равное, например, номеру текущей сессии. Ответ состоит в том, что элемент управления Image в ASP.NET любой версии – статический элемент. Это означает, что если в Url прописать имя файла картинки и пользователь загрузит эту картинку в своём обозревателе, то обновляй файл на новую картинку или нет, а пользователь как видел первую картинку, так и будет её видеть. Всё дело в том, что обозреватель Интернета сохраняет на компьютере пользователя все картинки, которые были загружены из Интернета для повторного использования. Это очень полезная вещь, так как экономится трафик и время загрузки Вэб-страниц. Как уже упоминалось, в ASP.NET 2-й версии ожидается появление динамического элемента управления DynamicImage, который каждый раз загружает новое изображение. Мы рассмотрим работу с ним ниже.

Примечание.

Библиотека Interop.OWC11 может иметь пространство имён отличное от Microsoft.Office.Interop.Owc11, для точного определения поищите слово «owc11» в обозревателе объектов. На рис. 8 показан случай, когда пространство имён называется «OWC11». Не забывайте, что в C# маленькие и большие буквы имеют разное толкование.

Рис. 8. Случай, когда для обращения к библиотеке необходимо пространство имён OWC11.

Рассмотрим 2-й способ – через обработчик HttpHandler.

Создание диаграммы будем проводить по шагам:

  1. В проекте добавим ссылку на компонент диаграммы.
  2. Создадим обработчик HttpHandler.
  3. Зарегистрируем обработчик.
  4. Нацелим картинку на обработчик.

А теперь рассмотрим более подробно на примере.

  1. В проекте добавим ссылку на компонент диаграммы.

Этот этап был рассмотрен выше. Просто добавляем ссылку на компонент COM (Microsoft Office Components 11) и всё.

2, 3, 4.  Создадим обработчик HttpHandler, зарегистрируем его если это класс и нацелим картинку на наш обработчик.

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

На страницей с картинкой Image просто напишем:

Image1.ImageUrl="Chart.aspx";

А на странице Chart.aspx изобразим всё то, что было в 1-м способе кроме элемента управления Image (за ненадобностью), единственное чуть-чуть подправим концовку процедуры Page_Load:

// Процедура загрузки страницы 
private void Page_Load(object sender, System.EventArgs e) 
{ 
  // Put user code to initialize the page here 
  //var 
  Int32 iConst=Convert.ToInt32(Microsoft.Office.Interop.Owc11.ChartSpecialDataSourcesEnum.chDataBound); 
  Microsoft.Office.Interop.Owc11.ChartSpaceClass ChartSpace1=
    new Microsoft.Office.Interop.Owc11.ChartSpaceClass(); 
  Microsoft.Office.Interop.Owc11.ChartDimensionsEnum chConstants=
    new Microsoft.Office.Interop.Owc11.ChartDimensionsEnum(); 
  //end var 
  //активизация заголовка диаграммы 
  ChartSpace1.HasChartSpaceTitle=true; 
  //создание заголовка 
  ChartSpace1.ChartSpaceTitle.Caption="Таблица tabNumB"; 
  //создание строки соединения с базой данных 
  ChartSpace1.ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=True;User ID=" + 
    "ИмяПользователя;Data Source=ИмяИлиIP-адресСервераSQL;Use Procedure for Prepare=1;" + 
    "Auto Translate=True;Packet Size=4096;Workstation ID=ИмяИлиIP-адресХостовогоСервера(" +
    "необязательный параметр);Use Encryption for Data=False;Tag with column collation when " +
    "possible=False;Initial Catalog=ИмяКаталогаБазыДанных;password=Пароль"; 
  //создание запроса на выборку 
  ChartSpace1.CommandText="SELECT dbo.tabNumB.NameMax AS [Наименование]," +
    " dbo.tabNumB.Id AS [Число] FROM dbo.tabNumB;"; 
  //определяем область категорий 
  ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimCategories,
    iConst,"Наименование"); 
  //определяем область значений 
  ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimValues,
    iConst,"Число"); 
  //определяем формат значений 
  ChartSpace1.SetData(Microsoft.Office.Interop.Owc11.ChartDimensionsEnum.chDimFormatValues,
    iConst,"Число"); 
  //отключаем изображение кнопок 
  ChartSpace1.DisplayFieldButtons=false; 
  //устанавливаем тип диаграммы 
  ChartSpace1.Charts[0].Type=
    Microsoft.Office.Interop.Owc11.ChartChartTypeEnum.chChartTypeColumnClustered; 
  //генерация бинарного потока в формате gif 
  Byte[] oImg=(Byte[])ChartSpace1.GetPicture("gif",300,300); 
  Response.ContentType="image/gif"; 
  Response.OutputStream.Write(oImg,0,oImg.Length); 
} 
 

Но, друзья, говоря по правде, делать так – себя не уважать – давайте лучше без лишних разговоров обработчик сделаем (применение специализированного HTTP-обработчика вместо обычной ASPX-страницы значительно эффективнее, поскольку обработчик не связан с таким длинным конвейером обработки, как обычная Вэб-страница; он меньше загружает исполняющую среду).

Рассмотрим листинг обработчика. Обратите внимание, что для расширения Вашего кругозора в коде применяется другое пространство имён: не «Microsoft.Office.Interop.Owc11», а «OWC11».

1-й способ. Через класс с регистрацией в Web.config

 // Имя этого C# файла Chart.cs и компилировать его можно воспользовавшись 
 // командной строкой: csc /t:Library /r:System.Web.dll Chart.cs 
 // Скопируйте Chart.dll в Вашу \bin папку. 
 using System; 
 using System.Web; 
 namespace HandlerChart 
 { 
    public class ChartHttpHandler : IHttpHandler 
    { 
       // Переопределяем метод ProcessRequest. 
       public void ProcessRequest(HttpContext context) 
       { 
          //var 
          Int32 iConst=Convert.ToInt32(OWC11.ChartSpecialDataSourcesEnum.chDataBound); 
          OWC11.ChartSpaceClass ChartSpace1=new OWC11.ChartSpaceClass(); 
          OWC11.ChartDimensionsEnum chConstants=new OWC11.ChartDimensionsEnum(); 
          Byte[] oImg; 
          //end var 
          //активизация заголовка диаграммы 
          ChartSpace1.HasChartSpaceTitle=true; 
          //создание заголовка 
          ChartSpace1.ChartSpaceTitle.Caption="Таблица tabNumB"; 
          //создание строки соединения с базой данных 
          ChartSpace1.ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=True;" +
            "User ID=ИмяПользователя;Data Source=ИмяИлиIP-адресСервераSQL;Use Procedure " +
            "for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=" +
            "False;Tag with column collation when possible=False;Initial Catalog=" +
            "ИмяКаталогаБазыДанных;password=Пароль"; 
          //создание запроса на выборку 
          ChartSpace1.CommandText="SELECT dbo.tabNumB.NameMax AS [Наименование], " +
		    "dbo.tabNumB.Id AS [Число] FROM dbo.tabNumB;"; 
          //определяем область категорий 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimCategories,iConst,"Наименование"); 
          //определяем область значений 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimValues,iConst,"Число"); 
          //определяем формат значений 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimFormatValues,iConst,"Число"); 
          //отключаем изображение кнопок 
          ChartSpace1.DisplayFieldButtons=false; 
          //устанавливаем тип диаграммы 
          ChartSpace1.Charts[0].Type=OWC11.ChartChartTypeEnum.chChartTypeColumnClustered3D; 
          //генерация бинарного потока в формате gif 
          oImg=(Byte[])ChartSpace1.GetPicture("gif",300,300); 
          context.Response.ContentType="image/gif"; 
          context.Response.OutputStream.Write(oImg,0,oImg.Length); 
       } 
       // Переопределяем свойство IsReusable. 
       public bool IsReusable 
       { 
          get { return true; } 
       } 
    } 
 } 
 /* 
 ______________________________________________________________ 
 Для применения этого HTTP-обработчика, поместите следующие строки в Web.config файл. 
 <configuration> 
   <system.web> 
    <httpHandlers> 
      <add verb="*" path="chart.aspx" type="Chart.HandlerChart.ChartHttpHandler,Chart"/> 
    </httpHandlers> 
   </system.web> 
 </configuration> 
 */

2-й способ. Через страницу ASHX без регистрации и без компиляции (рекомендуется).

Друзья, если Вы работаете в Студии VS.NET, то вряд ли станете использовать 1-й способ: он неудобен, так как требует ручной компиляции. Да и при работе без помощи Студии зачем самому компилировать? Предоставьте это ASP.NET-у. Он автоматически откомпилирует файл ASHX при первом обращении.

 <%@ WebHandler language="C#" class="HandlerChart.ChartHttpHandler" %> 
 // Имя этого HTML+C# файла Chart.ashx и компилировать его не надо. 
 using System; 
 using System.Web; 
 namespace HandlerChart 
 { 
    public class ChartHttpHandler : IHttpHandler 
    { 
       // Переопределяем метод ProcessRequest. 
       public void ProcessRequest(HttpContext context) 
       { 
          //var 
          Int32 iConst=Convert.ToInt32(OWC11.ChartSpecialDataSourcesEnum.chDataBound); 
          OWC11.ChartSpaceClass ChartSpace1=new OWC11.ChartSpaceClass(); 
          OWC11.ChartDimensionsEnum chConstants=new OWC11.ChartDimensionsEnum(); 
          Byte[] oImg; 
          //end var 
          //активизация заголовка диаграммы 
          ChartSpace1.HasChartSpaceTitle=true; 
          //создание заголовка 
          ChartSpace1.ChartSpaceTitle.Caption="Таблица tabNumB"; 
          //создание строки соединения с базой данных 
          ChartSpace1.ConnectionString="Provider=SQLOLEDB.1;Persist Security Info=True;" +
            "User ID=ИмяПользователя;Data Source=ИмяИлиIP-адресСервераSQL;Use Procedure " +
            "for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=" +
            "False;Tag with column collation when possible=False;Initial Catalog=" +
            "ИмяКаталогаБазыДанных;password=Пароль"; 
          //создание запроса на выборку 
          ChartSpace1.CommandText="SELECT dbo.tabNumB.NameMax AS [Наименование], dbo.tabNumB.Id AS" +
            " [Число] FROM dbo.tabNumB;"; 
          //определяем область категорий 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimCategories,iConst,"Наименование"); 
          //определяем область значений 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimValues,iConst,"Число"); 
          //определяем формат значений 
          ChartSpace1.SetData(OWC11.ChartDimensionsEnum.chDimFormatValues,iConst,"Число"); 
          //отключаем изображение кнопок 
          ChartSpace1.DisplayFieldButtons=false; 
          //устанавливаем тип диаграммы 
          ChartSpace1.Charts[0].Type=OWC11.ChartChartTypeEnum.chChartTypeColumnClustered3D; 
          //генерация бинарного потока в формате gif 
          oImg=(Byte[])ChartSpace1.GetPicture("gif",300,300); 
          context.Response.ContentType="image/gif"; 
          context.Response.OutputStream.Write(oImg,0,oImg.Length); 
       } 
       // Переопределяем свойство IsReusable. 
       public bool IsReusable 
       { 
          get { return true; } 
       } 
    } 
 }

Комментарии к этому способу.

  1. Чтобы нацелить картинку Image на HTTP-обработчик присвойте его свойству ImageUrl http-путь к файлу ASHX. В нашем случае обработчик находится в той же папке, что и страница с картинкой Image1:
Image1.ImageUrl="Chart.ashx";
  1. 2-й способ вообще-то требует передачи как минимум трёх параметров: ConnectionString, CommandText и названия. Это необходимо, чтобы управлять диаграммой, какжый раз выводя новую. Я предлагаю создать два массива, которые по номеру диаграммы выдавали бы эти два параметра. Тогда Вам останется в рамках Вашего проекта дополнять время от времени массивы, а картинкам присваивать ImageUrl=«Chart.ashx?con=НомерЭлементаМассиваСтрокСоединения&com=НомерЭлементаМассиваКоманд&cap= НомерЭлементаМассиваНаименований». Вот примерно какие строки добавятся в файл обработчика:
String[] sConnectionString=new String[1]; 
String[] sCommandText=new String[1]; 
String[] sCaption=new String[1]; 
sConnectionString[0]="Provider=SQLOLEDB.1;Persist Security Info=True;User ID=" +
  "ИмяПользователя;Data Source=ИмяИлиIP-адресСервераSQL;Use Procedure for " +
  "Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;" +
  "Tag with column collation when possible=False;Initial Catalog=" +
  "ИмяКаталогаБазыДанных;password=Пароль";
sCommandText[0]="SELECT dbo.tabNumB.NameMax AS [Наименование], dbo.tabNumB.Id AS " +
  "[Число] FROM dbo.tabNumB;"; 
sCaption[0]="Таблица tabNumB";
//создание заголовка
ChartSpace1.ChartSpaceTitle.Caption=sConnectionString[Convert.ToInt32(context.Request.QueryString["cap"])];
//создание строки соединения с базой данных 
ChartSpace1.ConnectionString=sConnectionString[Convert.ToInt32(context.Request.QueryString["con"])]; 
//создание запроса на выборку 
ChartSpace1.CommandText=sCommandText[Convert.ToInt32(context.Request.QueryString["com"])]; 
  1. В ASP.NET 2 (VS.NET 2005) предусмотрен специальный тип HTTP-обработчика HttpHandler для вывода картинок - ASIX. Но ASHX тоже работает.
  2. Используя DinamicImage, можно не применять обработчик вообще, присвоив картинке поток байтов:
DynamicImage1.ImageBytes=(Byte[])ChartSpace1.GetPicture("gif",300,300);

Примечание:

Подсветка кода ASHX не работает в старой версии Студии VS.NET 2003, но работает, начиная с 2005-й. Пример подсвечен 2005-й версией.

Рис. 9. Что происходит при обращении к странице ASHX (мы поменяли тип диаграммы на chChartTypeColumnClustered3D).

Обратите внимание, что для генерации объёмных диаграмм 3D необходим как минимум Windows XP, если у Вас Windows 2000, то объёма может не быть.

Заключение

Дорогие друзья, дополняя цикл статей, 4-я часть ещё больше упрощает работу с диаграммами в Вэб. Теперь у Вас, я надеюсь, не осталось стимулов использовать элементы управления сторонних разработчиков для работы с графиками. Даже самыми сложными. Делая ставку на стандартные средства Microsoft и свои умелые руки, Вы несомненно решите любую задачу. Вы полны решимости? Если да, то значит мы с автором предыдущих статей (Anatoly Lubarsky) не зря поработали.

Благодарности

Благодарю за поддержку Великого Учителя и Призрака Форума.


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


Автор: Max Pro
Прочитано: 6254
Рейтинг:
Оценить: 1 2 3 4 5

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

Прислал: Владимир
Мне надо сделать download owc11.exe удобный для необразованного пользователя. Ведь, казалось бы, отот объект сертифицирован и безопасен для загрузки. Но как сделать, чтобы не испугать потенциального пользователя программы непонятными сообщениями на

Прислал: Владимир
английском языке? Если кто знает, напишите на VOBEL @ narod.ru

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

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