Недавно снова пришлось немного поработать с Excel в Delphi. Суть работы состояла в следующем. Есть документ Excel, состоящий минимум из четырех листов (Sheets), на каждом листе содержатся таблицы практически произволоного (с точки зрения форматирования) состава, в таблицах записаны раличные данные. Сам файл может достигать нескольких Mb в размере - реально дофига чисел, текста и т.д. И всю это книгу Excel проверяет простенькая программка на Delphi - определяет возможные опечатки, несоответствия данных, суммирует данные и т.д. Проверка проводится периодически, причем не всегда на одном рабочем месте - иногда один человек проверяет один лист, второй - другой лист и т.д. И вот возник вопрос: как до начала проверки узнать был ли проверен лист и были ли обнаружены ошибки ранее?
Я решил не писать ничего на листах, никакого скрытого текста, примечаний и т.д., а воспользоваться коллекцией дополнительных свойств документа 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, иначе, например, при добавлении строки вы получите исключение "Неверное значение параметра".
На этом у меня все. Осталось только выложить исходники, рассмотренного в посте примера:
Delphi: Пример чтения и записи свойств документа Excel
Вот уволюсь с работы, назаводу 100 блогов, раскручусь и купить квартиру в киеве или в Москве :). Шучу, конечно, пока 2х блогов хватает заглаза, хоть и доход небольшой. А вообще, если вас интересует продажа квартир в киеве, то обращайтесь в "Благовест" - агенство по недвижимости.
-----------------------------
| Делись! | Загружай! | Плюсуй! |
| | |









Свежие комментарии