Компонент StringGrid Delphi

Совместная работа StringGrid и Excel




Уроки Delphi
  1.  Первая программа
  2.  Использование компонентов
  3.  События Delphi
  4.  Типы данных Delphi
  5.  Создание своих типов данных
  6.  Выражения и операторы
  7.  Работа с файлами в Delphi
  8.  Дополнительные формы
  9.  Подпрограммы в Delphi
  10. Исключительные ситуации
  11. Взаимодействие приложения с пользователем
  12. Указатели в Delphi
  13. Обзор компонентов
  14. Работа со строками
  15. Создание интерфейса
  16. Графика в Delphi
  17. Многопоточность в Delphi
  18. Динамическое создание
        компонентов
Поиск по сайту




 Это важно:
   Метод Application.ProcessMessages;

 Это полезно:
   Параметр Sender в обработчиках событий;










Бояться не надо



Если решили купить готовую фирму загляните на rslaw.ru

   Очень многие документы создаются и хранятся в формате электронных таблиц Microsoft Excel. Несмотря на то, что эти таблицы обладают возможностями для автоматической обработки документа, нам, дельфистам, гораздо приятнее работать в привычной среде, что которая и обладает к тому же гораздо более развитыми возможностями. Давайте посмотрим, как получать данные из Excel. Естественно, табличные данные будем размещать в привычную нам таблицу StringGrid.

   Для работы с Excel и другими программами из пакета Microsoft Office необходимо добавить в список uses модуль ComObj:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ComObj;

   Далее, описываем глобальную переменную типа Variant:

var
  Form1: TForm1;
  Excel: Variant;

   Далее, нужно создать объект Excel. Excell Application создаётся пустым, без таблиц, поэтому необходимо добавить хотя бы одну книгу. Делать это нужно в каком-либо обработчике, например обработчике нажатия кнопки, хотя можно и сразу в OnCreate Формы:

Excel:=CreateOleObject('Excel.Application');
Excel.Application.WorkBooks.Add('Имя_Файла.xls');

Если создаётся пустая книга, метод Add применяется без параметра - без имени файла. Естественно, можно предложить пользователю выбрать файл:

with OpenDialog1 do
  if Execute then
    Excel.Application.WorkBooks.Add(FileName);

   Для отладки необходимо, чтобы таблица Excel была видимой, а также лучше запретить задавать вопросы о сохранении данных при закрытии:

Excel.Visible:=True; //После отладки можно закомментировать эту строку
Excel.DisplayAlerts:=False;

   Сразу создайте метод закрытия объекта Excel, иначе при отладке, да и при работе пользователя в компьютере наплодится столько невидимых процессов Excel, что мама дорогая!.. В обработчике OnCloseQuery Формы напишите:

try
  Excel.Quit;
except
end;
CanClose:=True;

   Естественно, будет произведён выход из Excel, и затем закроется всё приложение. Но если нам нужно после закрытия процесса Excel продолжить работу с программой, то этот код помещается в обработчик нажатия кнопки. Однако, в данном случае его недостаточно. Попробуйте, и вы убедитесь, взглянув в список процессов в Диспетчере Задач, что наш процесс Excel жив и здоров! Это произошло потому, что он остаётся связанным с переменной, его создавшей (Excel же). Для реального уничтожения процесса нужно разорвать эту связь. Дополните вышеприведённый код строкой:

  Excel:=Unassigned;

и при нажатии кнопки закрытия наш Excel исчезнет из списка процессов.

   Теперь нужно получить данные из Excel. В Excel столбцы именуются буквами, но мы в Delphi обращаемся к ним привычно, по порядковым номерам. Обратите внимание, что, поскольку в Delphi первым в индексе идёт индекс столбца, а в таблице Excel индекс строки, то индексы должны быть расположены на противоположных местах. В обработчике нажатия кнопки:

with StringGrid1 do
  for i:=1 to RowCount-1 do
  for j:=1 to ColCount-1 do
    Cells[j, i]:=Excel.WorkSheets.Item['Лист1'].Cells[i, j];

   Маленькое предупреждение: если при отладке проверять внесение данных, то перед нажатием нашей кнопки нужно завершить ввод в Excel - нажать Enter. Ведь если ячейка таблицы Excel останется в режиме редактирования, то мы получим отказ от Excel.
   И ещё. Данные в Excel адресуются начиная с 1. Попытка получить содержимое фиксированных ячеек не удаётся. Поэтому фиксированные ячейки в таблице StringGrid при необходимости нужно заполнять самому, отдельно.

   А получить содержимое одной ячейки можно как указав номер строки и столбца, так и непосредственно указав адрес ячейки:

var S1, S2: String;
begin
  S1:=Excel.WorkSheets.Item['Лист1'].Cells[5, 6];
  S2:=Excel.WorkSheets.Item['Лист1'].Range['F5'];
end;

   В переменных S1 и S2 будет одинаковое значение.

   Теперь в таблице StringGrid мы имеем данные для обработки, и делаем с ними что хотим. Затем можно перенести обработанные данные назад в таблицу Excel. Делается это совершенно аналогично, в обработчике нажатия другой кнопки:

for i:=1 to Grid.RowCount-1 do
for j:=1 to Grid.ColCount-1 do
  Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=Grid.Cells[j, i];

   Если эти операции производятся с активным листом Excel, то можно сократить написание, и вместо:

Excel.WorkSheets.Item['Лист1'].Cells[i, j]

   писать:

Excel.Cells[i, j]

   Или можно создать переменную и присвоить ей значение того листа Excel, с которым производится работа:

var Sheet: Variant;
   S1, S2: String;
begin
  Sheet:=Excel.WorkSheets.Item['Лист1'];
  S1:=Sheet.Cells[5, 6];
  S2:=Sheet.Range['F5'];
end;

   Только имейте в виду, что таблица может содержать не только данные непосредственно в ячейках, но и формулы. При записи данных из нашей таблицы StringGrid всё, кроме непосредственно записываемого текста, будет уничтожено!

   Напоследок нужно заставить таблицу Excel сохранить обработанные данные:

Excel.ActiveWorkbook.SaveAs('Имя_Файла');//Или SaveAs('OpenDialog1.FileName');

   Можно вывести отчёт на печеть. Вот как задана функция печати:

function PrintOut(
From: Variant;//Необязательно. Номер срааницы с которой начинается печать.
To: Variant;//Необязательно. Номер страницы по какую продолжается печать.
Copies: Variant;//Необязательно. Количество копий.
Preview: Variant;//Необязательно. Предварительный просмотр (True или False).
ActivePrinter: Variant;//Необязательно. Имя активного принтера.
PrintToFile: Variant;//Необязательно. При значении True печать будет идти в файл.
Collate: Variant//Необязательно. При значении True копии страниц объединяются.
                ): Workbook;

   Воспользоваться этой функцией можно как методом переменной, указывающей страницу - Sheet (также Excel.ActiveWorkBook или Excel.WorkSheets):

  Sheet.PrintOut(1, 1, 1, False, True);

   Будет произведён вывод на печать с первой страницы по первую, одной копии, без предварительного просмотра, без указания принтера - печать идёт в файл. Предварительно будет выдан запрос на указание имени файла. Создаётся файл типа *.xps. Для его просмотра нужны специальные программы.

   Естественно, в Delphi можно осуществлять также и форматирование ячеек, и другие операции с таблицей Excel. Эту информацию добавлю чуть позже. А пока на первый раз достаточно.

Работа с регионом ячеек Excel

   Продолжить хочу с того факта, что операции чтения и записи данных по одной ячейке занимают довольно много времени - вы уже и сами наверное заметили. Есть способ ускорить этот процесс. Для этого нужно освоить несложные операции работы с регионом ячеек Excel.

   Регион ячеек таблицы Excel также имеет тип Variant и задаётся прямоугольником, с указанием левой верхней и правой нижней ячеек:

var Range: Variant;
begin
  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]];
end;

   В частности, регион может состоять и из одной ячейки:

  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[1, 1]];

   Эту запись проще выполнить с указанием адреса как в таблице Excel:

Range:=Excel.Range['A1'];

   Также можно задать и прямоугольный регион, если вам известны имена ячеек. Вот регион 4х4:

Range:=Excel.Range['A1:D4'];

   А вот как выполнить перепись региона 100Х100 ячеек Excel в таблицу StringGrid:

var Range: Variant;
     i, j: Integer;
begin
  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]];
  with StringGrid1 do
    for i:=1 to 100 do
    for j:=1 to 100 do
      Cells[i, j]:=Range.Cells[j, i];
end;

   Вот и всё! На моём компьютере, эта операция переписи региона 100х100 ячеек Excel в таблицу StringGrid длится около 300 мсек, что на 2 порядка быстрее, чем чтение и запись по одной ячейке.

   А, например, операция занесения какого-либо одного значения во все ячейки региона выполняется ещё проще. Занесём в наш вышеопределённый регион 100х100 слово 'Привет':

  Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]]:='Привет';

Очистка региона выполняется методом Clear:

  Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]].Clear;


Обзор компонентов Delphi           В начало урока          Компонент StringGrid Delphi

Уроки Delphi начинающим



Вопросы и комментарии (47)      Решение задач в Delphi

гость, добавлено 28.03.12, 09:08:48 
Добрый день автор! Огромное спасибо за статью..!
пытаюсь сохранить данные SG в Excel что то у меня не правильно, подскажите пожалуйста!
var
Excel: variant;
  i,j:integer;

begin

 with SaveDialog1 do
  if Execute then
  Excel:=CreateOleObject('Excel.Application');
  Excel.Application.WorkBooks.Add;

  for i:=1 to frChild.sg.RowCount-1 do
  for j:=1 to frChild.sg.ColCount-1 do

 Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=frChild.sg.Cells[j, i];
 Excel.Visible:=True;
Автор, добавлено 28.03.12, 09:33:50 
А что неправильно-то? То, что у вас недописано из необходимого - не видно добавления в uses модуля ComObj. И где сохранение? Конструкция

 with SaveDialog1 do
  if Execute then
  Excel:=CreateOleObject('Excel.Application');

создаст объект Excel в случае если вы указали файл для сохранения. Но всё остальное будет выполнено, даже если не указали:

Excel.Application.WorkBooks.Add;// Ошибка - объект не создан

  for i:=1 to frChild.sg.RowCount-1 do
  for j:=1 to frChild.sg.ColCount-1 do

 Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=frChild.sg.Cells[j, i];//ошибка
 Excel.Visible:=True; //ошибка

Поэтому нужно после

with SaveDialog1 do
  if Execute then

все остальные операторы заключить в begin/end. И ComObj в uses добавьте.
гость, добавлено 28.03.12, 10:02:57 
var
  i,j:integer;
  begin
  with SaveDialog1 do
  if Execute then
  begin
  Excel:=CreateOleObject('Excel.Application');
  Excel.Application.WorkBooks.Add;

  for i:=1 to frChild.sg.RowCount-1 do
  for j:=1 to frChild.sg.ColCount-1 do

 Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=frChild.sg.Cells[j, i];
 Excel.Visible:=True;
  end;

В начале цикла программа останавливается. возможно ли без указания файла в диалоге создать ексел, указать какое нибудь имя ?
гость, добавлено 28.03.12, 10:11:10 
имею в виду "сохранить как" - указываю имя и "сохранить".
в цикле что то не правильно, не пойму.
гость, добавлено 28.03.12, 10:19:22 
frChild - PageControl - SG имеет значение нахождение таблицы?
Автор, добавлено 28.03.12, 10:32:09 
Нет, если при компиляции ошибок не было, значит таблица найдена и всё нормально. Что значит программа останавливается? Зависает?
гость, добавлено 28.03.12, 11:03:43 
for i:=1 to frChild.sg.RowCount-1 do
на ней встает и выводит ошибку!

Project VKOKAD.exe raised exception class EAccessViolation with message 'Access violation at address 004E69F9 in module
'VKOKAD.exe'. Read of address 00000488'. Process stopped. Use Step or Run to continue.
Автор, добавлено 28.03.12, 11:07:48 
Осмелюсь спросить, а сам Excel в компе есть? Также предлагаю запаковать весь ваш проект Rar'ом и выслать мне на почту: andrzejx(a)yandex.ru
гость, добавлено 28.03.12, 11:13:29 
вот так через кнопку в той же форме с готовым файлом Ексел все выгружается. а с родительского нет! ((((
begin
  try
  ExcelApp := CreateOleObject('Excel.Application');
  t:=SG.RowCount-1;
  ExcelApp.WorkBooks.Open(GetCurrentDir() + '\сохранить');
  Excelapp.DisplayAlerts := false;
  ExcelApp.Visible := true;
  Sheet := ExcelApp.ActiveWorkbook.Worksheets[1];

  for j := 1 to SG.ColCount - 1 do begin
  for i := 1 to SG.RowCount - 1 do begin

  Sheet.Cells[i + 0, j + 0] := SG.Cells[j, i];
  end;end;
  ExcelApp.WorkBooks[1].WorkSheets[1].Range['A1:'+'O'+IntToStr(t+1)].Borders.LineStyle:=1;
  ExcelApp.WorkBooks[1].WorkSheets[1].Range['A1:'+'O'+IntToStr(t+1)].Font.Size := 8;
  ExcelApp.Worksheets[1].Name := 'Акт приемки';
  ExcelApp.Worksheets[1].PageSetup.Orientation := 2;
  except
  ExcelApp := unassigned;
  Sheet:= Unassigned;
end;
гость, добавлено 28.03.12, 11:24:47 
готовый Ексел хочу загрузить в SG та же ошибка!
Автор, добавлено 28.03.12, 11:28:19 
Сделайте что-то простое, просто открыть Excel, и дальше смотрите.
гость, добавлено 28.03.12, 11:37:32 
Вы пожалуйста попробуйте с родительской формы сохранить или открыть SG которая находиться в дочерней форме..!
Автор, добавлено 28.03.12, 11:54:53 
А что тут такого???

Excel:=CreateOleObject('Excel.Application');
Excel.Application.WorkBooks.Add;
for i:=1 to Form2.Grid.RowCount-1 do
for j:=1 to Form2.Grid.ColCount-1 do
  Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=Form2.Grid.Cells[j, i];

гость, добавлено 28.03.12, 12:04:57 
С Ексел в SG код у меня правильный? кликаю ничего не происходит!
var
Excel: variant;
  i,j: integer;
begin

with OpenDialog1 do
  if Execute then
  begin try
  Excel:=CreateOleObject('Excel.Application');
  Excel.Application.WorkBooks.Add;
  //Excel.Visible:=True;

  for i:=1 to sg.RowCount-1 do
  for j:=1 to sg.ColCount-1 do
 sg.Cells[j, i]:=Excel.WorkSheets.Item['Лист1'].Cells[i, j];
  finally
  Excel.quit;
  end;
Автор, добавлено 28.03.12, 12:26:59 
А что такое

finally
  Excel.quit;
end;

Поясняю: это уничтожение Excel. Что вы после этого хотите увидеть?
гость, добавлено 28.03.12, 12:34:28 
убрал. ничего не меняется, я ничего не пропустил в коде.
Автор, добавлено 28.03.12, 12:47:56 
Всё работает, может нужно и раскомментировать Excel.Visible? Тогда и увидите...
гость, добавлено 28.03.12, 12:48:21 
уррраааа!! получилось.... все спасибо Вам автор за все!
гость, добавлено 28.03.12, 12:57:05 
последний вопрос на сегодня! оказывается проблема в том что у SG без строк и столбцов кроме фиксированного. они у меня добавляются с другой кнопки. я просто добавил строки и все загрузилось, а мне надо чтоб SG был пустым. теперь вопрос как отследить сколько строк в Екселе чтоб добавилось в SG и потом только загрузить данные?
Автор, добавлено 28.03.12, 13:05:25 
Если вам известна структура документа, то - сканированием, проверить, где кончаются данные, и начинаются пустые ячейки. Вряд ли вы будете работать с полностью неизвестным документом.

Больше ничего в голову не приходит. И совсем необязательно проверять каждую строку. Вполне можно проверять через 10, 50, или даже 1000 строк - смотря по предполагаемому объёму документа.
гость, добавлено 28.03.12, 14:00:21 
В обработчике OnClose Формы напишите:

Excel.Quit. как правильно написать? ошибку выдает и не закрывает!
Автор, добавлено 28.03.12, 14:10:56 
У вас уже было написано:

finally
  Excel.quit;
end;

Это было правильно, и всё закрывало. Сейчас ошибка по другой причине. Может, самого объекта нет - не был открыт, или по ещё какой. Например, наверное у вас Excel локальная переменная одной процедуры, а закрывать пытаетесь в другой. Excel должен быть глобальной переменной.
гость, добавлено 28.03.12, 14:20:33 
Автор так и не заработало с родительского сохранение SG, а с самой дочерней формы все работает..
Автор, добавлено 28.03.12, 14:26:49 
Нет разницы, где находится компонент, если к нему правильно обращаться, с указанием Формы. У меня работает.
гость, добавлено 28.03.12, 14:31:02 
да глобальная. Ексел обязательно должен при работе запускаться? а если программу пришлось закрывать ошибка будет?
Автор, добавлено 28.03.12, 14:55:49 
Сами всё увидите, будет или не будет ошибка. Но, как в статье и указано, нужно предусмотреть закрытие Excel при закрытии программы. И всё равно проверяйте периодически в Диспетчере Задач лишние процессы Excel.
гость, добавлено 28.03.12, 14:57:25 
а как сделать так что бы при нажатий кнопки Save Dialog открывается имя пишешь путь указал и сохраняешь. а то открывается Ексел потом ее нужно сохранить куда надо.?
Автор, добавлено 28.03.12, 15:31:18 
Адрес файла сохраняется в свойстве FileName диалогов. Вот и сохраняйте.
Галина, добавлено 16.05.12, 22:49:58 
Здравствуйте. пытаюсь данные из StringGrid в эксель отправить. сохраняются они как текст. как сделать формат данных числовым? (не строится диаграмма)
Спасибо
Автор, добавлено 17.05.12, 07:46:47 
Применение числового формата к одной ячейке с адресом (1, 1):

Excel.Cells[1, 1].NumberFormat := '0,00';


Применение числового формата к первому столбцу:

Excel.Columns[1].NumberFormat := '0,00';


Не смог понять, как применить формат к строке... Ну что ж, придётся определять формат каждой ячейки в строке, на необходимую длину, в цикле.
Асёк, добавлено 25.05.12, 18:04:37 
Извините пожалуйста. Но с opendialog у меня не работает((можт я что-то не так делаю. помогите пожалуйста! а все остально получилось.
Галина, добавлено 25.05.12, 18:37:17 
Мне нужно при помощи opendialog загрузить файл в Stringgrid:
uses comobj; - сделала
Excel: Variant; - сделал..на форму opendialog положила.
Stringgrid без фиксированных строк...
А вот обработчик события нажатия кнопки

procedure TForm3.Button2Click(Sender: TObject);
var i,j,n:integer;
begin
Excel:=CreateOleObject('Excel.Application');
with OpenDialog1 do
  if Execute then
  Excel.Application.WorkBooks.Add(FileName);
Excel.DisplayAlerts:=False;
with StringGrid1 do
  for i:=0 to 5 do
  for j:=0 to 2 do
  Cells[j, i]:=Excel.WorkSheets.Item['Лист1'].Cells[i+1, j+1];
end;

OnClose - тож сделала

Автор, добавлено 25.05.12, 18:41:47 
Давайте сделаем так. Вместо

 Cells[j, i]:=Excel.WorkSheets.Item['Лист1'].Cells[i+1, j+1];

напишите

Cells[j, i]:=Excel.Cells[i+1, j+1];
Асёк, добавлено 26.05.12, 22:20:22 
Огромное Вам спасибо!! все заработало!! Это просто замечательно...у вас действительно очень хороший сайт... все доступно и понятно..
Асёк, добавлено 7.06.12, 22:11:53 
Извините пожалуйста, что еще раз к вам обращаюсь, но у меня тут опять возникла проблема...
на форме 2 эдита - это степень вероятности и степень свободы..
также есть файл Excel - там в ячейке [1,3] - стоит формула расчета критерия стьюдента..я в программе это делаю так...
procedure TForm7.Button3Click(Sender: TObject);
var a,b,c:real;
begin

Excel:=CreateOleObject('Excel.Application');
 Excel.Application.WorkBooks.Add('C:\KR_ST.xls');
 Excel.Visible:=True;
 Excel.DisplayAlerts:=False;

 a:=strtofloat(Edit1.Text);
  b:=strtofloat(Edit2.Text);
 Excel.WorkSheets.Item['Лист1'].Cells[1, 1]:=a;
 Excel.WorkSheets.Item['Лист1'].Cells[1, 2]:=b;
 
 end;

В Excel все хорошо посчиталось.. и вот мне теперь из него на форму delphi надо записать полученное число..это можо сделать как-нибудь?
Автор, добавлено 8.06.12, 08:59:26 
var Range: Variant;
 x: Real;

begin
 Range:=Excel.Range['B5'];
 x:=Range.Cells[1, 1];
end;

Вместо B5 поставьте адрес вашей ячейки.
Асёк, добавлено 8.06.12, 11:24:38 
Спасибо большое...работает..
При чем я так делала.. ну немного по-другому... и не работало..а теперь...
осталось мне для моей курсовой сделать только чтоб открывался excel а там уже была кнопка поиска решения...кошмарики...
АНДРЕЙ, добавлено 6.08.12, 19:20:09 
Обязательно ли в данном случае на клиентском компьютере должен быть установлен Excel при работе с программой, которая сохраняет данным методом содержимое StrigGrid в файл .xls?
Автор, добавлено 6.08.12, 20:01:14 
Да, это совершенно необходимо. Ведь Delphi только управляет, а выполняет всё сам Excel.
АНДРЕЙ, добавлено 6.08.12, 20:05:47 
Спасибо за Ваш ответ. А если возможность обойтись без Excel?

И еще один вопрос в догонку. Мне нужно, чтобы процесс Excel существовал только во время сохранения. Т.е, чтобы после сохранения он уничтожался, потому что с ним работа идет только во время сохранения данных, после нажатия кнопки "сохранить". Как убить процесс, после завершения сохранения файла? Excel.Quit, я так понял, для чего-то другого.
Автор, добавлено 6.08.12, 21:13:51 
Нет, почему для другого? Эт выход из Excel, его закрытие. Откройте Диспетчер Задач и сами увидите, есть процесс Excel или нет.

Обойтись без Excel поможет компонент на вкладке ActiveX - F1Book. Он выглядит как открытая таблица Excel и имеет те же возможности, ну, может, несклько попроще.
АНДРЕЙ, добавлено 6.08.12, 23:28:38 
Я поставил Excel.Quit в самом конце процедуры. Это помогло от того, что не плодятся процессы Excel, но 1 процесс всегда остается. Исчезает лишь после закрытия программы. Сам объект создается тоже только в этой процедуре...
Автор, добавлено 7.08.12, 09:57:57 
Ваша правда. Чё-то не выходит закрыть созданный процесс не закрывая приложение. Пробую найти решение.
Автор, добавлено 7.08.12, 12:12:36 
Оказывается, нужно дополнительно "разименовывать" переменную - разрывать её связь с объектом.

Я пробовал

FreAndNil(Excel); - неправильно. Вот как нужно:


Excel:=Unassigned;
АНДРЕЙ, добавлено 8.08.12, 00:14:42 
Всё отлично сработало, спасибо! =) Кстати, можете Вашу статьи дополнить на этот счет. буду разбираться с F1Book. Ах, и еще - вы обещали добавить информации об форматировании ячеек Excel. Такая статья уже есть? =)
Мария, добавлено 12.05.13, 07:12:26 
Добрый день! Я считываю данные из файла Excel в StringGrid. Подскажите как сделать, чтобы числа записывались в StringGrid с двумя знаками после запятой. Потому что, если у меня в Excel значение 5,00, то оно передается в StringGrid как просто 5. А если некоторые значения в Excel вычисляются, то вместо 5,56 у меня записывается в StringGrid 5,56323233. Вообщем как сделать, что бы все цифры были с двумя знаками после запятой. Спасибо!
Автор, добавлено 12.05.13, 07:23:27 
Нужно данные обрабатывать! Анализируйте число, если меньше двух знаков - добавляйте, лишние удаляйте...
Оставить комментарий:

Имя  

Текст комментария