Недавно снова пришлось немного поработать с Excel в Delphi. Суть работы состояла в следующем. Есть документ Excel, состоящий минимум из четырех листов (Sheets), на каждом листе содержатся таблицы практически произвольного (с точки зрения форматирования) состава, в таблицах записаны различные данные. Сам файл может достигать нескольких Mb в размере — реально дофига чисел, текста и т.д. И всю это книгу Excel проверяет простенькая программка на Delphi — определяет возможные опечатки, несоответствия данных, суммирует данные и т.д. Проверка проводится периодически, причем не всегда на одном рабочем месте — иногда один человек проверяет один лист, второй — другой лист и т.д. И вот возник вопрос: как до начала проверки узнать был ли проверен лист и были ли обнаружены ошибки ранее?
Я решил не писать ничего на листах, никакого скрытого текста, примечаний и т.д., а воспользоваться коллекцией дополнительных свойств документа Excel.
План статьи:
- Свойства рабочей книги (WorkBook)
- Пример чтения свойств документа
- Запись нового свойства книги Excel
Применительно конкретно к моей ситуации использование коллекции свойств документа Excel для хранения данных было предпочтительнее всех остальных по одной причине — далеко не каждый пользователь заглядывает в эти свойства и что-то там меняет, в отличие от основного содержимого документа. Было дело, когда особо «продвинутый» пользователь сносил к чертям собачьим все закладки из документа Word’а, а потом недоумевал «чё-то у меня ничё не печатает» :). Поэтому в последнее время при работе с продуктами MS Office, да и вообще любыми шаблонами, стараюсь «заныкать» служебную информацию так, чтоб никто её вообще не видел.
Но это, небольшой отступление. Перейдем непосредственно к свойствам документа.
Свойства рабочей книги (WorkBook)
У объекта WorkBook определено два свойства, отвечающие за доступ к информации о документе:
- ContentTypeProperties — возвращает коллекцию MetaProperties, каждый из элементов которой представляет собой мета-информацию по документу Excel. Свойство доступно только для чтения.
- CustomDocumentProperties — возвращает коллекцию DocumentProperties, каждый из элементов которой несет определенную дополнительную информацию по документу. Это те самый свойства, которые Вы можете просмотреть щелкнув правой кнопкой мыши на файле и выбрав «Свойства —> Особые». Свойство CustomDocumentProperties доступно как для чтения, так для записи.
Соответственно, второй коллекцией мы и научимся сегодня пользоваться.
У коллекции DocumentProperties определены следующие свойства:
Application — возвращает объект Application, в котором отрыт документ. Только для чтения.
Count — количество элементов в коллекции
Creator — содержит число (integer), представляющее собой идентификатор создателя свойства. Только для чтения.
Item — объект DocumentProperty, содержащий данные по свойству.
Parent — возвращает родительский элемент для коллекции DocumentProperties — объект рабочей книги WorkBook.
Также у коллекции определен всего один метод:
Add — добавление нового элемента в коллекцию.
Метод Add имеет следующий синтаксис:
Add(Name, LinkToContent, Type, Value, LinkSource)
где
Name: string — обязательный параметр, определяющий имя нового свойства.
LinkToContent: boolean — обязательный параметр, указывает, связано ли содержимое свойство с содержимым документа. Если равен True, то необходимо передавать параметр LinkSource
Type: enumerator — тип свойства. Может принимать одно из следующих значений: msoPropertyBoolean, msoPropertyDate, msoPropertyFloat, msoPropertyNumber, или msoPropertyString
Value: variant — значение свойства.
LinkSource: variant — определяет какие типы истоника данный будут использоваться.
В принципе, всё, что необходимо знать для того, чтобы записать новое свойство документа и прочитать уже записанные нам известно. Осталось только продемонстрировать это на практике.
Создаем новый проект Delphi, на главной форме приложения размещаем Edit’ы, 2 кнопки Button, ListBox и несколько Label’ов как показано на рисунке.
Теперь приступим к реализации кода. Чтобы не забивать пост уже известными процедурами проверки установлен ли Excel,запуска приложения, открытия рабочей книги и т.д., советую ещё раз просмотреть пост «Работа с Excel в Delphi. Основы основ.«. Перейдем сразу к чтению свойств документа.
Пример чтения свойств документа
Листинг «Чтение свойств документа»:
uses ... ComObj...; const // свойства документа msoPropertyTypeBoolean = 2; // Boolean value. msoPropertyTypeDate = 3; // Date value. msoPropertyTypeFloat = 5; // Floating point value. msoPropertyTypeNumber = 1; // Integer value. msoPropertyTypeString = 4; // String value. StampProp = 'Проверено'; //вспомогательные классы type TFileProperty = class private FItemIndex: integer; FCreator: integer; FName: string; FPropertyType: integer; FValue: variant; procedure SetCreator(const Value: integer); procedure SetItemIndex(const Value: integer); procedure SetName(const Value: string); procedure SetPropertyType(const Value: integer); procedure SetValue(const Value: variant); public property ItemIndex: integer read FItemIndex write SetItemIndex; property Creator: integer read FCreator write SetCreator; property Name: string read FName write SetName; property PropertyType: integer read FPropertyType write SetPropertyType; property Value: variant read FValue write SetValue; end; type TExcelFile = class private FFileName: string; // имя файла FExcelObject: OleVariant; // объект Excel FListsNames: TStringList;//имена всех листов книги FCustomProperties: array of TFileProperty; procedure SetExcelObject(const Value: OleVariant); procedure SetFileName(const Value: string); function GetListCount: integer; function GetPropertyList:TStringList; public constructor Create(const aFileName: string); destructor Destroy; procedure GetFileInfo; procedure ReadProperties; function LastCheck():TDate; procedure AddProperty(const aName: string; aType: byte; aValue: variant; Rewrite:boolean=true); function PropertyExist(const aName:string):integer; property FileName: string read FFileName write SetFileName;// имя файла с инвентаризацией property ExcelObject: OleVariant read FExcelObject write SetExcelObject;// объект Excel property ListsNames: TStringList read FListsNames; property ListCount: integer read GetListCount; property PropertyList: TStringList read GetPropertyList; end; [...] procedure TExcelFile.ReadProperties; var PropVar: OleVariant; i: integer; begin FCustomProperties:=nil; for i := 1 to FExcelObject.ActiveWorkBook.CustomDocumentProperties.Count do begin SetLength(FCustomProperties, length(FCustomProperties) + 1); FCustomProperties[length(FCustomProperties) - 1] := TFileProperty.Create; PropVar := FExcelObject.ActiveWorkBook.CustomDocumentProperties.Item[i]; FCustomProperties[length(FCustomProperties) - 1].FItemIndex := i; FCustomProperties[length(FCustomProperties) - 1].FCreator := PropVar.Creator; FCustomProperties[length(FCustomProperties) - 1].FName := PropVar.Name; FCustomProperties[length(FCustomProperties) - 1].FPropertyType := PropVar. Type ; FCustomProperties[length(FCustomProperties) - 1].FValue := PropVar.Value; end; end; function TExcelFile.GetPropertyList: TStringList; var i:integer; begin Result:=TStringList.Create; for I := 0 to Length(FCustomProperties) - 1 do Result.Add(FCustomProperties[i].FName+' = '+VarToStr(FCustomProperties[i].FValue)); end; procedure TForm11.Button1Click(Sender: TObject); var i:integer; begin if OpenDialog1.Execute then begin ExcelFile:=TExcelFile.Create(OpenDialog1.FileName); ExcelFile.ReadProperties; ListBox1.Items.Clear; ListBox1.Items.Assign(ExcelFile.PropertyList); end; end;
Полный исходник приложения вы можете скачать по ссылке в конце поста. В представленном листинге после запуска Excel и открытия файла считывается вся коллекця дополнительных свойств документа (метод ReadProperties) и выводится в список ListBox в формате «Имя = Значение». С чтением свойств покончено, теперь попробуем записать новое свойство книги WorkBook.
Запись нового свойства книги Excel
При записи нового свойства следует предварительно проверить существование свойства в коллекции, чтобы избежать исключительной ситуации. Чтобы не обращаться лишний раз к коллекции DocumentProperties, будем проверять свойство в массиве FCustomProperties класса TExcelFile.
Листинг функции проверки:
function TExcelFile.PropertyExist(const aName: string): integer; var i:integer; begin Result:=-1; for I := 0 to Length(FCustomProperties) - 1 do begin if LowerCase(aName)=LowerCase(FCustomProperties[i].FName) then begin Result:=i; break; end; end; end;
Если свойство с именем aName найдено в массиве, то возвращается индекс элемента, иначе -1. Теперь можно добавить новое свойство в коллекцию:
Листинг добавления нового свойства для WorkBook:
procedure TExcelFile.AddProperty(const aName: string; aType: byte; aValue: variant; Rewrite:boolean=true); var index:integer; begin try index:=PropertyExist(aName); if (index>-1) and Rewrite then FExcelObject.ActiveWorkBook.CustomDocumentProperties.Item[aName].Delete else if (index>-1) and (not Rewrite) then Exit; case aType of msoPropertyTypeBoolean,msoPropertyTypeFloat:FExcelObject.ActiveWorkBook.CustomDocumentProperties.Add (Name:=aName, LinkToContent:=false,Type:=aType, Value:=aValue); msoPropertyTypeDate:FExcelObject.ActiveWorkBook.CustomDocumentProperties.Add (Name := aName, LinkToContent := false, Type := aType, Value := VarToDateTime(aValue)); msoPropertyTypeNumber:begin index:=aValue; FExcelObject.ActiveWorkBook.CustomDocumentProperties.Add (Name := aName, LinkToContent := false, Type := aType, Value:=index); end; msoPropertyTypeString:FExcelObject.ActiveWorkBook.CustomDocumentProperties.Add (Name := aName, LinkToContent := false, Type := aType, Value := VarToStr(aValue)); end; ReadProperties; except MessageBox(0,PChar('Свойство '+aName+' уже записано'),'Ошибка',MB_OK+MB_ICONERROR) end; end;
Здесь вначале проверяется существование свойства в массиве. Если свойство найдено и Rewrite=true, то оно удаляется из коллекции и записывается по новой:
FExcelObject.ActiveWorkBook.CustomDocumentProperties.Add (Name := aName, LinkToContent := false, Type := aType, Value := aValue);
Применительно к нашей программе добавление нового свойства может выглядеть, например так:
procedure TForm11.Button2Click(Sender: TObject); begin ExcelFile.AddProperty(Edit2.Text,ComboBox1.ItemIndex+1,Edit3.Text); ListBox1.Items.Assign(ExcelFile.PropertyList); end;
Также следует отметить, что при добавлении нового свойства в коллекцию параметр Value метода Add следует всегда приводить к типу, заданному в Type, иначе, например, при добавлении строки вы получите исключение «Неверное значение параметра».
Книжная полка
Описание Описаны общие подходы к программированию приложений MS Office. Даны программные методы реализации функций MS Excel, MS Word, MS Access и MS Outlook в среде Delphi.
|
Купить на ЛитРес |