Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 8 июня 2010 в 17:21.
Категории: Моя работа.


Недавно снова пришлось немного поработать с Excel в Delphi. Суть работы состояла в следующем. Есть документ Excel, состоящий минимум из четырех листов (Sheets), на каждом листе содержатся таблицы практически произволоного (с точки зрения форматирования) состава, в таблицах записаны раличные данные. Сам файл может достигать нескольких Mb в размере - реально дофига чисел, текста и т.д. И всю это книгу Excel проверяет простенькая программка на Delphi - определяет возможные опечатки, несоответствия данных, суммирует данные и т.д. Проверка проводится периодически, причем не всегда на одном рабочем месте - иногда один человек проверяет один лист, второй - другой лист и т.д. И вот возник вопрос: как до начала проверки узнать был ли проверен лист и были ли обнаружены ошибки ранее?
Я решил не писать ничего на листах, никакого скрытого текста, примечаний и т.д., а воспользоваться коллекцией дополнительных свойств документа Excel.

Применительно конкретно к моей ситуации использование коллекции свойств документа Excel для хранения данных было предпочтительнее всех остальных по одной причине - далеко не каждый пользователь заглядывает в эти свойства и что-то там меняет, в отличие от основного содержимого документа. Было дело, когда особо "продвинутый" пользователь сносил к чертям собачим все закладки из документа Word'а, а потом недоумевал "чё-то у меня ничё не печатает" :). Поэтому в последнее время при работе с продуктами MS Office, да и вообще любыми шаблонами, стараюсь "заныкать" служебную информацию так, чтоб никто её вообще не видел.
Но это, небольшой отступление. Перейдем непосредственно к свойствам документа.

Свойства рабочей книги (WorkBook)

У объекта WorkBook определено два свойства, отвечающие за доступ к информации о документе:

  1. ContentTypeProperties - возвращает коллекцию MetaProperties, каждый из элементов которой представляет собой мета-информацию по документу Excel. Свойство доступно только для чтения.
  2. 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х блогов хватает заглаза, хоть и доход небольшой. А вообще, если вас интересует продажа квартир в киеве, то обращайтесь в "Благовест" - агенство по недвижимости.
-----------------------------
Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник