КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
[Главная страница] [Delphi] [Контакты]
Уделим немного времени вопросу создания собственного формата буфера обмена. Предположим, что в разрабатываемой базе данных в одну из таблиц вводится следующая однотипная информация: наименование товара, производитель, единица измерения, дата поступления и цена. Зачастую пользователю приходится вводить порядка десятка практически одинаковых записей, отличающихся лишь ценой или датой.
Чтобы облегчить его труд, научим нашу программу реализовывать методы копирования в буфер и вставки из буфера структурированных данных – записей. Однако в Windows не предопределен формат буфера обмена, способный хранить данные такого рода. В подобных ситуациях приходится немного поработать головой. Взгляните на следующий листинг.
unit My_CF; interface uses Windows, Clipbrd; type TDBRecord = packed record //тип записи, повторяющий структуру строки таблицы базы Name,Creator: string[40]; //товар, производитель Measure: string[5]; //единица измерения Money: currency; //цена за единицу Date: TDateTime; //дата поступления end; // объявление методов для работы с буфером procedure CopyToClipboard(DBRecord: TDBRecord); function LoadFromClipboard: TDBRecord; const FormatName='CF_MyFormat'; // константа с названием пользовательского // формата буфера var CF_MyFormat : Word; // переменная для хранения идентификатора формата implementation procedure CopyToClipboard(DBRecord : TDBRecord); begin //операции копирования данных DBRecord : TDBRecord в буфер обмена end; function LoadFromClipboard : TDBRecord; begin //операции изъятия данных из буфера обмена end;
Первое, что происходит после старта модуля, – это регистрация нашего пользовательского формата данных. Для этого в секции инициализации вызывается метод RegisterClipboardFormat():
function RegisterClipboardFormat(szFormat: PChar) : Integer;
Функцию регистрации формата интересует один-единственный параметр, содержащий название нового формата. В случае успешной регистрации метод возвращает число, идентифицирующее новый формат буфера обмена. Полученное значение будет находиться в диапазоне от 0xC000 до 0xFFFF. С этого момента формат станет доступным в списке форматов буфера обмена (вспомните методы HasFormat и Formats).
Если несколько позже любое другое приложение попытается зарегистрировать одноименный формат, то вместо повторной регистрации метод RegisterClipboardFormat() вернет идентификатор, полученный еще в момент первой регистрации. Таким образом, все приложения, знающие имя и структуру формата, получают право обмениваться данными через буфер обмена.
Вернемся к нашему примеру. Для хранения данных, соответствующих одной строке таблицы базы данных, в листинге объявлена запись TDBRecord, чья структура полностью повторяет характеристики полей таблицы. Далее объявлены заготовки методов, специализирующихся на операциях копирования строки из таблицы в буфер обмена (метод CopyToClipboard) и вставки строки из буфера (LoadFromClipboard). Настала пора наполнить эти методы содержанием. Взгляните на листинг процедуры, осуществляющей копирование данных в буфер обмена:
procedure CopyToClipboard(DBRecord : TDBRecord); var h: THandle; FirstByte: Pointer; begin //операции копирования данных DBRecord : TDBRecord в буфер обмена h := GlobalAlloc(GMEM_MOVEABLE, SizeOf(TDBRecord)); //распределяем область памяти FirstByte := GlobalLock(h); //начало зарезервированной области try Move(DBRecord, FirstByte^, SizeOf(TDBRecord)); //переносим DBRecord в память Clipboard.SetAsHandle(CF_MyFormat, h); //указываем буферу на данные finally GlobalUnLock(h); //освобождаем зарезервированную память end; end;
Метод GlobalAlloc() в общей куче распределяет некую область памяти, необходимую для размещения в ней содержимого записи TDBRecord. В первом параметре метода (при помощи константы GMEM_MOVEABLE) мы указываем, что данная область памяти может быть перемещаемой, а во втором параметре определяем размер этой области в байтах. Метод возвращает дескриптор созданного объекта, а мы в свою очередь сохраняем его в переменной h.
Следующий метод GlobalLock(), получив дескриптор h только что сформированного объекта, транслирует его в физический адрес этого объекта – адрес его первого байта в области памяти. Помимо этого метод GlobalLock информирует операционную систему о том, что мы приступили к работе с объектом h. Физически это выглядит как приращение счетчика ссылок на объект на единицу.
Процедура Move() из арсенала методов Delphi (модуль System) перемещает содержимое DBRecord в зарезервированную область памяти в байт с адресом FirstByte^. Нам остается воспользоваться уже знакомой процедурой SetAsHandle() из коллекции методов класса TClipboard. В заключение в секции finally с помощью метода GlobalUnLock() мы уменьшаем на единицу счетчик ссылок на эту область памяти, тем самым информируя Windows о том, что приложение прекратило работу с объектом h.
Рассмотрим метод, извлекающий данные из буфера обмена.
function LoadFromClipboard : TDBRecord; var h: THandle; FirstByte: Pointer; begin //операции изъятия данных из буфера обмена h := Clipboard.GetAsHandle(CF_MyFormat); if h <> 0 then begin FirstByte := GlobalLock(h); try Move(FirstByte^, Result, SizeOf(TDBRecord)); finally GlobalUnLock(h); end; end; end;
С помощью метода GetAsHandle() в переменную h записывается указатель буфера обмена. Если h не равен нулю (признак того, что буфер не пуст), узнаем адрес первого байта области памяти буфера и при помощи метода Move() копируем данные из буфера в структуру TDBRecord. Для того чтобы проверить работоспособность кода, на главной форме создаем две кнопки и пишем к ним две событийные процедуры реагирующие на щелчок мышью.
implementation uses My_CF; {$R *.dfm} procedure TfrmMain.btnCopyToClipboardClick(Sender: TObject); var DBRecord: TDBRecord; begin DBRecord.Name := edName.Text; DBRecord.Creator := edCreator.Text; DBRecord.Measure := edMeasure.Text; DBRecord.Money := StrToFloat(edMoney.Text); DBRecord.Date := dtpDate.Date; CopyToClipboard(DBRecord); end; procedure TfrmMain.btnPasteFromClipboardClick(Sender: TObject); var DBRecord: TDBRecord; begin DBRecord := LoadFromClipboard; edName1.Text := DBRecord.Name; edCreator1.Text := DBRecord.Creator; edMeasure1.Text := DBRecord.Measure; edMoney1.Text := FloatToStr(DBRecord.Money); edDate.Text := DateToStr(DBRecord.Date); end;
Исходный код проекта (Delphi XE)
Используемая литература: Delphi. Профессиональное программирование. Дмитрий Осипов.