
Эксперт
   
Профиль
Группа: Экс. модератор
Сообщений: 4147
Регистрация: 25.3.2002
Где: Москва
Репутация: 1 Всего: 162
|
В секции uses стоит так ExcelXP,{Excel2000, Excel97} крайней мере у меня, т.к. некоторые параметры при работе с разными версиями отличаются, например при открытии файла в версии XP больше параметров, чем в версии `97. На форме лежит компонента Ex1 типа TExcelApplication со страницы Servers, свойства AutoConnect и AutoQuit :=False, свойство ConnectKind:=ckRunningOrNew, Код | //объявления переменных var WorkBk : _WorkBook; // определяем WorkBook WorkSheet : _WorkSheet; // определяем WorkSheet Range:OleVariant;//
begin ... Ex1.Connect;//открываем сам Excel //открываем существующий файл, в разных версиях разное кол-во параметров Ex1.Workbooks.Open(FileName,EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam, LOCALE_USER_DEFAULT); //параметры для версии XP - имя файла, пути на ссылки в файле(UpdateLinks),Откывать ли //только для чтения, Формат, Пароль, WriteResPassword - ?, Игнорировать ли рекомендации //только для чтения, Origin - ?, Delimiter - символ-разделитель целой и дробной частей, Editable-? Ex1.Application.EnableEvents := false;//отключаем реакцию Excel`я на события
//так ищем название рабочей книги, т.к. Excel может уже быть открыт с другой книгой //поиск начинаем с единицы For i:=1 to ex1.Workbooks.Count do begin if ex1.Workbooks.Item[i].Name='MyFile.xls' then break; end; // Выбираем WorkBook WorkBk := ex1.WorkBooks.Item[iInd];
// Определяем WorkSheet //если кол-во листов больше 1, иначе нет смысла искать //memoSheets - TMemo - с ключевыми фразами, напрмер, //Лист 1, Мой лист, Данные, if WorkBk.Worksheets.Count>1 then begin
For x:=0 to memoSheets.Lines.Count-1 do begin For q:=1 to WorkBk.Worksheets.Count do begin WorkSheet:=WorkBk.WorkSheets.Get_Item(q) as _WorkSheet; if WorkSheet.Name = memoSheets.Lines[x] then begin //нашли лист bNaydeno:=True; WorkSheet.Activate(LOCALE_USER_DEFAULT);//активируем лист end; if bNaydeno= true then break; end; if bNaydeno=true then break; end;//for end else//if WorkSheet:=WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;;
------------------ //Поиск последней строки... //можно заранее ставить в файле какую-нибудь метку, напрмер 99999 //функция Find будет показана ниже iNameRow,iLastRow: Integer sNameCol: String if Find('99999',iNameRow,sNameCol, WorkSheet) then begin iLastRow:=iNameRow-1;//в столбце с наименованием ищем "99999"-конец импорта end else begin //и запоминаем в iRows try//если не находим 99999 то ищем последнюю незаполненную ячейку WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate; // Получаем значение последней строки iLastRow:=(ex1.ActiveCell.Row)-1; except try WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Select; // Получаем значение последней строки iLastRow:=(ex1.ActiveCell.Row); except iLastRow:=0; end;//try-except end;//try-except end;
|
memoNames - TMemo с наименованиями столбцов, т.к. в моем проекте у разных поставщиков Код | //могут столбцы называться по-разному, например, Товар, Наименование, Наименование товара //ищем наименование For r:=0 to memoNames.Lines.Count-1 do begin bNaydeno:=False; if Find(memoNames.Lines[r],iNameRow,sNameCol,WorkSheet) then begin ... break; end;//if Find(memoNames.Lines[r],iNameRow,sNameCol) then begin end;//For r:=0 to memoNames.Lines.Count-1 do begin //так ищем все нужные столбцы, запоминая столбец в каждую отдельную переменную
//начинаем импорт со строки iNameRow
//в конце могут начаться пустые строки, если неправльно //определилась последняя незаполненная ячейка //тогда нужно прервать цикл импорта if (WorkSheet.Cells.Item[iNameRow,sNameCol].Value='') or (WorkSheet.Cells.Item[iNameRow,sNameCol].Value=' ') then Inc(iStop)//если пустая строка, то увеличиваем на 1 else iStop:=0;//если следующая не пустая то обнуляем и продолжаем импорт //если начались пустые строки то прекращаем импорт if iStop>4 then break; //sg1-TStringGrid //наименование препарата sg1.Cells[1,e]:=WorkSheet.Cells.Item[iNameRow,sNameCol].Value; //цена sg1.Cells[2,e]:=WorkSheet.Cells.Item[iNameRow,sPriceCol].Value; //DelProb функция удаления всего лишнего, кроме цифр, точек и запятых //навыходе получаем вместо "2 305 585,85" "2305585,85" sg1.Cells[2,e]:=DelProb(sg1.Cells[2,e]);//удаляем пробелы //производитель sg1.Cells[3,e]:=WorkSheet.Cells.Item[iNameRow,sProdCol].Value; Inc(iNameRow); //добавляем строки к StringGrid только если они нужны if sg1.RowCount=e then sg1.RowCount:=sg1.RowCount+1;
//прерываем по желанию пользователя if fmMain.vAbort=False then begin//прерываем операцию ... try Ex1.Workbooks.Close(LOCALE_USER_DEFAULT); Ex1.Disconnect; Ex1.Quit; except end;//try-except
Exit; end;//if fmMain.vAbort=False then begin
//так закрываем Excel try Ex1.Quit; Ex1.Disconnect; except ShowMessage('Ошибка закрытия Excel'); //обнуляем переменную Range VarClear(Range);
|
sText - текст для поиска iRow - строка, в которой найдено значение sCol - колонка, в которой найдено значение UsedRange.Find - параметры для поиска, типа What:=sText, ищем в справке по Excel`ю Код | Function TfmImpExcel.Find(sText:String;Var iRow:Integer;Var sCol:String;WorkSheetF:_WorkSheet):Bool; Var UsedRange, Range: OLEVariant; t,y:Integer;//вспомогат для импорта FirstAddress: string; begin //поиск начали Result:=False; UsedRange := WorkSheetF.Range['A1','Z5000'];//диапазон поиска, напрмер от 'F25' до 'G30' Range := UsedRange.Find(What:=sText, LookIn := xlValues, LookAt := xlWhole,SearchDirection := xlNext); if not VarIsClear(Range) then begin try FirstAddress := Range.Address; //вычисляем номер строки из полученного адреса(абсолютные координаты) //он начинается после второго значка доллара //формат найденной строки,что-то типа $A$2 (абсолютные координаты) t:=PosEx('$',FirstAddress,2); iRow:=StrToInt(Copy(FirstAddress,t+1,length(FirstAddress)-t)); //вычисляем номер столбца из полученного адреса(абсолютные координаты) //буква начинается со второго символа y:=PosEx('$',FirstAddress,2); sCol:=Copy(FirstAddress,2,y-2); Result:=true; VarClear(Range); VarClear(UsedRange); except Result:=False; end;//try-except end;//if end;
|
Еще несколько примеров, используя Ole Excel:Variant - глобальная переменная Код | ... begin //вначале проверяем, не открыт ли Excel и закрываем if not VarIsEmpty(Excel) then begin Excel.Quit; Excel := Unassigned; end;//if
Try//открываем Excel и создаем раб.книгу Excel:=CreateOleObject('Excel.Application'); /кол-во листов в новой книге Excel.SheetsInNewWorkbook:=1;// //добавляем раб.книгу Excel.WorkBooks.Add; //в переменную "загоняем" текущий лист Sheets:=Excel.Workbooks[1].Sheets[1]; Except SysUtils.beep; ShowMessage('Не могу открыть Excel!'); Exit; end;//try-except
//рисуем border //сначала определяем диапазон Range:=Sheets.Range['B1']; Range.Borders[4].LineStyle := 1;//Range.Borders[4] - можно ставить от 1 до 8 - точно не мпомню
//рисуем border вокруг ячейки (обрамление) Range.Borders[1].LineStyle := 1; Range.Borders[2].LineStyle := 1; Range.Borders[3].LineStyle := 1; Range.Borders[4].LineStyle := 1;
//присваиваем значение яцейке Sheets.Cells[2,2]:=Edit1.Text;// формат Sheets.Cells[№ строки,№ колонки] //так выполняем выравнивание в диапазоне //присваиваем диапазону координаты ячейки Range:=Sheets.Cells[2,2];//можно переменные Range:=Sheets.Cells[iRow,iCol]; Range.HorizontalAlignment := xlCenter; Range.VerticalAlignment := xlCenter; //форматируем шрифт Sheets.Cells[iRow,3]:='ЗАЯВКА'; Range:=Sheets.Cells[iRow,3]; Range.Font.Bold:=True;
//с присваиванием значения ячейке могут быть проблемы, т.к. Excel думает, что он очень умный //и вместо числа может переформатировать в дату вида 12дек2004, что бы такого не случилось, //можно заранее отформатировать ячейку в нужный формат (дата, число, валюта, текстовый) //все форматы можно узнать в Excel`е, с пом. макросов, просмотрев затем код, созданный самим //Excel`ем //#,##0.000$ - денежный //[$-FC19]dd mmmm yyyy г/;@ - дата //h:mm;@ - время //0.00% - проценты //# ??/?? - простые дроби 21/25 //[<=9999999]###-####;(###) ###-#### - номер телефона //@ - текстовый формат, если указывать такой формат и присваивать //числовое значение, а затем складывать, то ничего не выйдет
//передаваемая строки из Delphi может отличаться, нужно эксперементировать
|
tZay - TTable dbGridZay - DBGrid vRow - integer Код | while not tZay.Eof do begin For iColCount:=0 to dbGridZay.Columns.Count-1 do begin Range:=Sheets.Cells[vRow,iColCount+1]; Case tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).DataType of ftFloat : begin Range.NumberFormat := '0,000'; Sheets.Cells[vRow,iColCount+1]:= tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsFloat end; ftString : begin Range.NumberFormat := '@'; Sheets.Cells[vRow,iColCount+1]:= tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsString; end; ftInteger : begin Range.NumberFormat := '0'; Sheets.Cells[vRow,iColCount+1]:= tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsInteger; end; ftAutoinc : begin Range.NumberFormat := '0'; Sheets.Cells[vRow,iColCount+1]:= tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsInteger; end;
ftDate : begin Range.NumberFormat := '@'; dDate:=tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsDateTime; Sheets.Cells[vRow,iColCount+1]:=FormatDateTime('dd.mm.yyyy',dDate); end else Range.NumberFormat := '@'; Sheets.Cells[vRow,iColCount+1]:= tZay.FieldByName(dbGridZay.Columns[iColCount].FieldName).AsString; end;//case-else
|
удаляем лишние столбцы (по умолчанию со сдвигом влево) dbGridZay - DBGrid Код | For iColCount:= dbGridZay.Columns.Count-1 downto 0 do begin if dbGridZay.Columns[iColCount].Visible=False then begin UsedRange := Sheets.Range['A1','Z100'];//диапазон поиска заголовка Range := UsedRange.Find(What:=dbGridZay.Columns[iColCount].title.Caption, LookIn := xlValues, LookAt := xlWhole,SearchDirection := xlNext); if not VarIsEmpty(Range) then begin try FirstAddress := Range.Address; s:=StringReplace(FirstAddress,'$','',[rfReplaceAll]); [b]Range:=Sheets.Range[s+':'+Copy(s,1,1)+IntToStr(vRow)];[/b] [b]Range.Delete;[/b] except
end;//try end;//if not VarIsEmpty(Range)then begin end;//if dbGridZay.Columns[iColCount].Visible=False then begin end;//for delete
//Объединение ячеек Sheet.Range[...].Merge(Across)
|
Относительно LOCALE_USER_DEFAULT Теоретически, в MSDN написано: "Indicates that the parameter is a locale ID (LCID)". Одни (Чарльз Калверт) предлагают в качестве его использовать 0, как идентификатор языка по умолчанию, другие - результат функции GetUserDefaultLCID. В некоторых случаях, чаще в связке Windows 2000 + Excel 2000, оба решения не проходят. Причем, выдается сообщение о попытке "использовать библиотеку старого формата..." Поэтому, рекомендуем в качестве lcid использовать значение константы LOCALE_USER_DEFAULT. Относительно открытия существующих рабочих книг Вот как описан метод Open в импортированной библиотеке типов: Код | function Open(const Filename: WideString; UpdateLinks: OleVariant; ReadOnly: OleVariant; Format: OleVariant; Password: OleVariant; WriteResPassword: OleVariant; IgnoreReadOnlyRecommended: OleVariant; Origin: OleVariant; Delimiter: OleVariant; Editable: OleVariant; Notify: OleVariant; Converter: OleVariant; AddToMru: OleVariant; lcid: Integer): Workbook; safecall;
|
Что вам из всего этого может понадобиться: · FileName Имя открываемого файла, желательно с полным путем, иначе Excel будет искать этот файл в каталоге по умолчанию; · AddToMru True - если необходимо запомнить файл в списке последних открытых файлов; · IgnoreReadOnlyRecommended Если файл рекомендован только для чтения, то при открытии Excel выдает соответствующее предупреждение. Чтобы его игнорировать, передайте в качестве данного параметра True. Используя позднее связывание При позднем связывании не нужно указывать все дополнительные параметры или LCID, можно просто написать вот так: Код | var Workbook: OLEVariant; ... Workbook := Excel.WorkBooks.Open('C:\Test.xls');
|
Примечание: Если вы хотите получше узнать метод Open, например, как с его помощью открывать файлы текстовых форматов с разделителями, воспользуйтесь "пишущим" плеером VBA. Запишите макросы, а затем поправьте их по необходимости. Создание новой книги Используя раннее связывание Код | var IWorkbook: Excel8_TLB._Workbook; ... IWorkbook := IExcel.Workbooks.Add(EmptyParam, xlLCID);
|
Передача в качестве первого параметра EmptyParam означает, что будет создана новая книга с количеством пустых листов, выставленным по умолчанию. Если в первом параметре вы передадите имя файла (с полным путем, иначе поиск осуществляется в каталоге по умолчанию), этот файл будет использован как шаблон для новой книги. Вы можете также передать одну из следующих констант: xlWBATChart, xlWBATExcel4IntlMacroSheet, xlWBATExcel4MacroSheet, или xlWBATWorksheet. В результате будет создана новая книга с единственным листом указанного типа. Внимание - важно! Excel не может держать открытыми несколько книг с одинаковыми названиями, даже если они лежат в разных каталогах, поэтому при создании файла по шаблону добавляет к имени файла новой книги номер (шаблон "test.xls" - новый файл "test1.xls"). Закрытие книги Используя раннее связывание Код | var SaveChanges: boolean; ... SaveChanges := True; IWorkbook.Close(SaveChanges, EmptyParam, EmptyParam, xlLCID);
|
Если в качестве параметра SaveChanges вы передадите EmptyParam, Excel задаст вопрос, сохранять ли рабочую книгу. Второй параметр позволяет вам определить имя файла, а третий указывает, нужно ли отправлять книгу следующему получателю. Используя позднее связывание При позднем связывании нет необходимости указывать дополнительные параметры, поэтому вы можете просто написать: Код | Workbook.Close(SaveChanges := True);
|
или Как передать абсолютный адрес ячейки? Нужно использовать символ $ - Лист1!$A$1:$D$3' Так можно добавить новый модуль: Код | var IModule: VBIDE8_TLB.VBComponent; //с эти нужно поэксперементировать ... IModule := IWorkbook.VBProject.VBComponents.Add( TOLEEnum(VBIDE8_TLB.vbext_ct_StdModule) ); IModule.Name :='MyModule1';
|
,поместить в него новую процедуру VBA: Код | IModule.CodeModule.AddFromString('PUBLIC SUB MySub1()'#13'Msgbox "Hello, World!"'#13'End sub'#13);
|
и запустить эту процедуру Код | OLEVariant(Excel).Run('MyModule1.MySub1');
|
Различные способы обращения к ячейкам Код | Var Value:Variant; ... try //различные способы Value := ISheet.Cells.Item[2, 1].Value; Value := ISheet.Range['A2', EmptyParam].Value; Value := ISheet.Range['TestCell', EmptyParam].Value; Value := IWorkbook.Names.Item('TestCell', EmptyParam, EmptyParam).RefersToRange.Value; finally ISheet := nil; end;
|
Копирование данных в буфер обмена Код | var ISheetSrc, ISheetDst: Worksheet;//в разных версиях IRangeSrc, IRangeDst: Range; //могут объявляться по разному ... IRangeSrc.Copy(IRangeDst);
|
Метод Copy интерфейса Range принимает в качестве параметра любой другой Range, совпадение размеров источника и получателя необязательно. При копировании области убедитесь, что не редактируете ячейку, иначе возникнет исключение "Call was rejected by callee". Использование метода Copy без указания параметра destination скопирует ячейки в буфер обмена.
--------------------
Написать можно все - главное четко представлять, что ты хочешь получить в конце.
|