Консультация № 185699
29.03.2012, 12:54
139.83 руб.
0 19 1
Уважаемые эксперты! Пожалуйста, ответьте на вопрос: При пуске программы настройки берутся из реестра, в частности пути к файла, выводятся в Едиты.
1 вопрос: В Edt1 выводится путь к первому файлу, допустим "C:\Prog\март.xls" как мне из этого файла в Items Combobox вывести названия листов. И после выбора в комбобоксе скажем третьего листа, данные с третьего листа грузились в StrinGrid1?

2 вопрос: В Edit2 выводится путь ко второму файлу "C:\Prog\2011.xls". Из этого файла у меня должно выборочно по листам (приблизительно -лист1 "А4" , лист2 D6) братся данные в StrinGrid2?
Подскажите как лучше организовать. Заранее благодарю за советы.

Обсуждение

Неизвестный
29.03.2012, 14:45
общий
это ответ
Здравствуйте, Владимир!
Предлагаю Вам примерный вариант решения поставленной задачи.
На форме:
три кнопки Button1, Button2, Button3.
поле Edit1
две таблицы StringGrid1, StringGrid2
ComboBox1

тестовый файл 1.xls должен находиться в каталоге C:\Prog

по кнопке 1 - загружается список листов из книги
по кнопке 2 - выбираем данные из листа, указанного в ComboBox1 в StringGrid1
по кнопке 3 - выборка из первого и второго листов

Дополнительные вопросы готов обсудить в мини-форуме

Удачи

Приложение:
unit Unit1;

interface

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

type
TForm1 = class(TForm)
Edit1: TEdit;
ComboBox1: TComboBox;
Button1: TButton;
Button2: TButton;
StringGrid1: TStringGrid;
Button3: TButton;
StringGrid2: TStringGrid;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
Excel, WorkBook: variant;
i: integer;

begin
Edit1.Text:= 'C:\Prog\1.xls';
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
Workbook:=Excel.Workbooks.Open(Edit1.Text);
Combobox1.Clear;
for i:= 1 to Workbook.Sheets.Count do
Combobox1.Items.Add(Workbook.Sheets.Item[i].Name);

Combobox1.ItemIndex:= 0;

Workbook.Close;
Excel.Quit;
Excel:= UnAssigned;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
Excel, WorkBook, Sheet1: variant;
i: integer;

begin
Edit1.Text:= 'C:\Prog\1.xls';
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
Workbook:=Excel.Workbooks.Open(Edit1.Text);
Sheet1:= Workbook.Sheets[ComboBox1.Items[ComboBox1.ItemIndex]];
StringGrid1.RowCount:= 11;

for i:= 1 to 10 do
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['a' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['b' + IntToStr(i)];
end;


Workbook.Close;
Excel.Quit;
Excel:= UnAssigned;
end;


procedure TForm1.Button3Click(Sender: TObject);
var
Excel, WorkBook, Sheet1, Sheet2, Sheet3: variant;

begin
Edit1.Text:= 'C:\Prog\1.xls';
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
Workbook:=Excel.Workbooks.Open(Edit1.Text);
Sheet1:= Workbook.Sheets['Лист1'];
Sheet2:= Workbook.Sheets['Лист2'];
Sheet3:= Workbook.Sheets['Лист3'];

StringGrid2.RowCount:= 11;

StringGrid2.Cells[1,1]:= Sheet1.Range['d4'];
StringGrid2.Cells[1,2]:= Sheet2.Range['a4'];
StringGrid2.Cells[1,3]:= Sheet1.Range['c3'];
StringGrid2.Cells[1,4]:= Sheet2.Range['e5'];

Workbook.Close;
Excel.Quit;
Excel:= UnAssigned;
end;

end.



5
Несколько вариантов советов, есть возможность выбрать подходящий. И всегда полный и быстрый ответ.
давно
Посетитель
352040
133
29.03.2012, 16:03
общий
Благодарю за быстрый ответ. Практически то что нужно. Но как говорится есть пара нюансов. На основе Вашего кода сделал так
Код:
procedure TForm1.Dan;
var
Excel, WorkBook: variant;
i: integer;
begin
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
Workbook:=Excel.Workbooks.Open(Edit2.Text);
Combobox1.Clear;
for i:= 1 to Workbook.Sheets.Count do
Combobox1.Items.Add(Workbook.Sheets.Item[i].Name);

Combobox1.ItemIndex:= 0;

Workbook.Close(0);
Excel.Quit;
Excel:= UnAssigned;
end;


Добавил только Close(0), что бы Excel вопросов при закрытии не задавал.

Выбор в ComboBox сделал таким образом

Код:
procedure TForm1.ComboBox1Click(Sender:TObject);
var
Excel, WorkBook, Sheet1: variant;
i: integer;

begin
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
Workbook:=Excel.Workbooks.Open(Edit2.Text);
Sheet1:= Workbook.Sheets[ComboBox1.Items[ComboBox1.ItemIndex]];
StringGrid1.RowCount:= 6;

for i:= 1 to 5 do
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['a' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['b' + IntToStr(i)];

end;
end;


Все работает прекрасно, но остается в диспетчере запущенная копия Excel.
Пытаюсь таким образом закрыть Excel
Код:
   
for i:= 1 to 5 do
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['a' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['b' + IntToStr(i)];

Workbook.Close(0);
Excel.Quit;
Excel:= UnAssigned;
end;
end;

Получаю сообщение "OLE error 800A01A8."
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
29.03.2012, 16:36
общий
У Вас закрытие Excel происходит в цикле. Надо его вынести после окончания цикла:
...
for i:= 1 to 5 do
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['a' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['b' + IntToStr(i)];
end; // окончание цикла

Workbook.Close(0);
Excel.Quit;
Excel:= UnAssigned;
end;
давно
Посетитель
352040
133
29.03.2012, 16:45
общий
Сам виноват, не внимателен. Завтра напишу что получилось. Благодарю за помощь.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
29.03.2012, 16:46
общий
Адресаты:
Удачи в разработке.
давно
Посетитель
352040
133
29.03.2012, 19:48
общий
Опять вопрос. Загрузка данных идет таким образом
Код:
  

StringGrid1.RowCount:= 6;
for i := 1 to 5 do

begin
StringGrid1.Cells[1,i]:= Sheet1.Range['B' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i)];
StringGrid1.Cells[3,i]:= Sheet1.Range['D' + IntToStr(i)];
StringGrid1.Cells[4,i]:= Sheet1.Range['E' + IntToStr(i)];
StringGrid1.Cells[5,i]:= Sheet1.Range['F' + IntToStr(i)];
StringGrid1.Cells[6,i]:= Sheet1.Range['G' + IntToStr(i)];
StringGrid1.Cells[7,i]:= Sheet1.Range['H' + IntToStr(i)];
StringGrid1.Cells[8,i]:= Sheet1.Range['J' + IntToStr(i)];
StringGrid1.Cells[9,i]:= Sheet1.Range['K' + IntToStr(i)];

Не могу понять, как в цикле указать диапазон. У меня в таблице данные (первая таблица) 'В5-В9' , а тут получается что грузит с В1 по F1 в высоту. Всю таблицу не показываю, думаю и так понятно.
Подскажите как исправить?
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
30.03.2012, 00:32
общий
Кроме обращения к ячейкам Sheet1.Range['B' + IntToStr(i)] можно использовать конструкцию:
Sheet1.Cells.Range[номер строки,номер колонки];
Sheet1.Range['B3'] это Sheet1.Cells.Range[3,2];

тогда можно организовать циклы по номеру строки и номеру колонки
давно
Посетитель
352040
133
30.03.2012, 11:08
общий
Сделал как Вы посоветовали. Ругается "OLE error 800A03EC."
Процедура
Код:
procedure TForm1.ComboBox1Click(Sender:TObject);
var
Ex, WorkBook, Sheet1: variant;
i, j: integer;
begin
Ex:=CreateOleObject('Excel.Application');
Ex.DisplayAlerts:=False;
Workbook:=Ex.Workbooks.Open(Edit2.Text);
Sheet1:= Workbook.Sheets[ComboBox1.Items[ComboBox1.ItemIndex]];
StringGrid1.RowCount:= 6;

for i := 1 to 5 do

begin
StringGrid1.Cells[1,i]:= Sheet1.Cells.Range[3,5];
StringGrid1.Cells[2,i]:= Sheet1.Cells.Range[4,5];
StringGrid1.Cells[3,i]:= Sheet1.Cells.Range[5,5];
StringGrid1.Cells[4,i]:= Sheet1.Cells.Range[6,5];
StringGrid1.Cells[5,i]:= Sheet1.Cells.Range[7,5];
StringGrid1.Cells[6,i]:= Sheet1.Cells.Range[8,5];

end;
Workbook.Close(0);
Ex.Quit;
Ex:= UnAssigned;

end;

Видимо я не правильно понял Ваш совет.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
30.03.2012, 12:08
общий
Адресаты:
Это я немного недоработал (прошу прощения - давно не пользовал)
Правильное обращение:

StringGrid1.Cells[1,i]:= VarToStr(Sheet1.Cells[3,5]);
давно
Посетитель
352040
133
30.03.2012, 16:00
общий
30.03.2012, 16:20
Прошу прощения за молчание. На работе нахожусь.
Попробовал все варианты. Самый первый способ подходит лучше.
Код:
 
Sheet1:= Workbook.Sheets[ComboBox1.Items[ComboBox1.ItemIndex]];

StringGrid1.RowCount:= 36;

for i := 1 to 35 do

begin

StringGrid1.Cells[1,i]:= Sheet1.Range['B' + IntToStr(i)];
StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i)];
StringGrid1.Cells[3,i]:= Sheet1.Range['D' + IntToStr(i)];
StringGrid1.Cells[4,i]:= Sheet1.Range['E' + IntToStr(i)];
StringGrid1.Cells[5,i]:= Sheet1.Range['F' + IntToStr(i)];
StringGrid1.Cells[6,i]:= Sheet1.Range['G' + IntToStr(i)];
StringGrid1.Cells[7,i]:= Sheet1.Range['H' + IntToStr(i)];
StringGrid1.Cells[8,i]:= Sheet1.Range['I' + IntToStr(i)];
StringGrid1.Cells[9,i]:= Sheet1.Range['J' + IntToStr(i)];
StringGrid1.Cells[10,i]:= Sheet1.Range['K' + IntToStr(i)];

Не буду весь код вставлять, думаю понятно. Но опять проблема осталась, берет данные начиная с "В1", а нужно с "В5". Нужно указать что брать с 5 строки, и вопрос снимется.
P.S. Еще вопрос, в догонку. Как ограничить количество знаков после запятой в StringGrid. В Excel они ограниченны, а при загрузке вылазит куча знаков после запятой.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
30.03.2012, 16:55
общий
Берем данные, начиная с 5-й строки:
...
StringGrid1.Cells[1,i]:= Sheet1.Range['B' + IntToStr(i+4)];
...

Насчет количества знаков:
можно идти через текстовую переменную, к примеру, temp:
...
temp:= Sheet1.Range['B' + IntToStr(i+4)];
StringGrid1.Cells[1,i]:= Format('%10.2f', [StrToFloat(temp)]);
// форматируем до 2 знаков , всего 10 символов
....
если нормально, то можно попробовать объединить:
StringGrid1.Cells[1,i]:= Format('%10.2f', [StrToFloat(Sheet1.Range['B' + IntToStr(i+4)])]);

Что-то похожее применял.
давно
Посетитель
352040
133
30.03.2012, 19:25
общий
Благодарю за быстрый ответ.
......
Ваш код подставил :
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['B' + IntToStr(i+4)];
StringGrid1.Cells[2,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['C' + IntToStr(i+4)])]);
Работает.
.......
Но опять наткнулся на проблему. Если в загружаемом документе, ячейка пустая (допускается и такое), ругается " is not valid floating point value is not valid floating point value".
Если не применять проверку на знаки после запятой, то проходит. Получается что нужно добавлять проверку на пустую ячейку?
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
31.03.2012, 01:18
общий
Адресаты:
Конечно, нужно проверять пустые ячейки, если необходимо форматировать.
давно
Посетитель
352040
133
31.03.2012, 09:34
общий
А не подскажите как лучше это организовать? В смысле проверку.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
давно
Посетитель
352040
133
31.03.2012, 16:23
общий
31.03.2012, 19:00
Попробовал таким образом обмануть
.....
If Length (StringGrid1.Cells[2,i])> 2 Then
StringGrid1.Cells[2,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['C' + IntToStr(i+4)])])
else StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i+4)]; ];

.....
Не вышло. Не хочет работать.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
31.03.2012, 21:51
общий
Так проверять нужно ячейку Excel, а у Вас идет проверка таблицы StringGrid.

Думаю проверять нужно как-то так:

... IF Sheet1.Range['C' + IntToStr(i+4)] > "" then StringGrid1.Cells[2,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['C' + IntToStr(i+4)])])
else StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i+4)];


[b]Второй вариант [/b]- использовать обработчик ошибок:

try
StringGrid1.Cells[2,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['C' + IntToStr(i+4)])]);
except
StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i+4)];
end;
давно
Посетитель
352040
133
01.04.2012, 11:49
общий
Еще раз благодарю за быстрый ответ.
На основании Вашего кода сделал свой код. Применил обработку ошибок там где есть возможность появления данных с разделителем (запятой).
Время загрузки не изменилось, а ошибки исчезли.
Код.
Код:
procedure TForm1.ComboBox1Click(Sender:TObject);
var
Ex, WorkBook, Sheet1: variant;
i: integer;
d: TDateTime;
Timer: string;
begin
Ex:=CreateOleObject('Excel.Application');
Ex.DisplayAlerts:=False;
Workbook:=Ex.Workbooks.Open(Edit2.Text);
Sheet1:= Workbook.Sheets[ComboBox1.Items[ComboBox1.ItemIndex]];
d:=Now;
StringGrid1.RowCount:= 36;
for i := 1 to 35 do
begin
StringGrid1.Cells[1,i]:= Sheet1.Range['B' + IntToStr(i+4)];
try
StringGrid1.Cells[2,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['C' + IntToStr(i+4)])]);
except
StringGrid1.Cells[2,i]:= Sheet1.Range['C' + IntToStr(i+4)];
end;
StringGrid1.Cells[3,i]:= Sheet1.Range['D' + IntToStr(i+4)];
StringGrid1.Cells[4,i]:= Sheet1.Range['E' + IntToStr(i+4)];
StringGrid1.Cells[5,i]:= Sheet1.Range['F' + IntToStr(i+4)];
try
StringGrid1.Cells[6,i]:= Format('%10.1f', [StrToFloat(Sheet1.Range['G' + IntToStr(i+4)])]);
except
StringGrid1.Cells[6,i]:= Sheet1.Range['G' + IntToStr(i+4)];
end;
try
StringGrid1.Cells[20,i]:= Format('%10.2f', [StrToFloat(Sheet1.Range['V' + IntToStr(i+4)])]);
except
StringGrid1.Cells[20,i]:= Sheet1.Range['V' + IntToStr(i+4)];
end;
StringGrid1.Cells[21,i]:= Sheet1.Range['W' + IntToStr(i+4)];
StringGrid1.Cells[22,i]:= Sheet1.Range['X' + IntToStr(i+4)];
StringGrid1.Cells[23,i]:= Sheet1.Range['Y' + IntToStr(i+4)];
StringGrid1.Cells[24,i]:= Sheet1.Range['AA' + IntToStr(i+4)];
StringGrid1.Cells[25,i]:= Sheet1.Range['AB' + IntToStr(i+4)];
StringGrid1.Cells[26,i]:= Sheet1.Range['AC' + IntToStr(i+4)];

end;
Workbook.Close(0);
Ex.Quit;
Ex:= UnAssigned;
Timer :='Время загрузки: '+FormatDateTime('hh:mm:ss:zzz',Now()-d);
StatusBar1.Panels[0].Text :=Timer;
end;

Вот так у меня получилось. Думаю вопрос можно закрывать.
Еще раз большое спасибо за помощь.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Неизвестный
01.04.2012, 13:23
общий
Адресаты:
Удачи в дальнейших разработках.
давно
Посетитель
352040
133
01.04.2012, 13:30
общий
Благодарю за помощь еще раз. Будут у меня еще вопросы по StringGrid, но с ними чуть попозже.
Об авторе:
Пользуюсь Delphi Enterprise Version7.
Форма ответа