Справочное руководство по Delphi

Что такое мультимедиа

  • Точного определения, что же это такое, нет. Но в данный момент и в данном месте, наверное, лучше дать по-возможности наиболее общее определение и сказать, что “мультимедиа” - это термин относящийся к почти всем формам анимации, звукам, видео, которые используются на компьютере.

  • Давая такое общее определение, нужно сказать, что в данном уроке мы имеем дело с подмножеством мультимедиа, которое включает:
    1. Показ видео в формате Microsoft's Video for Windows (AVI).
    2. Воспроизведение звуков и музыки из MIDI и WAVE файлов.
    Данную задачу можно выполнить с помощью динамической библиотеки Microsoft Multimedia Extensions для Windows (MMSYSTEM.DLL), методы которой инкапсулированы в компоненте TMediaPlay, находящийся на странице System Палитры Компонент Delphi.
    Для проигрывания файлов мультимедиа может потребоваться наличие некоторого оборудования и программного обеспечения. Так для воспроизведения звуков нужна звуковая карта. Для воспроизведения AVI в Windows 3.1 (или WFW) требуется установить ПО Microsoft Video.

    DDE-клиенты

  • На рис.4 представлен пример DDE-клиента во время дизайна в среде Delphi.

  • DDE-клиенты

    Рис.4: DDE-клиент в среде Delphi.
    Для построения DDE-клиента в Delphi используются два компонента TDDEClientConv и TDDEClientItem. Аналогично серверу, в программе обычно используется один объект TDDEClientConv и один и более связанных с ним TDDEClientItem.
    TDDEClientConv служит для установления связи с сервером и общим управлением DDE-связью. Установить связь с DDE-сервером можно как во время дизайна, так и во время выполнения программы, причем двумя способами. Первый способ - заполнить вручную необходимые свойства компонента. Это DdeService, DdeTopic и ServiceApplication. Во время дизайна щелкните дважды на одно из первых двух свойств в Инспекторе Объектов - Вы получите диалог для определения DDE-связи (см. рис.5).
    DDE-клиенты
     
    Рис.5: Диалог для установления связи с DDE-сервером (Report Smith).
    Укажите в диалоге имена DDE Service и DDE Topic. Эти имена можно узнать из документации по тому DDE-серверу, с которым Вы работаете. В случае DDE-сервера, созданного на Delphi, это имя программы (без .EXE) и имя объекта TDdeServerConv. Для установления связи через Clipboard в диалоге есть специальная кнопка Past Link. Ей можно воспользоваться, если Вы запустили DDE-сервер, сохранили каким-то образом информацию о связи и вошли в этот диалог. Например, если DDE-сервером является DataBase Desktop, то нужно загрузить в него какую-нибудь таблицу Paradox, выбрать любое поле и выбрать пункт меню Edit|Copy. После этого войдите в диалог и нажмите кнопку Paste Link. Поля в диалоге заполнятся соответствующим образом.
    Свойство ServiceApplication заполняется в том случае, если в поле DDEService содержится имя, отличное от имени программы, либо если эта программа не находится в текущей директории. В этом поле указывается полный путь и имя программы без расширения (.EXE). При работе с Report Smith здесь нужно указать, например : C:\RPTSMITH\RPTSMITH
    Данная информация нужна для автоматического запуска сервера при установлении связи по DDE, если тот еще не был запущен.

    В нашей демо- программе связь устанавливается во время выполнения программы в пунктах меню File|New Link и Edit|Paste Link. В пункте меню File|New Link программно устанавливается связь по DDE с помощью соответствующего метода объекта TDdeServerConv, OpenLink делать не надо, поскольку свойство ConnectMode имеет значение ddeAutomatic:

    procedure TFormD.doNewLink(Sender: TObject);

    begin

    DdeClient.SetLink(AppName.Text, TopicName.Text);

    DdeClientItem.DdeConv := DdeClient;

    DdeClientItem.DdeItem := ItemName.Text;

    end;

    Здесь же заполняются свойства объекта TDdeClietItem.

    В пункте меню Edit|Past Link программно устанавливается связь по DDE с использованием информации из Clipboard:

    procedure TFormD.doPasteLink(Sender: TObject);

    var

    Service, Topic, Item : String;

    begin

    if GetPasteLinkInfo (Service, Topic, Item) then

    begin

    AppName.Text := Service;

    TopicName.Text := Topic;

    ItemName.Text := Item;

    DdeClient.SetLink (Service, Topic);

    DdeClientItem.DdeConv := DdeClient;

    DdeClientItem.DdeItem := ItemName.Text;

    end;

    end;

    После того, как установлена связь, нужно позаботиться о поступающих по DDE данных, это делается в обработчике события OnChange объекта TDdeClietItem:

    procedure TFormD.DdeClientItemChange(Sender: TObject);

    begin

    DdeDat.Lines := DdeClientItem.Lines;

    end;

    Это единственная задача объекта TDdeClientItem.

    На объект TDdeClientConv возлагаются еще две задачи : пересылка данных на сервер и выполнение макросов. Для этого у данного объекта есть соответствующие методы. Посмотрим, как это можно было бы сделать. Выполнение макроса на сервере:

    procedure TFormD.doMacro(Sender: TObject);

    begin

    DdeClient.ExecuteMacroLines(XEdit.Lines, True);

    end;

    Пересылка данных на сервер:

    procedure TFormD.doPoke (Sender: TObject);

    begin

    DdeClient.PokeDataLines(DdeClientItem.DdeItem,XEdit.Lines);

    end;

    DDE-серверы

  • На рис.3 представлен пример DDE-сервера во время дизайна в среде Delphi.

  • DDE-серверы

    Рис.3: DDE-сервер в среде Delphi.
    Для построении DDE-сервера в Delphi имеются два объекта, расположенные на странице System Палитры Компонент - TDdeServerConv и TDdeServerItem. Обычно в проекте используется один объект TDdeServerConv и один или более TDdeServerItem. Для получения доступа к сервису DDE-сервера, клиенту потребуется знать несколько параметров : имя сервиса (Service Name) - это имя приложения (обычно - имя выполняемого файла без расширения EXE, возможно с полным путем); Topic Name - в Delphi это имя компоненты TDdeServerConv; Item Name - в Delphi это имя нужной компоненты TDdeServerItem.
    Назначение объекта TDdeServerConv - общее управление DDE и обработка запросов от клиентов на выполнение макроса. Последнее выполняется в обработчике события OnExecuteMacro, например, как это сделано в нашем случае:
    procedure TDdeSrvrForm.doMacro(Sender: TObject;
    Msg: TStrings);
    var
    Text: string;
    begin
    Text := '';
    if Msg.Count > 0 then Text := Msg.Strings[0];
    MessageDlg ('Executing Macro - ' + Text, mtInformation,
    [mbOK], 0);
    end;
    Объект TDdeServerItem связывается с TDdeServerConv и определяет, что, собственно, будет пересылаться по DDE. Для этого у него есть свойства Text и Lines. (Text имеет то же значение, что и Lines[0].) При изменении значения этих свойств автоматически происходит пересылка обновленных данных во все приложения-клиенты, установившие связь с сервером. В нашем приложении изменение значения свойства Lines происходит в обработчике события OnChange компонента Edit1:
    procedure TDdeSrvrForm.doOnChange(Sender: TObject);
    begin
    if not FInPoke then
    DdeTestItem.Lines := Edit1.Lines;
    end;
    Этот же компонент отвечает за получение данных от клиента, в нашем примере это происходило при нажатии кнопки Poke Data, это выполняется в обработчике события OnPokeData:
    procedure TDdeSrvrForm.doOnPoke(Sender: TObject);
    begin
    FInPoke := True;
    Edit1.Lines := DdeTestItem.Lines;
    FInPoke := False;

    end;

    И последнее - установление связи через Clipboard. Для этого служит метод CopyToClipboard объекта TDdeServerItem. Необходимая информации помещается в Clipboard и может быть вызвана из приложения-клиента при установлении связи. Обычно, в DDE-серверах для этого есть специальный пункт меню Paste Special или Paste Link.

    Итак, мы рассмотрели пример полнофункционального DDE-сервера, построенного с помощью компонент Delphi. Очень часто существующие DDE-серверы не полностью реализуют возможности DDE и предоставляют только часть сервиса. Например, ReportSmith позволяет по DDE только выполнять команды (макросы).

    DLL, использующие объекты VCL для работы с данными

  • При создании своей динамической библиотеки Вы можете использовать вызовы функций из других DLL. Пример такой DLL есть в поставке Delphi (X:\DELPHI\DEMOS\BD\BDEDLL). В эту DLL помещена форма, отображающая данные из таблицы и использующая для доступа к ней объекты VCL (TTable, TDBGrid, TSession), которые, в свою очередь, вызывают функции BDE. Как следует из комментариев к этому примеру, для такой DLL имеется ограничение: ее не могут одновременно использовать несколько задач. Это вызвано тем, что объект Session, который создается автоматически при подключении модуля DB, инициализируется для модуля, а не для задачи. Если попытаться загрузить эту DLL вторично из другого приложения, то возникнет ошибка. Для предотвращения одновременной загрузки DLL несколькими задачами нужно осуществить некоторые действия. В примере - это процедура проверки того, используется ли DLL в данный момент другой задачей.



  • Добавление новых объектов в VCL

  • Предположим, что у вас появился уже готовый компонент. Как его добавить в VCL? Для этого выберите пункт меню Options|Install Components… Появится диалог, как на рис.1

  • Добавление новых объектов в VCL

    Рис.1: Диалог установки нового компонента
    Нажмите “Add” и укажите модуль, содержащий процедуру регистрации, нажмите “OK” и после успешной перекомпиляции новый объект появится в палитре.

    Добавление TReport в приложение

  • Добавить отчет в приложение Delphi очень легко. Положите компонент TReport на форму. Инспектор Объектов Delphi показывает, что компонент TReport имеет несколько свойств и ни одного события.

  • Добавление TReport в приложение

    Рис.1: Инспектор объектов для свойств TReport

    Дополнительные элементы

    В данном разделе внимание фокусируется на трех инструментах, которые можно воспринимать как вспомогательные для среды программирования:


  • Меню (Menu System)


  • Панель с кнопками для быстрого доступа (SpeedBar)


  • Редактор картинок (Image Editor)

  • Меню предоставляет быстрый и гибкий интерфейс к среде Delphi, потому что может управляться по набору “горячих клавиш”. Это удобно еще и потому, что здесь используются слова или короткие фразы, более точные и понятные, нежели иконки или пиктограммы. Вы можете использовать меню для выполнения широкого круга задач; скорее всего, для наиболее общих задач вроде открытия и закрытия файлов, управления отладчиком или настройкой среды программирования.
    SpeedBar находится непосредственно под меню, слева от Палитры Компонент (рис.6). SpeedBar выполняет много из того, что можно сделать через меню. Если задержать мышь над любой из иконок на SpeedBar, то Вы увидите что появится подсказка, объясняющая назначение данной иконки.
    Дополнительные элементы

    Рис.6: SpeedBar находится слева от Палитры Компонент.
    Редактор Картинок, показанный на рис.7, работает аналогично программе Paintbrush из Windows. Вы можете получить доступ к этому модулю выбрав пункт меню Tools | Image Editor.
    Дополнительные элементы

    Рис.7: Редактор Картинок можно использовать для создания картинок для кнопок, иконок и др. визуальных частей для программы.
    А теперь нужно рассмотреть те элементы, которые программист на Delphi использует в повседневной жизни.




  • Доступ к экземпляру объекта exception

  • До сих пор мы рассматривали механизмы защиты кода и ресурсов, логику работы программы в исключительной ситуации. Теперь нужно немного разобраться с тем, как же обрабатывать возникшую ошибку. А точнее, как получить дополнительную информацию о коде ошибки, текст сообщения и т.п.

  • Как уже говорилось, при вызове исключительной ситуации (raise) автоматически создается экземпляр соответствующего класса, который и содержит информацию об ошибке. Весь вопрос в том, как в обработчике данной ситуации получить доступ к этому объекту.
    Рассмотрим модифицированную процедуру A в нашем примере:
    procedure NewA;
    begin
    writeln('Enter A');
    try
    writeln('Enter A''s try block');
    B;
    writeln('After B call');
    except
    on E: ESampleError do writeln(E.Message);
    on ESomethingElse do
    writeln('Inside A''s ESomethingElse handler');
    end;
    writeln('Exit A');
    end;
    Здесь все изменения внесены в строку
    on ESE: ESampleError do writeln(ESE.Message);
    Пример демонстрирует еще одно новшество в языке Object Pascal - создание локальной переменной. В нашем примере локальной переменной является ESE - это тот самый экземпляр класса ESampleError, который был создан в процедуре C в момент вызова исключительного состояния. Переменная ESE доступна только внутри блока do. Свойство Message объекта ESE содержит сообщение, которое было передано в конструктор Create в процедуре C.
    Есть еще один способ доступа к экземпляру exception - использовать функцию ExceptionObject:
    on ESampleError do
    writeln(ESampleError(ExceptionObject).Message);
  • Предопределенные обработчики исключительных ситуаций

  • Ниже Вы найдете справочную информацию по предопределенным исключениям, необходимую для профессионального программирования в Delphi.


  • Exception - базовый класс-предок всех обработчиков исключительных ситуаций.



  • EAbort - “скрытое” исключение. Используйте его тогда, когда хотите прервать тот или иной процесс с условием, что пользователь программы не должен видеть сообщения об ошибке. Для повышения удобства использования в модуле SysUtils предусмотрена процедура Abort, определенная, как:


  • procedure Abort;

    begin

    raise EAbort.CreateRes(SOperationAborted) at ReturnAddr;

    end;





  • EComponentError - вызывается в двух ситуациях:




  • 1) при попытке регистрации компоненты за пределами процедуры Register;


  • 2) когда имя компоненты не уникально или не допустимо.





  • EConvertError - происходит в случае возникновения ошибки при выполнении функций StrToInt и StrToFloat, когда конвертация строки в соответствующий числовой тип невозможна.






  • EInOutError - происходит при ошибках ввода/вывода при включенной директиве {$I+}.






  • EIntError - предок исключений, случающихся при выполнении целочисленных операций.




  • EDivByZero - вызывается в случае деления на ноль, как результат RunTime Error 200.




  • EIntOverflow - вызывается при попытке выполнения операций, приводящих к переполнению целых переменных, как результат RunTime Error 215 при включенной директиве {$Q+}.




  • ERangeError - вызывается при попытке обращения к элементам массива по индексу, выходящему за пределы массива, как результат RunTime Error 201 при включенной директиве {$R+}.






  • EInvalidCast - происходит при попытке приведения переменных одного класса к другому классу, несовместимому с первым (например, приведение переменной типа TListBox к TMemo).






  • EInvalidGraphic - вызывается при попытке передачи в LoadFromFile файла, несовместимого графического формата.






  • EInvalidGraphicOperation - вызывается при попытке выполнения операций, неприменимых для данного графического формата (например, Resize для TIcon).






  • EInvalidObject - реально нигде не используется, объявлен в Controls.pas.






  • EInvalidOperation - вызывается при попытке отображения или обращения по Windows-обработчику (handle) контрольного элемента, не имеющего владельца (например, сразу после вызова MyControl:=TListBox.Create(...) происходит обращение к методу Refresh).






  • EInvalidPointer - происходит при попытке освобождения уже освобожденного или еще неинициализированного указателя, при вызове Dispose(), FreeMem() или деструктора класса.







  • EListError - вызывается при обращении к элементу наследника TList по индексу, выходящему за пределы допустимых значений (например, объект TStringList содержит только 10 строк, а происходит обращение к одиннадцатому).






  • EMathError - предок исключений, случающихся при выполнении операций с плавающей точкой.






  • EInvalidOp - происходит, когда математическому сопроцессору передается ошибочная инструкция. Такое исключение не будет до конца обработано, пока Вы контролируете сопроцессор напрямую из ассемблерного кода.






  • EOverflow - происходит как результат переполнения операций с плавающей точкой при слишком больших величинах. Соответствует RunTime Error 205.






  • Underflow - происходит как результат переполнения операций с плавающей точкой при слишком малых величинах. Соответствует RunTime Error 206.




  • EZeroDivide - вызывается в результате деления на ноль.






  • EMenuError - вызывается в случае любых ошибок при работе с пунктами меню для компонент TMenu, TMenuItem, TPopupMenu и их наследников.






  • EOutlineError - вызывается в случае любых ошибок при работе с TOutLine и любыми его наследниками.






  • EOutOfMemory - происходит в случае вызовов New(), GetMem() или конструкторов классов при невозможности распределения памяти. Соответствует RunTime Error 203.






  • EOutOfResources - происходит в том случае, когда невозможно выполнение запроса на выделение или заполнение тех или иных Windows ресурсов (например таких, как обработчики - handles).






  • EParserError - вызывается когда Delphi не может произвести разбор и перевод текста описания формы в двоичный вид (часто происходит в случае исправления текста описания формы вручную в IDE Delphi).






  • EPrinter - вызывается в случае любых ошибок при работе с принтером.






  • EProcessorException - предок исключений, вызываемых в случае прерывания процессора- hardware breakpoint. Никогда не включается в DLL, может обрабатываться только в “цельном” приложении.







  • EBreakpoint - вызывается в случае останова на точке прерывания при отладке в IDE Delphi. Среда Delphi обрабатывает это исключение самостоятельно.






  • EFault - предок исключений, вызываемых в случае невозможности обработки процессором тех или иных операций.








  • EGPFault - вызывается, когда происходит “общее нарушение защиты” - General Protection Fault. Соответствует RunTime Error 216.








  • EInvalidOpCode - вызывается, когда процессор пытается выполнить недопустимые инструкции.








  • EPageFault - обычно происходит как результат ошибки менеджера памяти Windows, вследствие некоторых ошибок в коде Вашего приложения. После такого исключения рекомендуется перезапустить Windows.








  • EStackFault - происходит при ошибках работы со стеком, часто вследствие некорректных попыток доступа к стеку из фрагментов кода на ассемблере. Компиляция Ваших программ со включенной проверкой работы со стеком {$S+} помогает отследить такого рода ошибки.






  • ESingleStep - аналогично EBreakpoint, это исключение происходит при пошаговом выполнении приложения в IDE Delphi, которая сама его и обрабатывает.






  • EPropertyError - вызывается в случае ошибок в редакторах свойств, встраиваемых в IDE Delphi. Имеет большое значение для написания надежных property editors. Определен в модуле DsgnIntf.pas.






  • EResNotFound - происходит в случае тех или иных проблем, имеющих место при попытке загрузки ресурсов форм - файлов .DFM в режиме дизайнера. Часто причиной таких исключений бывает нарушение соответствия между определением класса формы и ее описанием на уровне ресурса (например,вследствие изменения порядка следования полей-ссылок на компоненты, вставленные в форму в режиме дизайнера).






  • EStreamError - предок исключений, вызываемых при работе с потоками.






  • EFCreateError - происходит в случае ошибок создания потока (например, при некорректном задании файла потока).






  • EFilerError - вызывается при попытке вторичной регистрации уже зарегистрированного класса (компоненты). Является, также, предком специализированных обработчиков исключений, возникающих при работе с классами компонент.









  • EClassNotFound - обычно происходит, когда в описании класса формы удалено поле-ссылка на компоненту, вставленную в форму в режиме дизайнера. Вызывается, в отличие от EResNotFound, в RunTime.








  • EInvalidImage - вызывается при попытке чтения файла, не являющегося ресурсом, или разрушенного файла ресурса, специализированными функциями чтения ресурсов (например, функцией ReadComponent).








  • EMethodNotFound - аналогично EClassNotFound, только при несоответствии методов, связанных с теми или иными обработчиками событий.








  • EReadError - происходит в том случае, когда невозможно прочитать значение свойства или другого набора байт из потока (в том числе ресурса).








  • EFOpenError - вызывается когда тот или иной специфированный поток не может быть открыт (например, когда поток не существует).






  • EStringListError - происходит при ошибках работы с объектом TStringList (кроме ошибок, обрабатываемых TListError).








  • Исключения, возникающие при работе с базами данных


  • Delphi, обладая прекрасными средствами доступа к данным, основывающимися на интерфейсе IDAPI, реализованной в виде библиотеки Borland Database Engine (BDE), включает ряд обработчиков исключительных ситуаций для регистрации ошибок в компонентах VCL работающим с БД. Дадим краткую характеристику основным из них:





  • EDatabaseError - наследник Exception ; происходит при ошибках доступа к данным в компонентах-наследниках TDataSet. Объявлено в модуле DB. Ниже приведен пример из Delphi On-line Help, посвященный этому исключению:


  • repeat {пока не откроем таблицу или не нажмем кнопку Cancel}

    try

    Table1.Active := True; {Пытаемся открыть таблицу}

    Break; { Если нет ошибки - прерваем цикл}

    except

    on EDatabaseError do

    {Если нажата OK - повторяем попытку открытия Table1}

    if MessageDlg('Не могу открыть Table1', mtError,

    [mbOK, mbCancel], 0) <> mrOK

    then

    raise;

    end;

    until False;



  • EDBEngineError - наследник EDatabaseError ; вызывается, когда происходят ошибки BDE или на сервере БД. Объявлено в модуле DB:







  • EDBEngineError = class(EDatabaseError)


  • private

    FErrors: TList;

    function GetError(Index: Integer): TDBError;

    function GetErrorCount: Integer;

    public

    constructor Create(ErrorCode: DBIResult);

    destructor Destroy;

    property ErrorCount: Integer;

    property Errors[Index: Integer]: TDBError;

    end;

    Особенно важны два свойства класса EDBEngineError :

    Errors - список всех ошибок, находящихся в стеке ошибок BDE. Индекс первой ошибки 0;

    ErrorCount - количество ошибок в стеке.

    Объекты, содержащиеся в Errors, имеют тип TDBError.

    Доступные свойства класса TDBError:

    ErrorCode - код ошибки, возвращаемый Borland Database Engine;

    Category - категория ошибки, описанной в ErrorCode;

    SubCode - ‘субкод’ ошибки из ErrorCode;

    NativeError - ошибка, возвращаемая сервером БД. Если NativeError 0, то ошибка в ErrorCode не от сервера;

    Message - сообщение, переданное сервером, если NativeError не равно 0; сообщение BDE - в противном случае.





  • EDBEditError - наследник Exception ; вызывается, когда данные не совместимы с маской ввода, наложенной на поле. Объявлено в модуле Mask.






  • Два вида программ мультимедиа

  • · Иногда приходится предоставлять пользователям простой путь для проигрывания максимально широкого круга файлов. Это означает, что Вам нужно будет дать пользователю доступ к жесткому диску или CD-ROM, и затем позволить ему выбрать и воспроизвести подходящий файл. В этом случае, на форме обычно располагается TMediaPlayer, предоставляющий возможность управления воспроизведением.

  • · Иногда программист может захотеть скрыть от пользователя существование компонента TMediaPlayer. То есть, воспроизвести звук или видео без того, чтобы пользователь заботился об их источнике. В частности, звук может быть частью презентации. Например, показ какого-нибудь графика на экране может сопровождаться объяснением, записанным в WAV файл. В течении презентации пользователь даже не знает о существовании TMediaPlayer. Он работает в фоновом режиме. Для этого компонент делается невидимым (Visible = False) и управляется программно.

    Главные составные части среды программирования

    Ниже перечислены основные составные части Delphi:


  • Дизайнер Форм (Form Designer)


  • Окно Редактора Исходного Текста (Editor Window)


  • Палитра Компонент (Component Palette)


  • Инспектор Объектов (Object Inspector)


  • Справочник (On-line help)

  • Есть, конечно, и другие важные составляющие Delphi, вроде линейки инструментов, системного меню и многие другие, нужные Вам для точной настройки программы и среды программирования.
    Программисты на Delphi проводят большинство времени переключаясь между Дизайнером Форм и Окном Редактора Исходного Текста (которое для краткости называют Редактор). Прежде чем Вы начнете, убедитесь, что можете распознать эти два важных элемента. Дизайнер Форм показан на рис.1, окно Редактора - на рис.2.
    Главные составные части среды программирования

    Главные составные части среды программирования

    Дизайнер Форм в Delphi столь интуитивно понятен и прост в использовании, что создание визуального интерфейса превращается в детскую игру. Дизайнер Форм первоначально состоит из одного пустого окна, которое Вы заполняете всевозможными объектами, выбранными на Палитре Компонент.
    Несмотря на всю важность Дизайнера Форм, местом, где программисты проводят основное время является Редактор. Логика является движущей силой программы и Редактор - то место, где Вы ее “кодируете”.
    Палитра Компонент (см. рис.3) позволяет Вам выбрать нужные объекты для размещения их на Дизайнере Форм. Для использования Палитры Компонент просто первый раз щелкните мышкой на один из объектов и потом второй раз - на Дизайнере Форм. Выбранный Вами объект появится на проектируемом окне и им можно манипулировать с помощью мыши.
    Палитра Компонент использует постраничную группировку объектов. Внизу Палитры находится набор закладок - Standard, Additional, Dialogs и т.д. Если Вы щелкнете мышью на одну из закладок, то Вы можете перейти на следующую страницу Палитры Компонент. Принцип разбиения на страницы широко используется в среде программирования Delphi и его легко можно использовать в своей программе. (На странице Additional есть компоненты для организации страниц с закладками сверху и снизу).

    Главные составные части среды программирования


    Рис.3: Палитра Компонент - место, где Вы выбираете объекты, которые будут помещены на вашу форму.

    Предположим, Вы помещаете компонент TEdit на форму; Вы можете двигать его с места на место. Вы также можете использовать границу, прорисованную вокруг объекта для изменения его размеров. Большинством других компонент можно манипулировать тем же образом. Однако, невидимые во время выполнения программы компоненты (типа TMenu или TDataBase) не меняют своей формы.

    Слева от Дизайнера Форм Вы можете видеть Инспектор Объектов (рис.4). Заметьте, что информация в Инспекторе Объектов меняется в зависимости от объекта, выбранного на форме. Важно понять, что каждый компонент является настоящим объектом и Вы можете менять его вид и поведение с помощью Инспектора Объектов.

    Инспектор Объектов состоит из двух страниц, каждую из которых можно использовать для определения поведения данного компонента. Первая страница - это список свойств, вторая - список событий. Если нужно изменить что-нибудь, связанное с определенным компонентом, то Вы обычно делаете это в Инспекторе Объектов. К примеру, Вы можете изменить имя и размер компонента TLabel изменяя свойства Caption, Left, Top, Height, и Width.

    Вы можете использовать закладки внизу Инспектора Объектов для переключения между страницами свойств и событий. Страница

    Главные составные части среды программирования


    событий связана с Редактором; если Вы дважды щелкнете мышкой на правую сторону какого-нибудь пункта, то соответствующий данному событию код автоматически запишется в Редактор, сам Редактор немедленно получит фокус, и Вы сразу же имеете возможность добавить код обработчика данного события. Данный аспект среды программирования Delphi будет еще обсуждаться позднее.

    Последняя важная часть среды Delphi - Справочник (on-line help). Для доступа к этому инструменту нужно просто выбрать в системном меню пункт Help и затем Contents. На экране появится Справочник, показанный на рис.5

    Главные составные части среды программирования


    Рис.5: Справочник - быстрый поиск любой информации.

    Справочник является контекстно-зависимым; при нажатии клавиши F1, Вы получите подсказку, соответствующую текущей ситуации. Например, находясь в Инспекторе Объектов, выберите какое-нибудь свойство и нажмите F1 - Вы получите справку о назначении данного свойства. Если в любой момент работы в среде Delphi возникает неясность или затруднение - жмите F1 и необходимая информация появится на экране.






  • Графическая печать (объект TPrinter)

    И все же, более интересно, как из программы созданной в Delphi можно вывести на печать графическую информацию. Для этого есть специальный объект Printer (класса TPrinter). Он становится доступен, если к программе подключить модуль Printers (т.е. добавить имя модуля в разделе uses
    ). С помощью этого объекта печать на принтере графической информации становится не сложнее вывода этой информации на экран. Основным является то, что Printer предоставляет разработчику свойство Canvas ( работа с канвой описана в предыдущем уроке) и методы, выводящие содержание канвы на принтер. Рассмотрим подробнее свойства и методы объекта Printer.
    Свойства Printer:

  • Aborted - тип булевский; показывает, прервал ли пользователь работу принтера методом Abort.

  • Canvas - канва, место для вывода графики; работа с Canvas описана в Уроке 5.
    Fonts - список доступных шрифтов.
    Handle - используется при прямых вызовах Windows API.
    Orientation - ориентация страницы, вертикально или горизонтально.
    PageWidth, PageHeight, PageNumber - соответственно ширина, высота и номер страницы.
    Printers
    перечисляет все установленные в системе принтеры, а
    PrinterIndex
    указывает, какой из них является текущим. Чтобы печатать на принтере по умолчанию здесь должно быть значение -1.
    Printing - тип булевский; показывает, начата ли печать (методом BeginDoc).
    Title -
    заголовок для Print Manager и для заголовка перед выводом на сетевом принтере.
    Методы Printer:
    Abort - прерывает печать, начатую методом BeginDoc

  • BeginDoc - вызывается перед тем, как начать рисовать на канве.

  • EndDoc - вызывается когда все необходимое уже нарисовано на канве, принтер начинает печатать именно после этого метода.
    NewPage - переход на новую страницу.
    Остальными методами объекта в обычных случаях пользоваться не нужно.
    Итак, порядок вывода на печать графической информации выглядит следующим образом:


  • выполняется метод BeginDoc


  • на канве (Canvas) рисуем все, что нужно


  • при необходимости разместить информацию на нескольких листах вызываем метод NewPage


  • посылаем нарисованное на принтер, выполняя метод EndDoc





  • Графические компоненты

  • В стандартную библиотеку визуальных компонент Delphi входит несколько объектов, с помощью которых можно придать своей программе совершенно оригинальный вид. Это - TImage (TDBImage), TShape, TBevel.

  • TImage
    позволяет поместить графическое изображение в любое место на форме. Этот объект очень прост в использовании - выберите его на странице Additional и поместите в нужное место формы. Собственно картинку можно загрузить во время дизайна в редакторе свойства Picture (Инспектор Объектов). Картинка должна храниться в файле в формате BMP (bitmap), WMF (Windows Meta File) или ICO (icon). (TDBImage отображает картинку, хранящуюся в таблице в поле типа BLOB. При этом доступен только формат BMP.)
    Как известно, форматов хранения изображений гораздо больше трех вышеназванных (например, наиболее известны PCX, GIF, TIFF, JPEG). Для включения в программу изображений в этих форматах нужно либо перевести их в формат BMP, либо найти библиотеки третьих фирм, в которых есть аналог TImage, “понимающий” данные форматы (есть как VBX объекты, так и “родные” объекты для Delphi).
    При проектировании следует помнить, что изображение, помещенное на форму во время дизайна, включается в файл .DPR и затем прикомпилируется к EXE файлу. Поэтому такой EXE файл может получиться достаточно большой. Как альтернативу можно рассмотреть загрузку картинки во время выполнения программы, для этого у свойства Picture (которое является объектом со своим набором свойств и методов) есть специальный метод LoadFromFile. Это делается, например, так:
    if OpenDialog1.Execute then
    Image1.Picture.LoadFromFile(OpenDialog1.FileName);
    Важными являются свойства объекта Center и Stretch - оба имеют булевский тип. Если Center установлено в True, то центр изображения будет совмещаться с центром объекта TImage. Если Stretch установлено в True, то изображение будет сжиматься или растягиваться таким образом, чтобы заполнить весь объект TImage.
    TShape - простейшие графические объекты на форме типа круг, квадрат и т.п. Вид объекта указывается в свойстве Shape. Свойство Pen определяет цвет и вид границы объекта. Brush задает цвет и вид заполнения объекта. Эти свойства можно менять как во время дизайна, так и во время выполнения программы.
    TBevel - объект для украшения программы, может принимать вид рамки или линии. Объект предоставляет меньше возможностей по сравнению с TPanel, но не занимает ресурсов. Внешний вид указывается с помощью свойств Shape и Style.

    Инструментальные средства

  • В дополнение к инструментам, обсуждавшимся выше, существуют пять средств, поставляемых вместе с Delphi. Эти инструментальные средства:

  • · Встроенный отладчик
    · Внешний отладчик (поставляется отдельно)
    · Компилятор командной строки
    · WinSight
    · WinSpector
    Данные инструменты собраны в отдельную категорию не потому, что они менее важны, чем другие, но потому, что они играют достаточно абстрактную техническую роль в программировании.
    Чтобы стать сильным программистом на Delphi, Вам понадобится понять, как использовать отладчик Delphi. Отладчик позволяет Вам пройти пошагово по исходному тексту программы, выполняя по одной строке за раз, и открыть просмотровое окно (Watch), в котором будут отражаться текущие значения переменных программы.
    Встроенный отладчик, который наиболее важен из пяти вышеперечисленных инструментов, работает в том же окне, что и Редактор. Внешний отладчик делает все, что делает встроенный и кое-что еще. Он более быстр и мощен, чем встроенный. Однако он не так удобен в использовании, главным образом из-за необходимости покидать среду Delphi.
    Теперь давайте поговорим о компиляторах. Внешний компилятор, называется DCC.EXE, полезен, в основном, если Вы хотите скомпилировать приложение перед отладкой его во внешнем отладчике. Большинство программистов, наверняка, посчитают, то гораздо проще компилировать в среде Delphi, нежели пытаться создать программу из командной строки. Однако, всегда найдется несколько оригиналов, которые будут чувствовать себя счастливее, используя компилятор командной строки. Но это факт - возможно создать и откомпилировать программу на Delphi используя только DCC.EXE и еще одну программу CONVERT.EXE, которая поможет создать формы. Однако, данный подход неудобен для большинства программистов.
    WinSight и WinSpector интересны преимущественно для опытных программистов в Windows. Это не значит, что начинающий не должен их запускать и экспериментировать с ними по своему усмотрению. Но эти инструменты вторичны и используются для узких технических целей.

    Из этих двух инструментов WinSight определенно более полезен. Основная его функция - позволить Вам наблюдать за системой сообщений Windows. Хотя Delphi делает много для того, чтобы спрятать сложные детали данной системы сообщений от неопытных пользователей, тем не менее Windows является операционной системой, управляемой событиями. Почти все главные и второстепенные события в среде Windows принимают форму сообщений, которые рассылаются с большой интенсивностью среди различными окнами на экране. Delphi дает Вам полный доступ к сообщениям Windows и позволяет отвечать на них, как только будет нужно. В результате, опытным пользователям WinSight становится просто необходим.

    WinSpector сохраняет запись о текущем состоянии машины в текстовый файл; Вы можете просмотреть этот файл для того, чтобы узнать, что неправильно идет в программе. Данный инструмент полезен, когда программа находится в опытной эксплуатации - можно получить важную информацию при крушении системы.

    Исключительные ситуации в DLL

    Возникновение исключительной ситуации в DLL, созданной в Delphi, приведет к прекращению выполнения всего приложения, если эта ситуация не была обработана внутри DLL. Поэтому желательно предусмотреть все возможные неприятности на момент разработки DLL. Можно порекомендовать возвращать результат выполнения импортируемой функции в виде строки или числа и, при необходимости, заново вызывать исключительную ситуацию в программе.
    Код в DLL:
    function MyFunc : string;
    begin
    try
    {собственно код функции}
    except
    on EResult: Exception do
    Result:=Format(DllErrorViewingTable,





  • [EResult.Message]);

  • else
    Result := Format(DllErrorViewingTable,





  • ['Unknown error']);

  • end;
    end;
    Код в программе:
    StrResult:=MyFunc;
    if StrResult<>’’ then
    raise Exception.Create(StrResult);

    Использование DDE

  • Приложение, получающее данные из другого приложения по DDE и/или управляющее другим приложением с помощью команд через DDE является DDE-клиентом. В этом случае второе приложение является DDE-сервером. Одно и то-же приложение может быть одновременно и сервером, и клиентом (например, MicroSoft Word). Построение DDE-серверов и DDE-клиентов удобно рассмотреть на примере, поставляемом с Delphi (каталог x:\delphi\demos\ddedemo). Сперва давайте рассмотрим логику работы примера. Для начала нужно откомпилировать проекты DDESRVR.DPR и DDECLI.DPR, после этого запустите программу DDECLI.EXE (DDE-клиент) и выберите пункт меню File|New Link. При этом должна запуститься программа DDESRVR (DDE-сервер). Если теперь редактировать текст в окне сервера, то изменения мгновенно отразятся в приложении-клиенте (см. рис.1, рис.2)

  • Использование DDE

    Рис.1: Приложение - DDE-сервер. Здесь идет редактирование текста.
    Использование DDE

    Рис.2: Приложение - DDE-клиент. Здесь отображаются изменения.
    Пример демонстрирует и другие возможности DDE: - пересылка данных из клиента на сервер (Poke Data); наберите любой текст в правом окне DDE-клиента и нажмите кнопку Poke Data, этот текст появится в окне сервера. - исполнение команд (макросов) на сервере; наберите любой текст в правом окне DDE-клиента и нажмите кнопку Exec Macro, DDE-сервер выдаст соответствующее диалоговое окно. - установление связи через Clipboard; закройте оба DDE-приложения и запустите их заново, затем в DDE-сервере выберите пункт меню Edit|Copy, далее в клиенте выберите пункт меню Edit|Paste Link.
    Теперь давайте рассмотрим эти демонстрационные программы с технической точки зрения и узнаем, каким образом в Delphi можно создать DDE-приложения. Начнем с DDE-сервера.

    Использование DLL в Delphi (импорт)

  • Для организации импорта, т.е. доступа к функциям, экспортируемым из DLL, так же как и для их экспорта, Delphi предоставляет стандартные средства.

  • Для показанных выше примеров, в Вашей программе следует объявить функции, импортируемые из DLL таким образом:
    { импорт по специфицированному имени }
    procedure ImportByName;external 'MYDLL' name 'MYEXPORTPROC';
    { импорт по индексу }
    procedure ImportByOrdinal; external 'MYDLL' index 10;
    { импорт по оригинальному имени }
    procedure MyExportFunc1; external 'MYDLL';
    Этот способ называется статическим импортом.
    Как Вы могли заметить, расширение файла, содержащего DLL, не указывается - по умолчанию подразумеваются файлы *.DLL и *.EXE. Как же тогда быть в случае, если файл имеет другое расширение (например, как COMPLIB.DCL в Delphi), или если требуется динамическое определение DLL и импортируемых функций (например, Ваша программа работает с различными графическими форматами, и для каждого из них существует отдельная DLL.)?
    Для решения такого рода проблем Вы можете обратиться напрямую к API Windows, используя, так называемый, динамический импорт:
    uses
    WinTypes, WinProcs, ... ;
    type
    TMyProc = procedure ;
    var
    Handle : THandle;
    MyImportProc : TMyProc;
    begin
    Handle:=LoadLibrary('MYDLL');
    if Handle>=32 then { if <=32 - error ! }
    begin
    @MyImportProc:=GetProcAddress(Handle,'MYEXPORTPROC');
    if MyImportProc<>nil then
    ...... {using imported procedure}
    end;
    FreeLibrary(Handle);
    end;
    !!! Синтаксические диаграммы объявлений экспорта/импорта, подмена точки выхода из DLL, и другие примеры, Вы можете найти в OnLine Help Delphi, Object Pascal Language Guide, входящему в Borland RAD Pack for Delphi, и, например, в книге "Teach Yourself Delphi in 21 Days".
    Если не говорить о генерируемом компилятором коде (сейчас он более оптимизирован), то все правила синтаксиса остались те же , что и в Borland Pascal 7.0

    Использование фильтров для ограничения числа записей в DataSet

    Процедура ApplyRange позволяет Вам установить фильтр, который ограничивает диапазон просматриваемых записей. Например, в БД Customers, поле CustNo имеет диапазон от 1,000 до 10,000. Если Вы хотите видеть только те записи, которые имеют номер заказчика между 2000 и 3000, то Вы должны использовать метод ApplyRange, и еще два связанных с ним метода. Данные методы работают только с индексированным полем.
    Вот процедуры, которые Вы будете чаще всего использовать при установке фильтров:
    procedure SetRangeStart;
    procedure SetRangeEnd;
    procedure ApplyRange;
    procedure CancelRange;
    Кроме того, у TTable есть дополнительные методы для управления фильтрами:
    procedure EditRangeStart;
    procedure EditRangeEnd;
    procedure SetRange;
    Для использования этих процедур необходимо:

  • Сначала вызвать SetRangeStart и использовать свойство Fields для определения начала диапазона.

  • Затем вызвать SetRangeEnd и вновь использовать свойство Fields для определения конца диапазона.

  • Первые два шага подготавливают фильтр, и теперь все что Вам необходимо, это вызвать ApplyRange, и новый фильтр вступит в силу.

  • Когда нужно прекратить действие фильтра - вызовите CancelRange.

  • Программа RANGE, которая есть среди примеров Delphi, показывает, как использовать эти процедуры. Чтобы создать программу, поместите TTable, TDataSource и TdbGrid на форму. Соедините их так, чтобы Вы видеть таблицу CUSTOMERS из подкаталога DEMOS. Затем поместите два объекта TLabel на форму и назовите их ‘Start Range’ и ‘End Range’. Затем положите на форму два объекта TEdit. Наконец, добавьте кнопки ‘ApplyRange’ и ‘CancelRange’. Когда Вы все выполните, форма имеет вид, как на рис.7
    Использование фильтров для ограничения числа записей в DataSet

    Рис.7: Программа RANGE показывает как ограничивать число записей таблицы для просмотра.
    Процедуры SetRangeStart и SetRangeEnd позволяют Вам указать первое и последнее значения в диапазоне записей, которые Вы хотите видеть. Чтобы начать использовать эти процедуры, сначала выполните double-click на кнопке ApplyRange, и создайте процедуру, которая выглядит так:

    procedure TForm1.ApplyRangeBtnClick(Sender: TObject);

    begin

    Table1.SetRangeStart;

    if RangeStart.Text <> '' then

    Table1. Fields[0].AsString := RangeStart.Text;

    Table1.SetRangeEnd;

    if RangeEnd.Text <> '' then

    Table1.Fields[0].AsString := RangeEnd.Text;

    Table1.ApplyRange;

    end;

    Сначала вызывается процедура SetRangeStart, которая переводит таблицу в режим диапазона (range mode). Затем Вы должны определить начало и конец диапазона. Обратите внимание, что Вы используете свойство Fields для определения диапазона:

    Table1.Fields[0].AsString := RangeStart.Text;

    Такое использование свойства Fields - это специальный случай, так как синтаксис, показанный здесь, обычно используется для установки значения поля. Этот специальный случай имеет место только после того, как Вы перевели таблицу в режим диапазона, вызвав SetRangeStart.

    Заключительный шаг в процедуре показанной выше - вызов ApplyRange. Этот вызов фактически приводит ваш запрос в действие. После вызова ApplyRange, TTable больше не в находится в режиме диапазона, и свойства Fields функционирует как обычно.

    Обработчик события нажатия кнопки ‘CancelRange’:

    procedure TForm1.CancelRangeBtnClick(Sender: TObject);

    begin

    Table1.CancelRange;

    end;






  • Использование SetKey для поиска в таблице

  • Для того, чтобы найти некоторую величину в таблице, программист на Delphi может использовать две процедуры SetKey и GotoKey. Обе эти процедуры предполагают, что поле по которому Вы ищете индексировано. Delphi поставляется с демонстрационной программой SEARCH, которая показывает, как использовать эти запросы.

  • Чтобы создать программу SEARCH, поместите TTable, TDataSource, TDBGrid, TButton, TLabel и TEdit на форму, и расположите их как показано на рис.6. Назовите кнопку Search, и затем соедините компоненты БД так, чтобы Вы видели в DBGrid1 таблицу Customer.
    Использование SetKey для поиска в таблице

    Рис.6: Программа SEARCH позволяет Вам ввести номер заказчика и затем найти его по нажатию кнопки.
    Вся функциональность программы SEARCH скрыта в единственном методе, который присоединен к кнопке Search. Эта функция считывает строку, введенную в окно редактора, и ищет ее в колонке CustNo, и наконец помещает фокус на найденной записи. В простейшем варианте, код присоединенный к кнопке Search выглядит так:
    procedure TSearchDemo.SearchClick(Sender: TObject);
    begin
    Table1.SetKey;
    Table1.FieldByName(’CustNo’).AsString := Edit1.Text;
    Table1.GotoKey;
    end;
    Первый вызов в этой процедуре установит Table1 в режим поиска. Delphi должен знать, что Вы переключились в режим поиска просто потому, что свойство Fields используется по другому в этом режиме. Далее, нужно присвоить свойству Fields значение, которое Вы хотите найти. Для фактического выполнения поиска нужно просто вызывать Table1.GotoKey.
    Если Вы ищете не по первичному индексу файла, тогда Вы должны определить имя индекса, который Вы используете в свойстве IndexName. Например, если таблица Customer имеет вторичный индекс по полю City, тогда Вы должны установить свойство IndexName равным имени индекса. Когда Вы будете искать по этому полю, Вы должны написать:
    Table1.IndexName := ’CityIndex’;
    Table1.Active := True;
    Table1.SetKey;
    Table1.FieldByName(’City’).AsString := Edit1.Text;
    Table1.GotoKey;
    Запомните: поиск не будет выполняться, если Вы не назначите правильно индекс (св-во IndexName). Кроме того, Вы должны обратить внимание, что IndexName - это свойство TTable, и не присутствует в других прямых потомках TDataSet или TDBDataSet.

    Когда Вы ищете некоторое значение в БД, всегда существует вероятность того, что поиск окажется неудачным. В таком случае Delphi будет автоматически вызывать exception, но если Вы хотите обработать ошибку сами, то могли бы написать примерно такой код:

    procedure TSearchDemo.SearchClick(Sender: TObject);

    begin

    Cust.SetKey;

    Cust.FieldByName('CustNo').AsString:= CustNoEdit.Text;

    if not Cust.GotoKey then

    raise Exception.CreateFmt('Cannot find CustNo %g',

    [CustNo]);

    end;

    В коде, показанном выше, либо неверное присвоение номера, либо неудача поиска автоматически приведут к сообщению об ошибке ‘Cannot find CustNo %g’.

    Иногда требуется найти не точно совпадающее значение, а близкое к нему, для этого следует вместо GotoKey

    пользоваться методом GotoNearest.

    Использование TDataSource для проверки состояния БД:

  • TDataSource имеет три ключевых события, связанных с состоянием БД

  • OnDataChange
    OnStateChange
    OnUpdateData
    OnDataChange
    происходит всякий раз, когда Вы переходите на новую запись, или состояние DataSet сменилось с dsInactive на другое, или начато редактирование. Другими словами, если Вы вызываете Next, Previous, Insert, или любой другой запрос, который должен привести к изменению данных, связанных с текущей записью, то произойдет событие OnDataChange. Если в программе нужно определить момент, когда происходит переход на другую запись, то это можно сделать в обработчике события OnDataChange:
    procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
    begin
    if DataSource1.DataSet.State = dsBrowse then begin
    DoSomething;
    end;
    end;
    Событие OnStateChange
    событие происходит всякий раз, когда изменяется текущее состояние DataSet. DataSet всегда знает, в каком состоянии он находится. Если Вы вызываете Edit, Append или Insert, то TTable знает, что он теперь находится в режиме редактирования (dsEdit или dsInsert). Аналогично, после того, как Вы делаете Post, то TTable знает что данные больше не редактируется, и переключается обратно в режим просмотра (dsBrowse).
    Dataset имеет шесть различных возможных состояний, каждое из которых включено в следующем перечисляемом типе:
    TDataSetState = (dsInactive, dsBrowse, dsEdit, dsInsert,
    dsSetKey, dsCalcFields);
    В течение обычного сеанса работы, БД часто меняет свое состояние между Browse, Edit, Insert и другими режимами. Если Вы хотите отслеживать эти изменения, то Вы можете реагировать на них написав примерно такой код:
    procedure TForm1.DataSource1StateChange(Sender: TObject);
    var
    S: String;
    begin
    case Table1.State of
    dsInactive: S := 'Inactive';
    dsBrowse: S := 'Browse';
    dsEdit: S := 'Edit';
    dsInsert: S := 'Insert';
    dsSetKey: S := 'SetKey';
    dsCalcFields: S := 'CalcFields';
    end;
    Label1.Caption := S;
    end;
    OnUpdateData
    событие происходит перед тем, как данные в текущей записи будут обновлены. Например, OnUpdateEvent будет происходить между вызовом Post и фактическим обновлением информации на диске.

    События, генерируемые TDataSource могут быть очень полезны. Иллюстрацией этого служит следующий пример. Эта программа работает с таблицей COUNTRY, и включает TTable, TDataSource, пять TEdit, шесть TLlabel, восемь кнопок и панель. Действительное расположение элементов показано на рис.11. Обратите внимание, что шестой TLabel расположен на панели внизу главной формы.

    Использование TDataSource для проверки состояния БД:


    Рис.11: Программа STATE показывает, как отслеживать текущее состояние таблицы.

    Для всех кнопок напишите обработчики, вроде:

    procedure TForm1.FirstClick(Sender: TObject);

    begin

    Table1.First;

    end;

    В данной программе есть одна маленькая хитрость, которую Вы должны понять, если хотите узнать, как работает программа. Так как есть пять отдельных редакторов TEdit на главной форме, то хотелось бы иметь некоторый способ обращаться к ним быстро и легко. Один простой способ состоит в том, чтобы объявить массив редакторов:

    Edits: array[1..5] of TEdit;

    Чтобы заполнить массив, Вы можете в событии OnCreate главной формы написать:

    procedure TForm1.FormCreate(Sender: TObject);

    var

    i: Integer;

    begin

    for i := 1 to 5 do

    Edits[i] := TEdit(FindComponent('Edit' + IntToStr(i)));

    Table1.Open;

    end;

    Код показанный здесь предполагает, что первый редактор, который Вы будете использовать назовем Edit1, второй Edit2, и т.д. Существование этого массива позволяет очень просто использовать событие OnDataChange, чтобы синхронизировать содержание объектов TEdit с содержимом текущей записи в DataSet:

    procedure TForm1.DataSource1DataChange(Sender: TObject;

    Field: TField);

    var

    i: Integer;

    begin

    for i := 1 to 5 do

    Edits[i].Text := Table1.Fields[i - 1].AsString;

    end;

    Всякий раз, когда вызывается Table1.Next, или любой другой из навигационных методов, то будет вызвана процедура показанная выше. Это обеспечивает то, что все редакторы всегда содержат данные из текущей записи.

    Всякий раз, когда вызывается Post, нужно выполнить противоположное действие, то есть взять информацию из редакторов и поместить ее в текущую запись. Выполнить это действие, проще всего в обработчике события TDataSource.OnUpdateData, которое происходит всякий раз, когда вызывается Post:


    procedure TForm1.DataSource1UpdateData(Sender: TObject);

    var

    i: Integer;

    begin

    for i := 1 to 5 do

    Table1.Fields[i - 1].AsString := Edits[i].Text;

    end;

    Программа будет автоматически переключатся в режим редактирования каждый раз, когда Вы вводите что-либо в одном из редакторов. Это делается в обработчике события OnKeyDown (укажите этот обработчик ко всем редакторам):

    procedure TForm1.Edit1KeyDown(Sender: TObject;

    var Key: Word; Shift: TShiftState);

    begin

    if DataSource1.State <> dsEdit then

    Table1.Edit;

    end;

    Этот код показывает, как Вы можете использовать св-во State

    DataSource, чтобы определить текущий режим DataSet.

    Обновление метки в статусной панели происходит при изменении состояния таблицы:

    procedure TForm1.DataSource1StateChange(Sender: TObject);

    var

    s : String;

    begin

    case DataSource1.State of

    dsInactive : s:='Inactive';

    dsBrowse : s:='Browse';

    dsEdit : s:='Edit';

    dsInsert : s:='Insert';

    dsSetKey : s:='SetKey';

    dsCalcFields : s:='CalcFields';

    end;

    Label6.Caption:=s;

    end;

    Данная программа является демонстрационной и ту же задачу можно решить гораздо проще, если использовать объекты TDBEdit.

    Класс TDataBase

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



  • Создания постоянного соединения с базой данных


  • Определения собственного диалога при соединении с базой данных (опрос пароля)


  • Создания локального псевдонима базы данных


  • Изменения параметров при соединении


  • Управления транзакциями

  • TDataBase является невидимым во время выполнения объектом. Он находится на странице “Data Access” Палитры Компонент. Для включения в проект TDataBase нужно “положить” его на главное окно вашей программы.
    Создание постоянного соединения с базой данных
    Если вы работаете с базой данных, то перед началом работы выполняется процедура соединения с этой базой. В процедуру соединения, кроме прочего, входит опрос имени и пароля пользователя (кроме случая работы с локальными таблицами Paradox и dBase через IDAPI). Если в программе не используется TDataBase, то процедура соединения выполняется при открытии первой таблицы из базы данных. Соединение с базой данных обрывается, когда в программе закрывается последняя таблицы из этой базы (это происходит в том случае, если свойство KeepConnections объекта Session установлено в False, но об этом чуть позже). Теперь, если снова открыть таблицу, то процедура установки соединения повторится и это может быть достаточно неудобно для пользователя. Чтобы соединение не обрывалось даже в том случае, когда нет открытых таблиц данной базы, можно использовать компонент типа TDataBase. В свойстве AliasName укажите псевдоним базы данных, с которой работает программа; в свойстве DatabaseName - любое имя (псевдоним БД), на которое будут ссылаться таблицы вместо старого псевдонима базы. Свойство Connected установите в True - процедура соединения с базой будет выполняться при запуске программы. И, наконец, свойство KeepConnection нужно установить в True (см. рис.1).
    Класс TDataBase

    Рис.1: Свойства TDataBase в Инспекторе объектов

    В нашем примере, после задания свойств DataBase1 нужно у всех таблиц, работающих с IBLOCAL в свойстве DatabaseName поставить Loc_IBLOCAL.

    Определение собственного диалога при соединении с базой данных

    По умолчанию при соединении с базой данных используется диалог опроса имени и пароля пользователя, показанный на рис.2

    Класс TDataBase


    Рис.2: Диалог авторизации пользователя

    При желании можно изменить внешний вид диалога или вообще его отменить. Для этого используются свойства и события класса TDataBase - LoginPrompt, Params и OnLogin.

    Чтобы отключить опрос имени и пароля установите свойство LoginPrompt в False. При этом в свойстве Params требуется в явном виде (во время дизайна либо во время выполнения) указать имя и пароль пользователя. Например, в программе можно написать (до момента соединения с базой, например в событии для Form1 OnCreate) :

    DataBase1.LoginPrompt:=False;

    DataBase1.Params.Clear;

    DataBase1.Params.Add(‘USER NAME=SYSDBA’);

    DataBase1.Params.Add(‘PASSWORD=masterkey’);

    DataBase1.Connected:=True;

    Чтобы использовать свой собственный диалог, в котором можно опрашивать не только имя и пароль пользователя, но и, например, сетевой протокол - создайте обработчик события OnLogin для DataBase1:

    procedure TForm1.Database1Login(Database: TDatabase;

    LoginParams: TStrings);

    begin

    Form2.ShowModal;

    if Form2.ModalResult = mrOK then

    with LoginParams do begin

    Values['USER NAME'] := User_Name;

    Values['PASSWORD'] := User_Pass;

    end;

    end;

    Здесь Form2

    - новое окно-диалог для ввода имени и пароля, User_Name и User_Pass - строки, куда сохраняются введенные имя и пароль.

    Создание локального псевдонима базы данных

    Обычно, псевдоним базы данных(Alias) определяется в утилите конфигурации BDE и информация о нем сохраняется в файле конфигурации IDAPI.CFG. Однако, в программе можно использовать не только ранее определенный в утилите конфигурации BDE псевдоним базы данных, но и так называемый локальный (т.е. видимый только внутри данной программы) псевдоним. Это иногда бывает нужно, например, для того, чтобы обезопасить программу в случае удаления используемого псевдонима из файла конфигурации BDE.


    Для того, чтобы создать локальный псевдоним БД, положите на главное окно проекта компонент DataBase1. Дальнейшие действия можно выполнить с помощью Инспектора Объектов, но удобнее это сделать через редактор компонент. Щелкните дважды мышкой на DataBase1 - появится диалог, показанный на рис.3

    Класс TDataBase


    Рис.3: Редактор компоненты класса TDataBase

    В этом диалоге требуется указать имя базы данных - это будет ее локальный псевдоним, на который ссылаются таблицы (свойство DatabaseName); тип драйвера (в нашем примере это INTRBASE); а также параметры, используемые при соединении с базой данных. Получить список параметров в поле “Parameter Overrides” можно по нажатию кнопки “Defaults”. Набор параметров зависит от типа БД, с которой вы работаете. Этим параметрам нужно присвоить требуемые значения - указать путь к серверу, имя пользователя и т.д. После выхода из редактора компонент имя, указанное в поле “Name” появится в списке имен баз данных для компонент типа TDataSet (TTable, TQuery etc.).

    Изменение параметров при соединении

    Иногда требуется изменить определенные в утилите конфигурации BDE параметры, используемые при установлении соединения с БД. Это можно сделать во время дизайна с помощью диалога, показанного на рис.3, в поле “Parameter Overrides”. Либо во время выполнения программы (до попытки соединения) прямым присвоением свойству Params объекта DataBase1:

    DataBase1.Params.Add(‘LANGDRIVER=ancyrr’);

    Управление транзакциями

    TDataBase позволяет начать в БД транзакцию (метод StartTransaction), закончить (Commit) или откатить ее (RollBack). Кроме того, можно изменять уровень изоляции транзакций (свойство TransIsoltion).

    TransIsolation Oracle Sybase and Informix InterBase

    Microsoft SQL

    Dirty read Read committed Read committed Dirty Read Read committed

    Read committed(Default) Read committed Read committed Read committed Read committed

    Repeatable read Repeatable read Read committed Repeatable Read Repeatable Read

    “Dirty Read” - внутри вашей текущей транзакции видны все изменения, сделанные другими транзакциями, даже если они еще не завершились по Commit. “Read Committed” - видны только “закоммитченные” изменения, внесенные в базу. “Repeatable Read” - внутри транзакции видны те данные, что были в базе на момент начала транзакции, даже если там на самом деле уже имеются изменения.






  • Класс TDataSet

  • TDataSet класс - один из наиболее важных объектов БД. Чтобы начать работать с ним, Вы должны взглянуть на следующую иерархию:

  • TDataSet
    |
    TDBDataSet
    |
    |-- TTable
    |-- TQuery
    |-- TStoredProc
    TDataSet содержит абстрактные методы там, где должно быть непосредственное управление данными. TDBDataSet знает, как обращаться с паролями и то, что нужно сделать, чтобы присоединить Вас к определенной таблице. TTable знает (т.е. уже все абстрактные методы переписаны), как обращаться с таблицей, ее индексами и т.д.
    Как Вы увидите в далее, TQuery имеет определенные методы для обработки SQL запросов.
    TDataSet - инструмент, который Вы будете использовать чтобы открыть таблицу, и перемещаться по ней. Конечно, Вы никогда не будете непосредственно создавать объект типа TDataSet. Вместо этого, Вы будете использовать TTable, TQuery или других потомков TDataSet (например, TQBE). Полное понимание работы системы, и точное значение TDataSet, будут становиться все более ясными по мере прочтения этой главы.
    На наиболее фундаментальном уровне, Dataset это просто набор записей, как изображено на рис.1
    Класс TDataSet

    Рис.1: Любой dataset состоит из ряда записей (каждая содержит N полей) и указатель на текущую запись.
    В большинстве случаев dataset будет иметь a прямое, один к одному, соответствие с физической таблицей, которая существует на диске. Однако, в других случаях Вы можете исполнять запрос или другое действие, возвращающие dataset, который содержит либо любое подмножество записей одной таблицы, либо объединение (join) между несколькими таблицами. В тексте будут иногда использоваться термины DataSet и TTable как синонимы.
    Обычно в программе используются объекты типа TTable или TQuery, поэтому в следующих нескольких главах будет предполагаться существование объекта типа TTable называемого Table1.
    Итак, самое время начать исследование TDataSet. Как только Вы познакомитесь с его возможностями, Вы начнете понимать, какие методы использует Delphi для доступа к данным, хранящимся на диске в виде БД. Ключевой момент здесь - не забывать, что почти всякий раз, когда программист на Delphi открывает таблицу, он будет использовать TTable или TQuery, которые являются просто некоторой надстройкой над TDataSet.

    Класс TPropertyEditor

    Прежде, чем писать свой собственный Редактор Свойств, нужно разобраться в базовом классе TPropertyEditor:
    TPropertyEditor = class
    private
    FDesigner: TFormDesigner;
    FPropList: PInstPropList;
    FPropCount: Integer;
    constructor Create(ADesigner: TFormDesigner; APropCount: Integer);
    function GetPrivateDirectory: string;
    procedure SetPropEntry(Index: Integer; AInstance: TComponent;
    APropInfo: PPropInfo);
    protected
    function GetPropInfo: PPropInfo;
    function GetFloatValue: Extended;
    function GetFloatValueAt(Index: Integer): Extended;
    function GetMethodValue: TMethod;
    function GetMethodValueAt(Index: Integer): TMethod;
    function GetOrdValue: Longint;
    function GetOrdValueAt(Index: Integer): Longint;
    function GetStrValue: string;
    function GetStrValueAt(Index: Integer): string;
    procedure Modified;
    procedure SetFloatValue(Value: Extended);
    procedure SetMethodValue(const Value: TMethod);
    procedure SetOrdValue(Value: Longint);
    procedure SetStrValue(const Value: string);
    public
    destructor Destroy; override;
    procedure Activate; virtual;
    function AllEqual: Boolean; virtual;
    procedure Edit; virtual;
    function GetAttributes: TPropertyAttributes; virtual;
    function GetComponent(Index: Integer): TComponent;
    function GetEditLimit: Integer; virtual;
    function GetName: string; virtual;
    procedure GetProperties(Proc: TGetPropEditProc);virtual;
    function GetPropType: PTypeInfo;
    function GetValue: string; virtual;
    procedure GetValues(Proc: TGetStrProc); virtual;
    procedure Initialize; virtual;
    procedure SetValue(const Value: string); virtual;
    property Designer: TFormDesigner read FDesigner;
    property PrivateDirectory: string read GetPrivateDirectory;
    property PropCount: Integer read FPropCount;
    property Value: string read GetValue write SetValue;
    end;
    Методы, приведенные ниже, можно переопределять (override) для изменения поведения Редактора свойств. ( "SetXxxValue" используется для представления одного из методов SetFloatValue, SetMethodValue, SetOrdValue или SetStrValue. "GetXxxValue" обозначает GetFloatValue, GetMethodValue, GetOrdValue или GetStrValue)




  • Activate


  • Вызывается, когда свойство выбирают в инспекторе объектов. Может быть полезно позволить некоторым атрибутам свойства определяться в каждый момент выбора этого свойства.






  • AllEqual


  • Вызывается всякий раз, когда на форме выбирается более чем один объект. Если этот метод возвращает True, то вызывается GetValue, иначе в Инспекторе Объектов показывается пустая строка. AllEqual вызывается при условии, что GetAttributes возвращает paMultiSelect.






  • Edit


  • Вызывается при нажатии кнопки '...' или по двойному щелчку мыши на свойстве. Этот метод может, к примеру, показать какое-нибудь диалоговое окно для редактирования свойства (пример - свойство Font).






  • GetAttributes


  • Возвращает необходимую Инспектору Объектов информацию для того, чтобы тот смог отобразить свойство в подходящей манере. GetAttributes возвращает множество (set) значений типа TPropertyAttributes:


  • paValueList:


  • Редактор свойств может возвращать список значений для этого свойства. Если этот атрибут установлен, то нужно определить GetValues. В Инспекторе объектов справа от свойства появится кнопка для выпадающего списка.

    paSortList: Инспектор объектов будет сортировать список, полученный от GetValues.

    paSubProperties: Свойство имеет подсвойства, которые будут показываться ниже в виде иерархии (outline). Если GetProperties будет генерировать объекты-свойства, то этот атрибут должен быть установлен.

    paDialog:

    Показывает, что метод Edit будет вызывать диалог. Если данный атрибут установлен, то появится кнопка '...' справа от свойства в Инспекторе Объектов.

    paMultiSelect: Позволяет свойству оставаться в Инспекторе Объектов, когда на форме выбрано сразу несколько объектов. Некоторые свойства не годятся для множественного выбора, например, Name.

    paAutoUpdate: Если этот атрибут установлен, то метод SetValue будет вызываться при каждом изменении, произведенном в редакторе, а не после завершения редактирования (пример - свойство Caption).

    paReadOnly:

    Значение менять нельзя.






  • GetComponent


  • Возвращает компонент под номером Index в случае множественного выбора объектов (multiselect). GetAttributes должен возвращать paMultiSelect.






  • GetEditLimit


  • Возвращает число символов, которые пользователь может ввести при редактировании свойства. По умолчанию 255 символов.






  • GetName


  • Возвращает имя свойства. По умолчанию это имя получается из информации о типе, все подчеркивания замещаются пробелами. Данный метод Вам нужно переопределять только в том случае, если имя свойства отличается от того, которое нужно отображать в Инспекторе Объектов.






  • GetProperties


  • Должен быть переопределен для вызова PropertyProc для каждого подсвойства (или вложенного свойства) редактируемого свойства и передачи нового TPropertyEdtior для каждого подсвойства. По умолчанию, PropertyProc не вызывается и подсвойства не ожидаются. TClassProperty будет передавать новый редактор свойств для каждого свойства, объявленного published в классе. TSetProperty передает новый редактор для каждого элемента множества.






  • GetPropType


  • Возвращает указатель на информацию о типе редактируемого свойства.






  • GetValue


  • Возвращает значение свойства в виде строки. По умолчанию возвращает '(unknown)'. Этот метод нужно переопределять с тем, чтобы возвращать правильное значение.






  • GetValues


  • Вызывается, если GetAttributes возвращает paValueList. Должно вызвать Proc для каждого значения, которое приемлемо для данного свойства.






  • Initialize


  • Вызывается при создании Редактора свойств.






  • SetValue(Value)


  • Вызывается для того, чтобы установить значение свойства. Редактор свойств должен уметь разобрать строку (Value) и вызвать метод SetXxxValue. Если строка имеет некорректный формат или неверное значение, то редактор Свойств должен сгенерировать исключительную ситуацию (exception), описывающую данную проблему. SetValue может вообще проигнорировать все изменения и оставить всю обработку изменений методу Edit (как в свойстве Picture).



  • Свойства и методы полезные при создании нового класса Редактора свойств:





  • PrivateDirectory (свойство)


  • Это директория, в которой находится .EXE, либо рабочая директория, указанная в DELPHI.INI. Если редактор должен сохранить какую-то информацию (установки), то лучше в этой директории.






  • Value (свойство)


  • Текущее значение свойства, то же самое возвращает GetValue.





  • Modified (метод)


  • Вызывается для того, чтобы показать, что значение свойства изменилось. Методы SetXxxValue вызывают Modified автоматически.






  • GetXxxValue (метод)


  • Возвращает значение первого из редактируемых свойств.






  • SetXxxValue (метод)


  • Устанавливает значения свойства для всех выбранных объектов.






  • Компонент TMediaPlayer

    Для начала давайте создадим новый проект, затем поместим компонент TMediaPlayer (стр. System Палитры) на форму, как показано на рис.1.
    Компонент TMediaPlayer

    Рис.1: Компонент TMediaPlayer на форме.
    Компонент TMediaPlayer оформлен, как панель управления устройством с кнопками. Как и на магнитофоне, здесь есть кнопки “воспроизведение”, “перемотка”, “запись” и др.
    Поместив компонент на форму, Вы увидите, что Инспектор Объектов содержит свойство "FileName" (см. рис.2). Щелкните дважды
    Компонент TMediaPlayer

    Рис.2: Свойства TMediaPlayer в Инспекторе Объектов
    на этом свойстве и выберите имя файла с расширением AVI, WAV или
    MID. На рис.2 выбран AVI файл DELPHI.AVI. Далее нужно установить свойство AutoOpen в True.
    После выполнения этих шагов программа готова к запуску. Запустив программу, нажмите зеленую кнопку “воспроизведение” (крайняя слева) и Вы увидите видеоролик (если выбрали AVI) или услышите звук (если выбрали WAV или MID). Если этого не произошло или появилось сообщение об ошибке, то возможны два варианта:


  • Вы ввели неправильное имя файла.


  • Вы не настроили правильным образом мультимедиа в Windows. Это означает, что либо у Вас нет соответствующего ”железа”, либо не установлены нужные драйверы. Установка и настройка драйверов производится в Control Panel, требования к “железу” приводятся в любой книге по мультимедиа (нужна звуковая карта, например совместимая с Sound Blaster).

  • Итак, Вы имеете возможность проигрывать AVI, MIDI и WAVE файлы просто указывая имя файла.
    Еще одно важное свойство компонента TMediaPlayer - Display. Изначально оно не заполнено и видео воспроизводится в отдельном окошке. Однако, в качестве экрана для показа ролика можно использовать, например, панель. На форму нужно поместить компонент TPanel, убрать текст из св-ва Caption. Далее, для TMediaPlayer, в свойстве Display выбрать из списка Panel1. После этого надо запустить программу и нажать кнопку “воспроизведение” (см. рис.3)
    Компонент TMediaPlayer

    Рис.3: Воспроизведение AVI на панели.




  • Компонент TReport

  • Библиотека визуальных компонент Delphi включает объект TReport. TReport обеспечивает вызов из программы Delphi программы ReportSmith runtime и печати отчета. TReport расположен на странице Data Access Палитры Компонент.


  • Краткий Обзор

    В этой главе Вы узнаете некоторые основные понятия о запросах (queries) и транзакциях. Это достаточно широкие понятия, поэтому обсуждение разбито на следующие основные части:

  • Объект TQuery.


  • Использование SQL с локальным и удаленным серверами (Select, Update, Delete и Insert).


  • Использование SQL для создания объединения (joins), связанных курсоров (linked cursors) и программ, которые ведут поиск заданных записей.

  • Сокращение SQL означает Structured Query Language - Язык Структурированных Запросов, и обычно произноситься либо как "Sequel" либо " Ess Qu El”. Однако, как бы Вы его ни произносили, SQL - это мощный язык БД, который легко доступен из Delphi, но который отличается от родного языка Delphi. Delphi может использовать утверждения SQL для просмотра таблиц, выполнять объединение таблиц, создавать отношения один-ко-многим, или исполнить почти любое действие, которое могут сделать ваши основные инструменты БД. Delphi поставляется с Local SQL, так что Вы можете выполнять запросы SQL при работе с локальными таблицами, без доступа к SQL серверу.
    Delphi обеспечивает поддержку “pass through SQL”, это означает то, что Вы можете составлять предложения SQL и посылать их непосредственно серверам Oracle, Sybase, Inrterbase и другим. “Pass through SQL” - это мощный механизм по двум причинам:


  • Большинство серверов могут обрабатывать SQL запросы очень быстро, а это означает, что используя SQL для удаленных данных, Вы получите ответ очень быстро.


  • Есть возможность составлять SQL запросы, которые заставят сервер исполнить специализированные задачи, недоступные через родной язык Delphi.

  • Перед чтением этой статьи Вы должны иметь, по крайней мере, элементарное понятие о серверах и различиях между локальными и удаленными (remote) данными.




  • Важной составной частью приложения является вывод данных на печать - получение отчета. В пакет Delphi входит средство для генерации и печати отчетов - ReportSmith. Вы можете объединить отчет с приложениями Delphi. Также, библиотека визуальных компонент Delphi включает специальный компонент TReport. В данном уроке показано, как использовать компоненту TRepor и рассмотрены основные принципы проектирования отчетов в ReportSmith.

  • ReportSmith

  • Borland ReportSmith является инструментом для получения отчетов и интегрирован в среду Delphi. Он может быть вызван непосредственно из меню Tools. Отчет может быть добавлен к приложениям Delphi, для этого есть компонента TReport на странице Data Access Палитры Компонентов. Войти в ReportSmith можно, нажав правую кнопка мыши на компоненте TReport и выбрав пункт контекстного меню (popup menu) или двойным щелчком левой кнопки мыши на компоненте TReport на форме.
    Отчеты могут быть созданы для SQL БД или локальных БД и не требуют знания сложных команд БД. Интерфейс ReportSmith использует стандартные инструменты Windows 3.1 типа tool bar, formatting ribbon, и “drag and drop”. Если пользователь уже знаком с интерфейсом стандартных Windows-программ, типа Word for Windows или Quattro Pro for Windows, ему будет “знаком” и интерфейс ReportSmith. ReportSmith предлагает 4 типа отчетов: Табличный, Кросс-таблица(CrossTab), Форма(Form) и Наклейка(Label).
    ReportSmith использует концепцию “живых данных”, т.е. работа происходит с настоящими данными все время, а не только тогда, когда запускается просмотр (preview). Кроме этого, ReportSmith легко работает с чрезвычайно большими БД при помощи адаптивной технологии управления памятью. В ReportSmith можно управлять тем, где сохраняется результат выборки данных из БД: в локальный памяти клиентской PC, на жестком диске клиентской PC, или на сервере.
    ReportSmith включает поддержку:


  • Встроенных шаблонов и стилей

  • Отчетов типа перекрестных таблиц (Cross tab)

  • Отчетов в виде почтовых адресов

  • Вычисляемых полей и полей суммирования

  • Многоуровневой сортировки и группировки

  • Многоуровневых отчетов (master-details)

  • Отчеты, созданные с помощью ReportSmith могут распространяться бесплатно вместе с ReportSmith runtime-модулем. Конечные пользователи могут купить полную версию ReportSmith, для того чтобы создать свои собственные отчеты. Информация о ReportSmith доступна в руководстве ReportSmith for Windows - Creating Reports из коробки Delphi.




  • о том, что лучше один

    Borland Delphi

    или о том, что лучше один раз увидеть, чем сто раз услышать.

    Допускаю, что вы пока еще не слышали этого названия. Хотя, быть может, и слышали. Delphi - это греческий город, где жил дельфийский оракул. И этим именем был назван новый программный продукт с феноменальными характеристиками.

    Hадо отметить, что к моменту выхода продукта обстановка вокруг компании Borland складывалась не лучшим для нее образом. Поговаривали о возможной перепродаже компании, курс акций компании неудержимо катился вниз. Сейчас уже можно без всяких сомнений утверждать, что период трудностей позади. Hеверно, конечно, было бы говорить, что только Delphi явился причиной восстановления компании; кроме Delphi, у Borland появились и другие замечательные продукты, так же, как и Delphi, основывающиеся на новых, появившихся недавно у компании Borland, технологиях. Я имею в виду новые BDE 2.0, BC++ 4.5, Paradox for Windows 5.0, dBase for Windows 5.0, BC++ 2.0 for OS/2.

    Тем не менее, именно Delphi стал тем продуктом, на примере которого стало ясно, что у Borland есть еще порох в пороховницах, и что один единственный продукт может настолько удачно сочетать несколько передовых технологий.

    Delphi. Основные характеристики продукта.

    Delphi - это комбинация нескольких важнейших технологий:



  • Высокопроизводительный компилятор в машинный код


  • Объектно-ориентированная модель компонент


  • Визуальное (а, следовательно, и скоростное) построение приложений из программных прототипов


  • Масштабируемые средства для построения баз данных


  • Компилятор в машинный код

    Компилятор, встроенный в Delphi, обеспечивает высокую производительность, необходимую для построения приложений в архитектуре “клиент-сервер”. Этот компилятор в настоящее время является самым быстрым в мире, его скорость компиляции составляет свыше 120 тысяч строк в минуту на компьютере 486DX33. Он предлагает легкость разработки и быстрое время проверки готового программного блока, характерного для языков четвертого поколения (4GL) и в то же время обеспечивает качество кода, характерного для компилятора 3GL. Кроме того, Delphi обеспечивает быструю разработку без необходимости писать вставки на Си или ручного написания кода (хотя это возможно).


    В процессе построения приложения разработчик выбирает из палитры компонент готовые компоненты как художник, делающий крупные мазки кистью. Еще до компиляции он видит результаты своей работы - после подключения к источнику данных их можно видеть отображенными на форме, можно перемещаться по данным, представлять их в том или ином виде. В этом смысле проектирование в Delphi мало чем отличается от проектирования в интерпретирующей среде, однако после выполнения компиляции мы получаем код, который исполняется в 10-20 раз быстрее, чем то же самое, сделанное при помощи интерпретатора. Кроме того, компилятор компилятору рознь, в Delphi компиляция производится непосредственно в родной машинный код, в то время как существуют компиляторы, превращающие программу в так называемый p-код, который затем интерпретируется виртуальной p-машиной. Это не может не сказаться на фактическом быстродействии готового приложения.

    Объектно-ориентированная модель программных компонент

    Основной упор этой модели в Delphi делается на максимальном реиспользовании кода. Это позволяет разработчикам строить приложения весьма быстро из заранее подготовленных объектов, а также дает им возможность создавать свои собственные объекты для среды Delphi. Никаких ограничений по типам объектов, которые могут создавать разработчики, не существует. Действительно, все в Delphi написано на нем же, поэтому разработчики имеют доступ к тем же объектам и инструментам, которые использовались для создания среды разработки. В результате нет никакой разницы между объектами, поставляемыми Borland или третьими фирмами, и объектами, которые вы можете создать.

    В стандартную поставку Delphi входят основные объекты, которые образуют удачно подобранную иерархию из 270 базовых классов. Для начала - неплохо. Но если возникнет необходимость в решении какой-то специфической проблемы на Delphi, советуем, прежде чем попытаться начинать решать проблему “с нуля”, просмотреть список свободно распространяемых или коммерческих компонент, разработанных третьими фирмами, количество этих фирм в настоящее время превышает число 250, хотя, возможно, я не обо всех знаю. Скептики, возможно, не поверят мне, когда я скажу, что на Delphi можно одинаково хорошо писать как приложения к корпоративным базам данных, так и, к примеру, игровые программы. Тем не менее, это так. Во многом это объясняется тем, что традиционно в среде Windows было достаточно сложно реализовывать пользовательский интерфейс. Событийная модель в Windows всегда была сложна для понимания и отладки. Но именно разработка интерфейса в Delphi является самой простой задачей для программиста.


    Быстрая разработка работающего приложения из прототипов

    о том, что лучше один


    Игровая программа Rendzu была собрана моим коллегой из готовых кусков за рабочий день, причем большая часть времени была посвящена прихорашиванию и приукрашиванию. Screen Saver в виде прыгающих часиков был также изготовлен на Delphi за весьма незначительное время. Теперь эти часики украшают почти каждую IBM-совместимую машину в нашем Демо-центре клиент-серверных технологий. Конечно, на разработку серьезной информационно-поисковой системы в архитектуре клиент-сервер может уйти гораздо большее время, чем на разработку программы-игрушки. Тем не менее многие наши коллеги, до Delphi

    программировавшие на других языках, утверждают, что на Delphi скорость изготовления сложного проекта выше раз в 10.

    о том, что лучше один


    Cреда Delphi включает в себя полный набор визуальных инструментов для скоростной разработки приложений (RAD - rapid application development), поддерживающей разработку пользовательского интерфейса и подключение к корпоративным базам данных. VCL - библиотека визуальных компонент, включает в себя стандартные объекты построения пользовательского интерфейса, объекты управления данными, графические объекты, объекты мультимедиа, диалоги и объекты управления файлами, управление DDE и OLE. Единственное, что можно поставить в вину Delphi, это то, что готовых компонент, поставляемых Borland, могло бы быть и больше. Однако, разработки других фирм, а также свободно распространяемые программистами freeware-компоненты уже восполнили этот недостаток. Постойте, - скажете вы, ведь это уже было. Да, это было в Visual Basic.

    Соответствующий стандарт компонент назывался VBX. И этот стандарт так же поддерживается в Delphi. Однако, визуальные компоненты в Delphi обладают большей гибкостью. Вспомним, в чем была проблема в VB. Прикладной программист программировал, вообще говоря, в среде языка бэйсик. А компоненты в стандарте VBX готовили ему его коллеги-профессионалы на С++.

    VBX’ы приходили, “как есть”, и ни исправить, ни добавить ничего было нельзя.


    А для изготовления VBX надо было осваивать “кухню” языка C++. В Delphi визуальные компоненты пишутся на объектном паскале, на том же паскале, на котором пишется алгоритмическая часть приложения. И визуальные компоненты Delphi получаются открытыми для надстройки и переписывания. Чувствуете разницу?

    Масштабируемые средства для построения баз данных

    Объекты БД в Delphi основаны на SQL и включают в себя полную мощь Borland Database Engine. В состав Delphi также включен Borland SQL Link, поэтому доступ к СУБД Oracle, Sybase, Informix и InterBase происходит с высокой эффективностью. Кроме того, Delphi включает в себя локальный сервер Interbase для того, чтобы можно было разработать расширяемые на любые внешние SQL-сервера приложения в офлайновом режиме. Разработчик в среде Delphi, проектирующий информационную систему для локальной машины (к примеру, небольшую систему учета медицинских карточек для одного компьютера), может использовать для хранения информации файлы формата .dbf (как в dBase или Clipper) или .db (Paradox). Если же он будет использовать локальный InterBase for Windows 4.0 (это локальный SQL-сервер, входящий в поставку), то его приложение безо всяких изменений будет работать и в составе большой системы с архитектурой клиент-сервер.

    Вот она - масштабируемость на практике - одно и то же приложение можно использовать как для локального, так и для более серьезного клиент-серверного вариантов.

    Delphi - два варианта поставки

    Я уже упомянул о технологиях, которые объединяет Delphi. Теперь можно перейти к описанию собственно продукта. Что лежит внутри в коробке, и чем может воспользоваться программист при разработке прикладной системы? Выпущены две версии Delphi - одна (Delphi Client-Server) адресована для разработчиков приложений в архитектуре “клиент-сервер”, а другая (Delphi for Windows) предназначена для остальных программистов. Приложения, разработанные при помощи Delphi, можно использовать без выплаты royalty-процентов и без оплаты runtime- лицензий.


    Клиент-серверная версия Delphi

    Она адресована корпоративным разработчикам, желающим разрабатывать высокопроизводительные приложения для рабочих групп и корпоративного применения.

    Клиент-серверная версия включает в себя следующие особенности:





  • SQL Links: специально написанные драйвера для доступа к Oracle, Sybase, Informix, InterBase




  • Локальный сервер InterBase: SQL-сервер для Windows 3.1. СУБД для разработки в корпоративных приложений на компьютере, не подключенном к локальной сети.




  • ReportSmith Client/server Edition: генератор отчетов для SQL-серверов




  • Team Development Support: предоставляет версионный контроль при помощи PVCS компании Intersolve (приобретается отдельно) или при помощи других программных продуктов версионного контроля




  • Visual Query Builder - это средство визуального построения SQL-запросов




  • лицензия на право распространения приложений в архитектуре клиент-сервер, изготовленных при помощи Delphi




  • исходные тексты всех визуальных компонент


  • Delphi for Windows

    Delphi for Windows представляет из себя подмножество Delphi Client-Server и предназначен для разработчиков высокопроизводительных персональных приложений, работающих с локальными СУБД типа dBase и Paradox.Delphi Desktop Edition предлагает такую же среду для быстрой разработки и первоклассный компилятор как и клиент-серверная версия (Client/Server Edition). Эта среда позволяет разработчику быстро изготавливать персональные приложения, работающие с персональными СУБД типа dBase и Paradox. Delphi позволяет также создавать разработчику DLL, которая может быть вызвана из Paradox, dBase, C++ или каких-нибудь других готовых программ.

    В Delphi for Windows, как и в Delphi Client-Server, входят





  • компилятор Object Pascal (этот язык является расширением языка Borland Pascal 7.0)




  • генератор отчетов ReportSmith 2.5 (у которого, правда, отсутствует возможность работы с SQL-серверами)




  • среда визуального построителя приложений





  • библиотека визуальных компонент




  • Локальный сервер InterBase


  • RAD Pack for Delphi

    В этом обзоре стоит упомянуть еще один продукт, выпущенный компанией Borland для Delphi. В RAD Pack for Delphi входит набор полезных дополнений, которые помогут разработчику при освоении и использовании Delphi. Это учебник по объектному паскалю, интерактивный отладчик самой последней версии, Borland Visual Solutions Pack (набор VBX для реализации редакторов, электронных таблиц, коммуникационные VBX, VBX с деловой графикой и т.п.), Resource WorkShop для работы с ресурсами Borland Pascal 7.0, а также дельфийский эксперт для преобразования ресурсов BP 7.0 в формы Delphi.

    Для кого предназначен Delphi

    В первую очередь Delphi предназначен для профессионалов-разработчиков корпоративных информационных систем. Может быть, здесь следует пояснить, что конкретно имеется в виду. Не секрет, что некоторые удачные продукты, предназначенные для скоростной разработки приложений (RAD - rapid application development) прекрасно работают при изготовлении достаточно простых приложений, однако, разработчик сталкивается с непредвиденными сложностями, когда пытается сделать что-то действительно сложное. Бывает, что в продукте вскрываются присущие ему ограничения только по прошествии некоторого времени.

    Delphi такие ограничения не присущи. Хорошее доказательство тому - это тот факт, что сам Delphi разработан на Delphi. Можете делать выводы. Однако Delphi предназначен не только для программистов-профессионалов. Я читал в электронной конференции совершенно неожиданные для меня письма, где учителя, врачи, преподаватели ВУЗов, бизнесмены, все те, кто используют компьютер с чисто прикладной целью, рассказывали о том, что приобрели Delphi for Windows для того, чтобы быстро решить какие-то свои задачи, не привлекая для этого программистов со стороны. В большинстве случаев им это удается. Поразительный факт - журнал Visual Basic Magazine присудил свою премию Delphi for Windows.

    Руководители предприятий, планирующие выделение средств на приобретение программных продуктов, должны быть уверены в том, что планируемые инвестиции окупятся. Поэтому одним из оцениваемых факторов должен быть вопрос - а легко ли найти специалиста по Delphi и сколько будет стоить его обучение, сколько времени специалист затратит на овладение продуктом. Ответ здесь получить весьма просто - любой программист на паскале способен практически сразу профессионально освоить Delphi. Специалисту, ранее использовавшему другие программные продукты, придется труднее, однако самое первое работающее приложение он сможет написать в течение первого же часа работы на Delphi. И, конечно же, открытая технология Delphi является мощным гарантом того, что инвестици, сделанные в Delphi, будут сохранены в течение многих лет.


    Некоторые особенности Delphi

    Локальный сервер InterBase - следует заметить, что этот инструмент предназначен только для автономной отладки приложений. В действительности он представляет из себя сокращенный вариант обработчика SQL-запросов InterBase, в который не включены некоторые возможности настоящего сервера InterBase. Отсутствие этих возможностей с лихвой компенсируется преимуществом автономной отладки программ.

    Team Development Support - средство поддержки разработки проекта в группе. Позволяет существенно облегчить управление крупными проектами. Это сделано в виде возможности подключения такого продукта как Intersolve PVCS 5.1 непосредственно к среде Delphi.

    Высокопроизводительный компилятор в машинный код - в отличие от большинства Паскаль-компиляторов, транслирующих в p-код, в Delphi программный текст компилируется непосредственно в машинный код, в результате чего Delphi- приложения исполняются в 10-20 раз быстрее (особенно приложения, использующие математические функции). Готовое приложение может быть изготовлено либо в виде исполняемого модуля, либо в виде динамической библиотеки, которую можно использовать в приложениях, написанных на других языках программирования.

    Открытая компонентная архитектура

    Благодаря такой архитектуре приложения, изготовленные при помощи Delphi, работают надежно и устойчиво. Delphi поддерживает использование уже существующих объектов, включая DLL, написанные на С и С++, OLE сервера, VBX, объекты, созданные при помощи Delphi. Из готовых компонент работающие приложения собираются очень быстро. Кроме того, поскольку Delphi имеет полностью объектную ориентацию, разработчики могут создавать свои повторно используемые объекты для того, чтобы уменьшить затараты на разработку.

    Delphi предлагает разработчикам - как в составе команды, так и индивидуальным - открытую архитектуру, позволяющую добавлять компоненты, где бы они ни были изготовлены, и оперировать этими вновь введенными компонентами в визуальном построителе. Разработчики могут добавлять CASE-инструменты, кодовые генераторы, а также авторские help’ы, доступные через меню Delphi.


    Two-way tools - однозначное соответствие между визуальным проектированием и классическим написанием текста программы Это означает, что разработчик всегда может видеть код, соответствующий тому, что он построил при помощи визуальных инструментов и наоборот.

    Визуальный построитель интерфейсов (Visual User-interface builder) дает возможность быстро создавать клиент-серверные приложения визуально, просто выбирая компоненты из соответствующей палитры.

    Библиотека визуальных компонент

    Эта библиотека объектов включает в себя стандартные объекты построения пользовательского интерфейса, объекты управления данными, графические объекты, объекты мультимедиа, диалоги и объекты управления файлами, управление DDE и OLE.

    Структурное объектно-ориентированное программирование

    Delphi использует структурный объектно-ориентированный язык (Object Pascal), который сочетает с одной стороны выразительную мощь и простоту программирования, характерную для языков 4GL, а с другой стороны эффективность языка 3GL. Программисты немедленно могут начать производить работающие приложения, и им не придется для этого изучать особенности программирования событий в Windows. Delphi полностью поддерживает передовые программные концепции включая инкапсуляцию, наследование, полиморфизм и управление событиями.

    Поддержка OLE 2.0, DDE и VBX

    Это очень важная особенность для разработчиков в среде Windows, поскольку в уже существующие Windows-приложения программист может интегрировать то, что разработает при помощи Delphi.

    Delphi: настраиваемая cреда разработчика

    о том, что лучше один


    После запуска Delphi в верхнем окне горизонтально располагаются иконки палитры компонент. Если курсор задерживается на одной из иконок, под ней в желтом прямоугольнике появляется подсказка

    Из этой палитры компонент вы можете выбирать компоненты, из которых можно строить приложения. Компоненты включают в себя как визуальные, так и логические компоненты. Такие вещи, как кнопки, поля редактирования - это визуальные компоненты; а таблицы, отчеты - это логические.


    Понятно, что поскольку в Delphi вы визуальным образом строите свою программу, все эти компоненты имеют свое графическое представление в поле форм для того, чтобы можно было бы ими соответствующим образом оперировать. Но для работающей программы видимыми остаются только визуальные компоненты. Компоненты сгруппированы на страницах палитры по своим функциям. К примеру, компоненты, представляющие Windows “common dialogs” все размещены на странице палитры с названием “Dialogs”.

    Delphi позволяет разработчикам настроить среду для максимального удобства. Вы можете легко изменить палитру компонент, инструментальную линейку, а также настраивать выделение синтаксиса цветом.

    Заметим, что в Delphi вы можете определить свою группу компонент и разместить ее на странице палитры, а если возникнет необходимость, перегруппировать компоненты или удалить неиспользуемые.

    Интеллектуальный редактор

    о том, что лучше один


    Редактирование программ можно осуществлять, используя запись и исполнение макросов, работу с текстовыми блоками, настраиваемые комбинации клавиш и цветовое выделение строк .

    Графический отладчик

    Delphi обладает мощнейшим, встроенным в редактор графическим отладчиком, позволяющим находить и устранять ошибки в коде. Вы можете установить точки останова, проверить и изменить переменные, при помощи пошагового выполнения в точности понять поведение программы. Если же требуются возможности более тонкой отладки, Вы можете использовать отдельно доступный Turbo Debugger, проверив ассемблерные инструкции и регистры процессора.

    Инспектор объектов

    о том, что лучше один


    Этот инструмент представляет из себя отдельное окно, где вы можете в период проектирования программы устанавливать значения свойств и событий объектов (Properties & Events).

    Менеджер проектов.

    о том, что лучше один


    Дает возможность разработчику просмотреть все модули в соответствующем проекте и снабжает удобным механизмом для управления проектами.

    Менеджер проектов показывает имена файлов, время/дату выбранных форм и пр.

    Можно немедленно попась в текст или форму, просто щелкнув мышкой на соответствующее имя.


    Навигатор объектов

    о том, что лучше один


    Показывает библиотеку доступных объектов и осуществляет навигацию по вашему приложению. Можно посмотреть иерархию объектов, прекомпилированные модули в библиотеке, список глобальных имен вашего кода.

    Дизайнер меню

    Можно создавать меню, сохранить созданные в виде шаблонов и затем использовать в их в любом приложении.

    Эксперты

    Это набор инструментальных программ, облегчающих проектирование и настройку Ваших приложений.

    Есть возможность подключать самостоятельно разработанные эксперты. Потенциально это та возможность, при помощи которой третьи фирмы могут расширять Delphi CASE-инструментами, разработанными специально для Delphi.

    Включает в себя:





  • Эксперт форм, работающих с базами данных




  • Эксперт стилей и шаблонов приложений




  • Эксперт шаблонов форм


  • В состав RAD Pack входит эксперт для преобразования ресурсов, изготовленных в Borland Pascal 7.0, в формы Delphi. Уже появились эксперты, облегчающие построение DLL и даже написание собственных экспертов

    Интерактивная обучающая система

    Позволяет более полно освоить Delphi. Она являются не просто системой подсказок, а показывает возможности Delphi на самой среде разработчика.

    Компоненты доступа к базам данных и визуализации данных

    Библиотека объектов содержит набор визуальных компонент, значительно упрощающих разработку приложений для СУБД с архитектурой клиент-сервер. Объекты инкапсулируют в себя нижний уровень - Borland Database Engine.

    Предусмотрены специальные наборы компонент, отвечающих за доступ к данным, и компонент, отображающих данные. Компоненты доступа к данным позволяют осуществлять соединения с БД, производить выборку, копирование данных, и т.п.

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

    Разработка приложений БД

    о том, что лучше один


    Delphi позволяет использовать библиотеку визуальных компонент для быстрого создания надежных приложений, которые легко расширяются до приложений с архитектурой клиент-сервер. Другими словами, Вы можете создать приложение, работающее с локальным сервером InterBase, а затем использовать созданное приложение, соединяясь с удаленным SQL-сервером через SQL-Links.


    Библиотека объектных Визуальных Компонент

    Компоненты, используемые при разработке в Delphi (и также собственно самим Delphi), встроены в среду разработки приложений и представляют из себя набор типов объектов, используемых в качестве фундамента при строительстве приложения.

    Этот костяк называется Visual Component Library (VCL). В VCL есть такие стандартные элементы управления, как строки редактирования, статические элементы управления, строки редактирования со списками, списки объектов.

    Еще имеются такие компоненты, которые ранее были доступны только в библиотеках третьих фирм: табличные элементы управления, закладки, многостраничные записные книжки.

    VCL содержит специальный объект, предоставлющий интерфейс графических устройств Windows, и позволяющий разработчикам рисовать, не заботясь об обычных для программирования в среде Windows деталях.

    Ключевой особенностью Delphi является возможность не только использовать визуальные компоненты для строительства приложений, но и создание новых компонент. Такая возможность позволяет разработчикам не переходить в другую среду разработки, а наоборот, встраивать новые инструменты в существующую среду. Кроме того, можно улучшить или полностью заменить существующие по умолчанию в Delphi компоненты.

    Здесь следует отметить, что обычных ограничений, присущих средам визуальной разработки, в Delphi нет. Сам Delphi написан при помощи Delphi, что говорит об отсутствии таких ограничений.

    Классы объектов построены в виде иерархии, состоящей из абстрактных, промежуточных, и готовых компонент. Разработчик может пользоваться готовыми компонентами, создавать собственные на основе абстрактных или промежуточных, а также создавать собственные объекты.

    Язык программирования Delphi базируется на Borland Object Pascal.

    Кроме того, Delphi поддерживает такие низкоуровневые особенности, как подклассы элементов управления Windows, перекрытие цикла обработки сообщений Windows, использование встроенного ассемблера.

    Формы, модули и метод разработки “Two-Way Tools”


    Формы - это объекты, в которые вы помещаете другие объекты для создания пользовательского интерфейса вашего приложения. Модули состоят из кода, который реализует функционирование вашего приложения, обработчики событий для форм и их компонент.

    Информация о формах хранится в двух типах файлов - .dfm и .pas, причем первый тип файла - двоичный - хранит образ формы и ее свойства, второй тип описывает функционирование обработчиков событий и поведение компонент. Оба файла автоматически синхронизируются Delphi, так что если добавить новую форму в ваш проект, связанный с ним файл .pas автоматически будет создан, и его имя будет добавлено в проект.

    Такая синхронизация и делает Delphi

    two-way-инструментом, обеспечивая полное соответствие между кодом и визуальным представлением. Как только вы добавите новый объект или код, Delphi устанавливает т.н. “кодовую синхронизацию” между визуальными элементами и соответствующими им кодовыми представлениями.

    Например, предположим, вы добавили описание поведения формы (соотв. обработчик событий), чтобы показывать окно сообщения по нажатию кнопки. Такое описание появляется, если дважды щелкнуть мышкой непосредственно на оъект Button в форме или дважды щелкнуть мышью на строчку OnClick на странице Events в Инспекторе объектов. В любом случае Delphi создаст процедуру или заголовок метода, куда вы можете добавить код.

    procedure TForm1.Button1Click(Sender: TObject);

    begin

    end;

    Cоздавая этот код, Delphi автоматически формирует декларацию объекта TForm1, которая содержит процедуру ButtonClick, представляющую из себя собственно обработчик события.

    TForm1 = class

    (TForm)

    Button1: Tbutton;

    procedure

    Button1Click(Sender: TObject);

    private

    { Private declarations }

    public

    { Public declarations }

    end;

    Конечно вы запросто можете решить после получения этого кода, что автоматически созданные имена Вас не устраивают, и заменить их. Например, Button1 на Warning. Это можно сделать изменив свойство Name для Button1 при помощи Инспектора объектов. Как только вы нажмете Enter, Delphi автоматически произведет соответствующую синхронизацию в коде. Так как объект TForm1 существует в коде, вы свободно можете добавлять любые другие поля, процедуры, функции или object definition. К примеру, вы можете дописать в коде свою собственную процедуру, обрабатывающую событие, а не делать это визуальным методом.


    Следующий пример показывает, как это можно сделать. Обработчик принимает аргумент типа TObject, который позволяет нам определить, если необходимо, кто инициировал событие. Это полезно в случае, когда несколько кнопок вызывают общую процедуру для обработки.

    TForm1 = class(TForm)

    Warning: TButton;

    Button1: TButton;

    procedure

    WarningClick(Sender: TObject);

    procedure

    NewHandler(Sender: TObject);

    private

    { Private declarations }

    public

    { Public declarations }

    end;

    Здесь мы имеем дело уже с второй стороной синхронизации. Визуальная среда в данном случае распознает, что новая процедура добавлена к объекту и соответствующие имена появляются в Инспекторе объектов.

    Добавление новых объектов

    Delphi - это прежде всего среда разработки, базирующаяся на использовании компонент. Поэтому вы можете добавлять совершенно новые компоненты в палитру компонент. Вы можете создавать компоненты внутри Delphi, или вводить компоненты, созданные как управляющие элементы VBX или OLE 2.0, или же вы можете использовать компоненты, написанные на С или С++ в виде dll.

    Последовательность введения новой компоненты состоит из трех шагов:





  • наследование из уже существующего типа компоненты




  • определение новых полей, свойств и методов




  • регистрация компоненты


  • Это все делается через меню Install Components

    Добавление управляющих элементов VBX

    Delphi генерирует объектное расширение VBX, которое инсталлируется в качестве компонент.

    Например, если вы инсталлируете SaxComm VBX из Visual Solutions Pack компании Borland в Delphi, автоматически генерится тип объекта TComm, который наследуется из стандартного TVBXControl. Когда вы инсталлируете компоненты, Delphi будет компилировать и подлинковывать их к библиотеке компонент.

    Делегирование: события программируются проще

    Под делегированием понимается то, что некий объект может предоставить другому объекту отвечать на некоторые события.

    Такая модель в некоторых случаях значительно упрощает программирование. Например, вместо того чтобы создавать подкласс для Windows controls при добавлении нового поведения, вы можете просто привязать процедуру обработки события, которая будет вызываться автоматически на каждый щелчок мышью пользователем или нажатие им клавиши. Аналогично вы можете написать процедуру определения допустимости таблицы, которая будет выполняться обработчиком события, когда транзакция начинается или завершается, записи вставляются, удаляются или изменяются.


    К примеру, когда вы добавляете кнопку в форму и прикрепляете код, обрабатывающий нажатие, вы фактически используете делегирование кода для ассоциирования кода с событием OnClick. Такая ассоциация происходит для вас автоматически. Если проверить страницу Events в Инспекторе объектов для вашего приложения, можно увидеть ассоциированные с событиями процедуры.

    Ссылки на классы

    Ссылки на классы придают дополнительный уровень гибкости, так, когда вы хотите динамически создавать объекты, чьи типы могут быть известны только во время выполнения кода. К примеру, ссылки на классы используются при формировании пользователем документа из разного типа объектов, где пользователь набирает нужные объекты из меню или палитры. Собственно, эта технология использовалась и при построении Delphi.

    Обработка исключительных ситуаций

    Серьезные приложения должны надежным образом обрабатывать исключительные ситуации, сохранять, если возможно, выполнение программы или, если это невозможно, аккуратно ее завершать. Написание кода, обрабатывающего исключительные ситуации, всегда было непростой задачей, и являлось источником дополнительных ошибок.

    В Delphi это устроено в стиле С++. Исключения представлены в виде объектов, содержащих специфическую информацию о соответствующей ошибке (тип и место- нахождение ошибки). Разработчик может оставить обработку ошибки, существо-вавшую по умолчанию, или написать свой собственный обработчик.

    Обработка исключений реализована в виде exception-handling blocks (также еще называется protected blocks), которые устанавливаются ключевыми словами try

    и end. Существуют два типа таких блоков: try...except и try...finally.

    Общая конструкция выглядит примерно так:

    try

    { выполняемые операторы }

    except

    on exception1 do statement1;

    { реакция на ситуации }

    on exception2 do statement2;

    else

    { операторы по умолчанию }

    end;

    Конструкция try....finally предназначена для того, чтобы разработчик мог быть полностью уверен в том, что, что бы ни случилось, перед обработкой исключительной ситуации всегда будет выполнен некоторый код (например, освобождение ресурсов).


    try

    { выполняемые операторы }

    finally

    { операторы, выполняемые безусловно }

    end;

    Немного о составе продукта

    Документация.





  • Руководство пользователя




  • Руководство по написанию компонент




  • Построение приложений, работающих с базами данных




  • Руководство по генератору отчетов ReportSmith




  • Руководство по SQL Links


  • В составе Delphi входит 5 интерактивных обучающих систем, документация в электронном виде и около 10 Мб справочной информации.

    Требования к аппаратным и программным средствам





  • Windows 3.1 и выше




  • 27 Mb дискового пространства для минимальной конфигурации




  • 50 Mb дискового пространства для нормальной конфигурации




  • процессор 80386, а лучше 80486




  • 6-8 Mb RAM


  • Заключение

    В данной статье описаны возможности нового программного продукта компании Borland, а также некоторые особенности проектирования приложений с его помощью. В мире уже многие разработчики твердо ориентируются на использование Delphi как на инструмент, позволяющий создавать высокоэффективные клиент-серверные приложения. Более того, список готовых профессионально выполненных приложений настолько велик, что не позволяет полностью привести его в статье. Диапазон разработанных при помощи Delphi программных продуктов также поражает - от игровых программ до мощнейших банковских систем. Прошло всего полгода - и столько результатов. Delphi как продукт имеет версию 1.0, мы уже имеем сведения о том, что предполагается реализовать в следующей версии Delphi, и поскольку Delphi разрабатывается на Delphi, можем быть уверены, что разработка новой версии ведется действительно скоростными методами.

    В данной статье рассказывается как

    Управление проектом

    Содержание

    Обзор

    Проект Delphi

    Пункт меню “File”

    Управление проектом

    Обзор других пунктов меню

    Edit

    Search

    View

    Compile

    Пункт меню Options | Project

    Forms

    Applications

    Compiler

    Linker

    Directories/Conditionals

    Конфигурация среды программирования



    Обзор

    В данной статье рассказывается как :





  • Добавлять и удалять формы и модули в проект




  • Управлять окнами на рабочем пространстве




  • Создавать выполняемый файл для Windows




  • Тонко настроить среду программирования


  • Проект Delphi

    Любой проект имеет, по-крайней мере, шесть файлов, связанных с ним. Три из них относятся к управлению проектом из среды и напрямую программистом не меняются. Вот эти файлы :





  • Главный файл проекта, изначально называется PROJECT1.DPR.




  • Первый модуль программы /unit/, который автоматически появляется в начале работы. Файл называется UNIT1.PAS по умолчанию, но его можно назвать любым другим именем, вроде MAIN.PAS.




  • Файл главной формы, который по умолчанию называется UNIT1.DFM, используется для сохранения информации о внешнем виде главной формы.




  • Файл PROJECT1.RES содержит иконку для проекта, создается автоматически.




  • Файл, который называется PROJECT1.OPT по умолчанию, является текстовым файлом для сохранения установок, связанных с данным проектом. Например, установленные Вами директивы компилятора сохраняются здесь.




  • Файл PROJECT1.DSK содержит информацию о состоянии рабочего пространства.


  • Разумеется, если сохранить проект под другим именем, то изменят название и файлы с расширением RES, OPT и DSK.

    После компиляции программы получаются файлы с расширениями:

    DCU - скомпилированные модули

    EXE - исполняемый файл

    DSM - служебный файл для запуска программы в среде, очень большой, рекомендуется стирать его при окончании работы.

    ~PA, ~DP - backup файлы Редактора.

    Пункт меню “File”

    Если нужно сохранить проект, то Вы выбираете пункт главного меню “File” (с помощью мышки или по клавише Alt+F). Пункт меню “File” выглядит следующим образом:


    New Project

    Open Project

    Save Project

    Save Project As

    Close Project

    ---------------------

    New Form

    New Unit

    New Component

    Open File

    Save File

    Save File As

    Close File

    ---------------------

    Add File

    Remove File

    ---------------------

    Print

    ---------------------

    Exit

    ---------------------

    1 PREV1.DPR

    2 PREV2.DPR

    Как Вы можете видеть, здесь есть шесть секций; вот их назначение:





  • Первая секция дает возможность управления проектом вцелом.




  • Вторая секция дает контроль над формами, модулями и компонентами проекта.




  • Третья позволяет добавлять и удалять файлы из проекта.




  • Четвертая управляет печатью.




  • Пятая секция - выход из Delphi




  • Шестая секция предоставляет список ранее редактировавшихся проектов; Вы можете быстро открыть нужный.


  • Как Вы увидите позже, большинство операций из пункта меню “File” можно выполнить с помощью Менеджера Проекта (Project Manager), который можно вызвать из пункта меню View. Некоторые операции доступны и через SpeedBar. Данная стратегия типична для Delphi: она предоставляет несколько путей для решения одной и той же задачи, Вы сами можете решать, какой из них более эффективен в данной ситуации.

    Каждая строка пункта меню “File” объяснена в Справочнике. Выберите меню “File” и нажмите F1, появится экран справочника, как на рис.1.

    В данной статье рассказывается как


    Рис.1: Delphi включает подсказку, как использовать пункт меню “File”.

    Большинство из пунктов первой секции очевидны. “New Project” начинает новый проект, “Open Project” открывает существующий проект и т.д.

    Первые два пункта второй секции позволяют Вам создать новую форму или новый модуль. Выбирая “New Form”, Вы создаете новую форму и модуль, связанный с ней; выбирая “New Unit”, Вы создаете один модуль.

    “New Component” вызывает диалог для построения заготовки нового визуального компонента. В результате создается модуль, который можно скомпилировать и включить в Палитру Компонент.

    “Open File” открывает при необходимости любой модуль или просто текстовый файл. Если модуль описывает форму, то эта форма тоже появится на экране.


    При создании нового модуля Delphi дает ему имя по-умолчанию. Вы можете изменить это имя на что-нибудь более осмысленное (например, MAIN.PAS) с помощью пункта “Save File As“.

    “Save File” сохраняет только редактируемый файл, но не весь проект.

    “Close File” удаляет файл из окна Редактора.

    Нужно обратить внимание: Вы должны регулярно сохранять проект через File | Save Project либо через нажатие Ctrl+S.

    Управление проектом

    Теперь, когда Вы знаете о создании проекта с помощью пункта меню “File”, перейдем к Менеджеру Проектов, который помогает управлять проектом. Менеджер Проектов, рис.3, разделен на две части. Верхняя - панель с управляющими кнопками. Нижняя - список модулей, входящих в проект.

    В данной статье рассказывается как


    Рис.3: Кнопки сверху используются для удаления и добавления модулей в проект.

    Вы можете использовать кнопки с плюсом и минусом для добавления и удаления файлов в проекте. Эти изменения влияют на файлы с исходным текстом, то есть, если добавить в проект модуль, то ссылка на него появится в файле с расширением DPR.

    Краткое описание других кнопок :





  • Третья слева кнопка - просмотр текста модуля, на котором стоит курсор.




  • Четвертая - просмотр формы, если есть таковая для данного модуля




  • Пятая - вызов диалога настройки проекта, сам диалог будет рассмотрен позже.




  • Последняя - сохранение изменений на диске.


  • Обзор других пунктов меню

    Пункт меню “File” обсуждался ранее. Далее рассматриваются другие важные пункты - “Edit”, “Search”, “View” и “Compile”, но менее подробно. Далее, снова подробно, рассказывается о “Options”.










  • Пункт меню “Edit”


  • “Edit” содержит команды “Undo” и “Redo”, которые могут быть очень полезны при работе в редакторе для устранения последствий при неправильных действиях, например, если случайно удален нужный фрагмент текста.


  • Отметьте для себя, что Справочник (on-line help) объясняет как нужно использовать пункт меню Options | Environment для настройки команды “Undo”. Возможность ограничить возможное количество команд “Undo” может пригодиться, если Вы работаете на машине с ограниченными ресурсами.


    Команды “Cut”, “Copy”, “Paste” и “Delete” - как во всех остальных приложениях Windows, но их можно применять не только к тексту, но и к визуальным компонентам.

    “Bring To Front”, “Send To Back”, “Align” и “Size” обсуждались в Уроке № 2. Оставшиеся четыре пункта помогают быстро “приукрасить” внешний вид формы.

  • Пункт меню “Menu”


  • В “Search” есть команда “Find Error” (поиск ошибки), которая поможет отследить ошибку периода выполнения программы. Когда в сообщении об ошибке указан ее адрес, Вы можете выбрать пункт меню Search | Find Error и ввести этот адрес. Если это представится возможным, то среда переместит Вас в то место программы, где произошла ошибка.


  • Пункт меню “View”


  • Составляющие пункта меню “View”:





  • Project Manager (Менеджер Проекта).




  • Project Source - загружает главный файл проекта (DPR) в Редактор




  • Установка, показывать или нет Object Inspector на экране.




  • Установка, показывать или нет Alignment Palette. То же самое доступно из пункт меню Edit | Align.




  • Browser - вызов средства для просмотра иерархии объектов программы, поиска идентификатора в исходных текстах и т.п.




  • Watch, Breakpoint и Call Stack - связаны с процедурой отладки программы и будут обсуждаться позднее.




  • Component List - список компонент, альтернатива Палитре Компонент. Используется для поиска компонента по имени или при отсутствии мыши.




  • Window List - список окон, открытых в среде Delphi.




  • Toggle Form/Unit, Units, Forms - переключение между формой и соответствующим модулем, выбор модуля или формы из списка.




  • New Edit Window - открывает дополнительное окно Редактора. Полезно, если нужно, например, просмотреть две разных версии одного файла.




  • SpeedBar и Component Palette - установки, нужно ли их отображать.










  • Пункт меню “Compile”


  • В пункте меню “Compile” проект можно скомпилировать (compile) или перестроить (build). Если выбрать Compile или Run, то Delphi перекомпилирует только те модули, которые изменились со времени последней компиляции. Build all, с другой стороны, перекомпилирует все модули, исходные тексты которых доступны. Команда Syntax Check только проверяет правильность кода программы, но не обновляет DCU файлы.



  • В самом низу - пункт Information, который выдает информацию о программе: размеры сегментов кода, данных и стека, размер локальной динамической памяти и количество скомпилированных строк.

  • Пункт меню “Run”


  • Можно использовать “Run” для компиляции и запуска программы и для указания параметров командной строки для передачи в программу. Здесь же имеются опции для режима отладки.

    Пункт меню Options | Project

    “Options” наиболее сложная часть системного меню. Это центр управления, из которого вы можете изменять установки для проекта и для всей рабочей среды Delphi. В “Options” есть семь пунктов:

    Project

    Environment

    Tools

    Gallery

    --

    Open Library

    Install Components

    Rebuild Library

    Первые четыре пункта вызывают диалоговые окна. Ниже приведено общее описание пункта меню “Options”:





  • Project - выбор установок, которые напрямую влияют на текущий проект, это могут быть, к примеру, директивы компилятора проверки стека (stack checking) или диапазона (range checking).




  • Environment - конфигурация самой среды программирования (IDE). Например, здесь можно изменить цвета, используемые в Редакторе.




  • Tools - позволяет добавить или удалить вызов внешних программ в пункт главного меню “Tools”. Например, если Вы часто пользуетесь каким-нибудь редактором или отладчиком, то здесь его вызов можно добавить в меню.




  • Gallery - позволяет определить специфические установки для Эксперта Форм и Эксперта Проектов и их “заготовок”. Эксперты и “заготовки” предоставляют путь для ускорения конструирования интерфейса программы.




  • Последние три пункта позволяют сконфигурировать Палитру Компонент.


  • Диалог из пункта Options | Project включает пять страниц:





  • На странице Forms перечислены все формы, включенные в проект; Вы можете указать, нужно ли автоматически создавать форму при старте программы или Вы ее создадите сами.




  • На странице Application Вы определяете элементы программы такие, как заголовок, файл помощи и иконка.




  • Страница Compiler включает установки для генерации кода, управления обработкой ошибок времени выполнения, синтаксиса, отладки и др.





  • На странице Linker можно определить условия для процесса линковки приложения




  • Страница Directories/Conditionals - здесь указываются директории, специфичные для данного проекта.


  • После предыдущего абзаца с общим описанием, каждая страница описана детально в отдельной главе.

    Все установки для проекта сохраняются в текстовом файле с расширением OPT и Вы можете вручную их исправить.










  • Страница Forms


  • В данной статье рассказывается как


  • На странице Forms можно выбрать главную форму проекта. Изменения, которые Вы сделаете, отобразятся в соответствующем файле DPR. Например, в нижеследующем проекте, Form1 является главной, поскольку появляется первой в главном блоке программы:

    program Project1;

    uses

    Forms,

    Unit1 in 'UNIT1.PAS' {Form1},

    Unit2 in 'UNIT2.PAS' {Form2};

    {$R *.RES}

    begin

    Application.CreateForm(TForm1, Form1);

    Application.CreateForm(TForm2, Form2);

    Application.Run;

    end.

    Если изменить код так, чтобы он читался

    begin

    Application.CreateForm(TForm2, Form2);

    Application.CreateForm(TForm1, Form1);

    Application.Run;

    end.

    то теперь Form2 станет главной формой проекта.

    Вы также можете использовать эту страницу для определения, будет ли данная форма создаваться автоматически при старте программы. Если форма создается не автоматически, а по ходу выполнения программы, то для этого нужно использовать процедуру Create.

    Кстати, в секции Uses имя формы в фигурных скобках является существенным для Менеджера Проектов и удалять его не стоит. Не нужно вообще ничего изменять вручную в файле проекта, если только Вы не захотели создать DLL, но об этом позднее.

  • Страница Applications


  • На странице Applications, см. рис.5, вы можете задать заголовок (Title), файл помощи (Help file) и пиктограмму (Icon) для проекта.


  • В данной статье рассказывается как


    Рис.5: Страница общих установок для приложения.

  • Страница Compiler


  • Ранее уже говорилось, что установки из пункта меню “Options | Project” сохраняются в соответствующем файле с расширением OPT. Давайте рассмотрим директивы компилятора на странице Compiler (рис.6).



  • В данной статье рассказывается как


    Рис.6: Страница для определения директив компилятора.

    Следующая таблица показывает, как различные директивы отображаются в OPT файле, на странице Compiler и внутри кода программы:

  • Страница Linker


  • Теперь давайте перейдем к странице Linker, показанной на рис.7.


  • В данной статье рассказывается как


    Рис.7: Страница линковщика.

    Установки отладчика рассматриваются ниже. Если буфер линковщика расположен в памяти, то линковка происходит быстрее.

    Размер стека (Stack Size) и локальной динамической памяти (Heap Size) весьма важны. Delphi устанавливает по умолчанию и Stack Size, и Heap Size в 8192 байт каждый. Вам может понадобиться изменить размер стека в программе, но обычно это не более 32Кб. В сумме эти два размера не должны превышать 64Кб, иначе будет выдаваться ошибка при компиляции программы.

  • Страница Directories/Conditionals


  • Страница Directories/Conditionals, рис.8, дает возможность расширить число директорий, в которых компилятор и линковщик ищут DCU файлы.

    В данной статье рассказывается как


    Рис.8: Страница Directories/Conditionals.

    В файле DELPHI.INI содержится еще один список директорий. Запомните, что в OPT файле - список директорий для конкретного проекта, а в файле DELPHI.INI - список относится к любому проекту.

    Output directory - выходная директория, куда складываются EXE и DCU файлы, получающиеся при компиляции.

    Search path - список директорий для поиска DCU файлов при линковке. Директории перечисляются через точку с запятой ;


    Conditional defines - для опытного программиста и на первом этапе создания проекта не требуется. Для информации можно вызвать Справочник (on-line help).

    Конфигурация среды программирования (IDE)

    Пункт меню “Options | Environment” предоставляет Вам большой набор страниц и управляющих элементов, которые определяют внешний вид и работу IDE. Delphi позволяет сделать следующие важные настройки:




  • Определить, что из проекта будет сохраняться автоматически.




  • Можно менять цвета IDE.




  • Можно менять подсветку синтаксиса в Редакторе.




  • Можно изменить состав Палитры Компонент.




  • Указать “горячие клавиши” IDE.


  • Первая страница пункта меню “Options | Environment” показана на рис.9

    В данной статье рассказывается как


    Рис.9: Страница Preferences.

    В группе “Desktop Contents” определяется, что будет сохраняться при выходе из Delphi. Если выбрать Desktop Only - это сохранит информацию о директориях и открытых окнах, если выбрать Desktop And Symbols - это сохранит то же самое плюс информацию для броузера (browser).

    В группе “Autosave” указывается, что нужно сохранять при запуске программы. Если позиция Editor Files выбрана, то сохраняются все модифицированные файлы из Редактора при выполнении команд Run|Run, Run|Trace Into, Run|Step Over, Run|Run To Cursor или при выходе из Delphi. Если позиция Desktop выбрана - сохраняется рабочая среда при закрытии проекта или при выходе из Delphi. Если Вы позже откроете проект, то он будет иметь тот же вид, что и при его закрытии.

    В группе “Form Designer” можно установить, показывать ли сетку (grid) на экране и выравнивать ли объекты по ней, и размер ячеек сетки.

    В группе “Debugging”: опция Integrated Debugging - использовать ли встроенный отладчик; Step Program Block - отладчик остановится на первой строке модуля, в котором есть отладочная информация; Break On Exception - останавливать ли программу при возникновении исключительной ситуации; Minimize On Run - свертывать ли Delphi при запуске программы. После закрытия программы среда Delphi восстанавливается. Hide Designers On Run - прячет окна Дизайнера (Инспектор Объектов, формы) при запуске приложения.


    Show Compiler Progress - показывать ли окно, в котором отражается процесс компиляции программы.

    “Gallery” - указывает, в каких случаях нужно предоставлять “галерею” (коллекцию заготовок и экспертов).

    Страницы Editor Options, Editor Display и Editor Colors позволяют Вам изменить цвета и “горячие” клавиши, используемые IDE. Страница Editor Display показана на рис.10, а Editor Colors - на рис.11.

    В данной статье рассказывается как


    Рис.10: Страница Editor Display.

    В данной статье рассказывается как


    Рис.11: Страница Editor Colors.

    Существует несколько способов изменить назначение “горячих” клавиш, используемых Редактором. Например, многие пользователи привыкли, что по клавише F5 максимизируется окно Редактора. Для этого им надо использовать расположение клавиш, называемое “Classic” (Keystroke mapping : Classic). Всего есть четыре вида конфигурации клавиш:





  • “Default” - характерно для Microsoft. Если Вы новичок в Windows или уже привыкли к этому расположению клавиш, то это подойдет.




  • “Classic” - более известно ветеранам Borland C++ и Borland Pascal. Поддерживает многие комбинации клавиш WordStar и отладчик управляется старым добрым способом.




  • Остальные два вида - имитируют редакторы Epsilon и BRIEF. Подойдут, если вы с ними знакомы.


  • Точное описание назначения клавиш можно найти в Справочнике (в Help | Topic Search набрать “key mapping”).

    Цвета IDE можно изменить на странице Editor Colors.

    И, наконец, Editor Options (рис.12).

    В данной статье рассказывается как


    Рис.12: На странице Editor Options можно настроить тонкие детали работы Редактора.

    Многие из установок на данной странице не очень важны для большинства пользователей, поэтому остановимся лишь на некоторых.

    “Use syntax highlight” - выделять ли цветом синтаксические конструкции в Редакторе Исходного текста.

    “Find text at cursor” - если включено, то при поиске (Ctrl+F) в качестве подстроки для поиска будет браться то слово, на котором стоит курсор.

    Обо всех опциях можно подробнее узнать в Справочнике (F1).

    Установки сохраняются в файле DELPHI.INI, который находится в директории Windows.

    На данном уроке мы изучим,

    Создание таблиц с помощью Database Desktop



    Содержание

    Обзор

    Утилита Database Desktop

    Заключение


  • Обзор


  • На данном уроке мы изучим, как создавать таблицы базы данных с помощью утилиты Database Desktop, входящей в поставку Delphi. Хотя для создания таблиц можно использовать различные средства (SQL - компонент TQuery и WISQL, компонент TTable), применение этой утилиты позволяет создавать таблицы в интерактивном режиме и сразу же просмотреть их содержимое - и все это для большого числа форматов. Это особенно удобно для локальных баз данных, в частности Paradox и dBase.


  • Утилита Database Desktop


  • На данном уроке мы изучим,


    Database Desktop - это утилита, во многом похожая на Paradox, которая поставляется вместе с Delphi для интерактивной работы с таблицами различных форматов локальных баз данных - Paradox и dBase, а также SQL-серверных баз данных InterBase, Oracle, Informix, Sybase (с использованием SQL Links). Исполняемый файл утилиты называется DBD.EXE, расположен он, как правило, в директории, называемом DBD (при установке по умолчанию). Для запуска Database Desktop просто дважды щелкните по ее иконке.

    На данном уроке мы изучим,


    После старта Database Desktop выберите команду меню File|New|Table для создания новой таблицы. Перед Вами появится диалоговое окно выбора типа таблицы, как показано на рис.1. Вы можете выбрать любой формат из предложенного, включая различные версии одного и того же формата.

    После выбора типа таблицы Database Desktop представит Вам диалоговое окно, специфичное для каждого формата, в котором Вы сможете определить поля таблицы и их тип, как показано на рис.2.

    На данном уроке мы изучим,


    Имя поля в таблице формата Paradox представляет собой строку, написание которой подчиняется следующим правилам:





  • Имя должно быть не длиннее 25 символов.




  • Имя не должно начинаться с пробела, однако может содержать пробелы. Однако, если Вы предполагаете в будущем переносить базу данных в другие форматы, разумнее будет избегать включения пробелов в название поля. Фактически, в целях переносимости лучше ограничиться девятью символами в названии поля, не включая в него пробелы.





  • Имя не должно содержать квадратные, круглые или фигурные скобки [], () или {}, тире, а также комбинацию символов “тире” и “больше” (->).




  • Имя не должно быть только символом #, хотя этот символ может присутствовать в имени среди других символов. Хотя Paradox поддерживает точку (.) в названии поля, лучше ее избегать, поскольку точка зарезервирована в Delphi для других целей.


  • Имя поля в таблице формата dBase представляет собой строку, написание которой подчиняется правилам, отличным от Paradox:





  • Имя должно быть не длиннее 10 символов.




  • Пробелы в имени недопустимы.


  • Таким образом, Вы видите, что имена полей в формате dBase подчиняются гораздо более строгим правилам, нежели таковые в формате Paradox. Однако, мы еще раз хотим подчеркнуть, что если перед Вами когда-либо встанут вопросы совместимости, то лучше сразу закладывать эту совместимость - давать полям имена, подчиняющиеся более строгим правилам.

    Укажем еще правила, которым подчиняется написание имен полей в формате InterBase.





  • Имя должно быть не длиннее 31 символа.




  • Имя должно начинаться с букв A-Z, a-z.




  • Имя поля может содержать буквы (A-Z, a-z), цифры, знак $ и символ подчеркивания (_).




  • Пробелы в имени недопустимы.




  • Для имен таблиц запрещается использовать зарезервированные слова InterBase.


  • Следующий (после выбора имени поля) шаг состоит в задании типа поля. Типы полей очень сильно различаются друг от друга, в зависимости от формата таблицы. Для получения списка типов полей перейдите к столбцу “Type”, а затем нажмите пробел или щелкните правой кнопкой мышки. Приведем списки типов полей, характерные для форматов Paradox, dBase и InterBase.

    Итак, поля таблиц формата Paradox могут иметь следующий тип (для ввода типа поля можно набрать только подчеркнутые буквы или цифры):

    Табл. A: Типы полей формата Paradox

    OPT File Options Page Editor Symbol
    F Force Far Calls {$F+}
    A Word Align Date {$A+}
    U Pentium-Safe FDIV {$U+}
    K Smart Callbacks {$K+}
    W Windows (3.0) Stack Frame {$W+}
    R Range Checking {$R+}
    S Stack Checking {$S+}
    I IO Checking {$I+}
    Q Overflow Checking {$Q+}
    V Strict Var Strings {$V+}
    B Complete Boolean Evaluation {$B+}
    X Extended Syntax {$X+}
    T Typed @ Operator {$T+}
    P Open Parameters {$P+}
    D Debug Information {$D+}
    L Local Symbols {$L+}
    Y Symbol Information {$Y+}
    N Numeric Processing {$N+}
    Поля таблиц формата dBase могут иметь следующий тип (для ввода типа поля можно набрать только подчеркнутые буквы или цифры):

    Табл. B: Типы полей формата dBase

    Alpha строка длиной 1-255 байт, содержащая любые печатаемые символы
    Number числовое поле длиной 8 байт, значение которого может быть положительным и отрицательным. Диапазон чисел - от 10-308 до 10308 с 15 значащими цифрами
    $ (Money) числовое поле, значение которого может быть положительным и отрицательным. По умолчанию, является форматированным для отображения десятичной точки и денежного знака
    Short числовое поле длиной 2 байта, которое может содержать только целые числа в диапазоне от -32768 до 32767
    Long Integer числовое поле длиной 4 байта, которое может содержать целые числа в диапазоне от -2147483648 до 2147483648
    # (BCD) числовое поле, содержащее данные в формате BCD (Binary Coded Decimal). Скорость вычислений немного меньше, чем в других числовых форматах, однако точность - гораздо выше. Может иметь 0-32 цифр после десятичной точки
    Date поле даты длиной 4 байта, которое может содержать дату от 1 января 9999 г. до нашей эры - до 31 декабря 9999 г. нашей эры. Корректно обрабатывает високосные года и имеет встроенный механизм проверки правильности даты
    Time поле времени длиной 4 байта, содержит время в миллисекундах от полуночи и ограничено 24 часами
    @ (Timestamp) обобщенное поле даты длиной 8 байт - содержит и дату и время
    Memo поле для хранения символов, суммарная длина которых более 255 байт. Может иметь любую длину. При этом размер, указываемый при создании таблицы, означает количество символов, сохраняемых в таблице (1-240) - остальные символы сохраняются в отдельном файле с расширением .MB
    Formatted Memo поле, аналогичное Memo, с добавлением возможности задавать шрифт текста. Также может иметь любую длину. При этом размер, указываемый при создании таблицы, означает количество символов, сохраняемых в таблице (0-240) - остальные символы сохраняются в отдельном файле с расширением .MB. Однако, Delphi в стандартной поставке не обладает возможностью работать с полями типа Formatted Memo
    Graphic поле, содержащее графическую информацию. Может иметь любую длину. Смысл размера - такой же, как и в Formatted Memo. Database Desktop “умеет” создавать поля типа Graphic, однако наполнять их можно только в приложении
    OLE поле, содержащее OLE-данные (Object Linking and Embedding) - образы, звук, видео, документы - которые для своей обработки вызывают создавшее их приложение. Может иметь любую длину. Смысл размера - такой же, как и в Formatted Memo. Database Desktop “умеет” создавать поля типа OLE, однако наполнять их можно только в приложении. Delphi “напрямую” не умеет работать с OLE-полями, но это легко обходится путем использования потоков
    Logical поле длиной 1 байт, которое может содержать только два значения - T (true, истина) или F (false, ложь). Допускаются строчные и прописные буквы
    + (Autoincrement) поле длиной 4 байта, содержащее нередактируемое (read-only) значение типа long integer. Значение этого поля автоматически увеличивается (начиная с 1) с шагом 1 - это очень удобно для создания уникального идентификатора записи (физический номер записи не может служить ее идентификатором, поскольку в Парадоксе таковой отсутствует. В InterBase также отсутствуют физические номера записей, но отсутствует и поле Autoincrement. Его с успехом заменяет встроенная функция Gen_id, которую удобней всего применять в триггерах)
    Binary поле, содержащее любую двоичную информацию. Может иметь любую длину. При этом размер, указываемый при создании таблицы, означает количество символов, сохраняемых в таблице (0-240) - остальные символы сохраняются в отдельном файле с расширением .MB. Это полнейший аналог поля BLOb в InterBase
    Bytes строка цифр длиной 1-255 байт, содержащая любые данные
    (true, истина) или F,f,N,n (false, ложь). Допускаются строчные и прописные буквы. Таким образом, в отличие от Парадокса, допускаются буквы “Y” и “N” (сокращение от Yes и No)

    Поля таблиц формата InterBase могут иметь следующий тип:

    Табл. C: Типы полей формата InterBase

    Character (alpha) строка длиной 1-254 байт, содержащая любые печатаемые символы
    Float (numeric) числовое поле размером 1-20 байт в формате с плавающей точкой, значение которого может быть положительным и отрицательным. Может содержать очень большие величины, однако следует иметь в виду постоянные ошибки округления при работе с полем такого типа. Число цифр после десятичной точки (параметр Dec в DBD) должно быть по крайней мере на 2 меньше, чем размер всего поля, поскольку в общий размер включаются сама десятичная точка и знак
    Number (BCD) числовое поле размером 1-20 байт, содержащее данные в формате BCD (Binary Coded Decimal). Скорость вычислений немного меньше, чем в других числовых форматах, однако точность - гораздо выше. Число цифр после десятичной точки (параметр Dec в DBD) также должно быть по крайней мере на 2 меньше, чем размер всего поля, поскольку в общий размер включаются сама десятичная точка и знак
    Date поле даты длиной 8 байт. По умолчанию, используется формат короткой даты (ShortDateFormat)
    Logical поле длиной 1 байт, которое может содержать только значения “истина” или “ложь” - T,t,Y,y
    Memo поле для хранения символов, суммарная длина которых более 255 байт. Может иметь любую длину. Это поле хранится в отдельном файле. Database Desktop не имеет возможности вставлять данные в поле типа Memo
    OLE поле, содержащее OLE-данные (Object Linking and Embedding) - образы, звук, видео, документы - которые для своей обработки вызывают создавшее их приложение. Может иметь любую длину. Это поле также сохраняется в отдельном файле. Database Desktop “умеет” создавать поля типа OLE, однако наполнять их можно только в приложении. Delphi “напрямую” не умеет работать с OLE-полями, но это легко обходится путем использования потоков
    Binary поле, содержащее любую двоичную информацию. Может иметь любую длину. Данное поле сохраняется в отдельном файле с расширением .DBT. Это полнейший аналог поля BLOb в InterBase
    до 3.4*1038 с 7 значащими цифрами

    до 1.7*10308 с 15 значащими цифрами

    Итак, мы изучили все типы полей, являющиеся “родными” для Delphi.

    После этого для таблиц Paradox мы можем определить поля, составляющие первичный ключ, причем все они должны быть в начале записи, а первое поле, входящее в ключ, должно быть первым полем в записи. Для этого достаточно по ней дважды щелкнуть мышкой или нажать любую клавишу.

    После создания таблицы, с ней можно связать некоторые свойства, перечень которых зависит от формата таблицы. Так, для таблиц формата Paradox можно задать:





  • Validity Checks (проверка правильности) - относится к полю записи и определяет минимальное и максимальное значение, а также значение по умолчанию. Кроме того, позволяет задать маску ввода




  • Table Lookup (таблица для “подсматривания”) - позволяет вводить значение в таблицу, используя уже существующее значение в другой таблице




  • Secondary Indexes (вторичные индексы) - позволяют доступаться к данным в порядке, отличном от порядка, задаваемого первичным ключом




  • Referential Integrity (ссылочная целостность) - позволяет задать связи между таблицами и поддерживать эти связи на уровне ядра. Обычно задается после создания всех таблиц в базе данных





  • Password Security (парольная защита) - позволяет закрыть таблицу паролем




  • Table Language (язык таблицы) - позволяет задать для таблицы языковый драйвер.


  • В таблицах dBase не существует первичных ключей. Однако, это обстоятельство можно преодолеть путем определения уникальных (Unique) и поддерживаемых (Maintained) индексов (Indexes). Кроме того, для таблиц dBase можно определить и язык таблицы (Table Language) - языковый драйвер, управляющий сортировкой и отображением символьных данных.

    Определения дополнительных свойств таблиц всех форматов доступны через кнопку “Define” (для таблиц InterBase данная кнопка называется “Define Index...” и позволяет определять лишь только индекс, но не первичный ключ) в правой верхней части окна (группа Table Properties). Причем, все эти действия можно проделывать не только при создании таблицы, но и тогда, когда она уже существует. Для этого используется команда Table|Restructure Table (для открытой в данный момент таблицы) или Utilities|Restructure (с возможностью выбора таблицы). Однако, если Вы желаете изменить структуру или добавить новые свойства для таблицы, которая в данный момент уже используется другим приложением, Database Desktop откажет Вам в этом, поскольку данная операция требует монопольного доступа к таблице. Но зато все произведенные в структуре изменения сразу же начинают “работать” - например, если Вы определите ссылочную целостность для пары таблиц, то при попытке вставить в дочернюю таблицу данные, отсутствующие в родительской таблице, в Delphi возникнет исключительное состояние.

    В заключение отметим еще часто используемую очень полезную возможность Database Desktop. Создавать таблицу любого

    формата можно не только “с чистого листа”, но и путем копирования структуры уже существующей таблицы. Для этого достаточно воспользоваться кнопкой “Borrow”, имеющейся в левом нижнем углу окна. Появляющееся диалоговое окно позволит Вам выбрать существующую таблицу и включить/выключить дополнительные опции, совпадающие с уже перечисленными свойствами таблиц. Это наиболее легкий способ создания таблиц.








  • Заключение


  • Итак, на данном уроке мы познакомились со штатной утилитой, используемой для интерактивного создания и модификации таблиц различной структуры. И хотя управление таблицами можно осуществлять с помощью различных средств (SQL-скрипт в WISQL, компонент TTable, компонент TQuery), данная утилита позволяет делать это в интерактивном режиме наиболее простым способом.

    На данном уроке мы познакомимся

    Создание таблиц с помощью SQL-запросов



    Содержание

    Обзор

    Создание таблиц с помощью SQL

    Заключение

    Обзор

    На данном уроке мы познакомимся


    На данном уроке мы познакомимся еще с одной возможностью создания таблиц - через посылку SQL-запросов. Как Вы, наверное, могли заметить на предыдущем уроке, Database Desktop не обладает всеми возможностями по управлению SQL-серверными базами данных. Поэтому с помощью Database Desktop удобно создавать или локальные базы данных или только простейшие SQL-серверные базы данных, состоящие из небольшого числа таблиц, не очень сильно связанных друг с другом. Если же Вам необходимо создать базу данных, состоящую из большого числа таблиц, имеющих сложные взаимосвязи, можно воспользоваться языком SQL (вообще говоря, для этих целей лучше всего использовать специализированные CASE-средства, которые позволяют в интерактивном режиме сгенерировать всю структуру базы данных и сформировать все связи; описание двух наиболее удачных CASE-средств - System Architect и S-Designor - дано в дополнительных уроках). При этом можно воспользоваться компонентом Query

    в Delphi, каждый раз посылая по одному SQL-запросу, а можно записать всю последовательность SQL-предложений в один так называемый скрипт и послать его на выполнение, используя, например, Windows Interactive SQL (WISQL.EXE) - интерактивное средство посылки SQL-запросов к InterBase (в том числе и локальному InterBase), входящее в поставку Delphi. Конечно, для этого нужно хорошо знать язык SQL, но, уверяю Вас, сложного в этом ничего нет! Конкретные реализации языка SQL незначительно отличаются в различных SQL-серверах, однако базовые предложения остаются одинаковыми для всех реализаций. Практика показывает, что если нет необходимости создавать таблицы во время выполнения программы, то лучше воспользоваться WISQL.






  • Создание таблиц с помощью SQL


  • Если Вы хотите воспользоваться компонентом TQuery, сначала поместите его на форму. После этого настройте свойство DatabaseName на нужный Вам алиас (если базы данных еще не существует, удобней создать ее в WISQL командой File|Create Database..., а затем уже настроить на нее новый алиас). После этого можно ввести SQL-предложение в свойство SQL. Для выполнения запроса, изменяющего структуру, вставляющего



  • или обновляющего

    данные на сервере, нужно вызвать метод ExecSQL

    компонента TQuery. Для выполнения запроса, получающего

    данные с сервера (т.е. запроса, в котором основным является оператор SELECT), нужно вызвать метод Open

    компонента TQuery. Это связано с тем, что BDE при посылке запроса типа SELECT открывает так называемый курсор, с помощью которого осуществляется навигация по выборке данных (подробней об этом см. в уроке, посвященном TQuery).

    Как показывает опыт, проще воспользоваться утилитой WISQL. Для этого в WISQL выберите команду File|Run an ISQL Script... и выберите файл, в котором записан ваш скрипт, создающий базу данных. После нажатия кнопки “OK” ваш скрипт будет выполнен, и в нижнее окно будет выведен протокол его работы.

    Приведем упрощенный синтаксис SQL-предложения для создания таблицы на SQL-сервере InterBase (более полный синтаксис можно посмотреть в online-справочнике по SQL, поставляемом с локальным InterBase):

    CREATE TABLE table

    ( [, | ...]);

    где

    table - имя создаваемой таблицы,

    - описание поля,

    - описание ограничений и/или ключей (квадратные скобки []

    означают необязательность, вертикальная черта | означает “или”).

    Описание поля состоит из наименования поля и типа поля (или домена - см. урок 9), а также дополнительных ограничений, накладываемых на поле:

    = col {datatype | COMPUTED BY () | domain}

    [DEFAULT {literal | NULL | USER}]

    [NOT NULL] []

    [COLLATE collation]

    Здесь

    col - имя поля;

    datatype - любой правильный тип SQL-сервера (для InterBase такими типами являются - см. урок 11 - SMALLINT, INTEGER, FLOAT, DOUBLE PRECISION, DECIMAL, NUMERIC, DATE, CHAR, VARCHAR, NCHAR, BLOB), символьные типы могут иметь CHARACTER SET - набор символов, определяющий язык страны. Для русского языка следует задать набор символов WIN1251;

    COMPUTED BY () - определение вычисляемого на уровне сервера поля, где - правильное SQL-выражение, возвращающее единственное значение;


    domain - имя домена (обобщенного типа), определенного в базе данных;

    DEFAULT - конструкция, определяющая значение поля по умолчанию;

    NOT NULL - конструкция, указывающая на то, что поле не может быть пустым;

    COLLATE - предложение, определяющее порядок сортировки для выбранного набора символов (для поля типа BLOB не применяется). Русский набор символов WIN1251 имеет 2 порядка сортировки - WIN1251 и PXW_CYRL. Для правильной сортировки, включающей большие буквы, следует выбрать порядок PXW_CYRL.

    Описание ограничений и/или ключей включает в себя предложения CONSTRAINT или предложения, описывающие уникальные поля, первичные, внешние ключи, а также ограничения CHECK

    (такие конструкции могут определяться как на уровне поля, так и на уровне таблицы в целом, если они затрагивают несколько полей):

    = [CONSTRAINT constraint ]



    Здесь

    = {{PRIMARY KEY | UNIQUE} (col[,col...]) | FOREIGN KEY (col [, col ...]) REFERENCES other_table

    | CHECK ()}

    =

    { { | ()}

    | [NOT] BETWEEN AND

    | [NOT] LIKE [ESCAPE ]

    | [NOT] IN ( [, ...] |

    = {

    col [] | | |

    | NULL | USER | RDB$DB_KEY } [COLLATE collation]

    = num | "string" | charsetname "string"

    = {

    COUNT (* | [ALL] | DISTINCT )

    | SUM ([ALL] | DISTINCT )

    | AVG ([ALL] | DISTINCT )

    | MAX ([ALL] | DISTINCT )

    | MIN ([ALL] | DISTINCT )

    | CAST ( AS )

    | UPPER ()

    | GEN_ID (generator, )

    }

    = {= | < | > | <= | >= | !< | !> | <> | !=}


    = выражение SELECT по одному полю, которое возвращает в точности одно значение.

    Приведенного неполного синтаксиса достаточно для большинства задач, решаемых в различных предметных областях. Проще всего синтаксис SQL можно понять из примеров. Поэтому мы приведем несколько примеров создания таблиц с помощью SQL.

    Пример A: Простая таблица с конструкцией PRIMARY KEY на уровне поля

    CREATE TABLE REGION (

    REGION REGION_NAME NOT NULL PRIMARY KEY,

    POPULATION INTEGER NOT NULL);

    Предполагается, что в базе данных определен домен REGION_NAME, например, следующим образом:

    CREATE DOMAIN REGION_NAME

    AS VARCHAR(40) CHARACTER SET WIN1251 COLLATE PXW_CYRL;

    Пример B: Таблица с предложением UNIQUE

    как на уровне поля, так и на уровне таблицы

    CREATE TABLE GOODS (

    MODEL SMALLINT NOT NULL UNIQUE,

    NAME CHAR(10) NOT NULL,

    ITEMID INTEGER NOT NULL, CONSTRAINT MOD_UNIQUE

    UNIQUE (NAME, ITEMID));

    Пример C: Таблица с определением первичного ключа, внешнего ключа и конструкции CHECK, а также символьных массивов

    CREATE TABLE JOB (

    JOB_CODE JOBCODE NOT NULL,

    JOB_GRADE JOBGRADE NOT NULL,

    JOB_REGION REGION_NAME NOT NULL,

    JOB_TITLE VARCHAR(25) CHARACTER SET WIN1251 COLLATE PXW_CYRL NOT NULL,

    MIN_SALARY SALARY NOT NULL,

    MAX_SALARY SALARY NOT NULL,

    JOB_REQ BLOB(400,1) CHARACTER SET WIN1251,

    LANGUAGE_REQ VARCHAR(15) [5],

    PRIMARY KEY (JOB_CODE, JOB_GRADE, JOB_REGION),

    FOREIGN KEY (JOB_REGION) REFERENCES REGION (REGION),

    CHECK (MIN_SALARY < MAX_SALARY));

    Данный пример создает таблицу, содержащую информацию о работах (профессиях). Типы полей основаны на доменах JOBCODE, JOBGRADE, REGION_NAME и SALARY. Определен массив LANGUAGE_REQ, состоящий из 5 элементов типа VARCHAR(15). Кроме того, введено поле JOB_REQ, имеющее тип BLOB с подтипом 1

    (текстовый блоб) и размером сегмента 400. Для таблицы определен первичный ключ, состоящий из трех полей JOB_CODE, JOB_GRADE и JOB_REGION. Далее, определен внешний ключ (JOB_REGION), ссылающийся на поле REGION

    таблицы REGION. И, наконец, включено предложение CHECK, позволяющее производить проверку соотношения для двух полей и вызывать исключительное состояние при нарушении такого соотношения.


    Пример D: Таблица с вычисляемым полем

    CREATE TABLE SALARY_HISTORY (

    EMP_NO EMPNO NOT NULL,

    CHANGE_DATE DATE DEFAULT "NOW" NOT NULL,

    UPDATER_ID VARCHAR(20) NOT NULL,

    OLD_SALARY SALARY NOT NULL,

    PERC_CHANGE DOUBLE PRECISION DEFAULT 0 NOT NULL

    CHECK (PERC_CHANGE BETWEEN -50 AND 50),

    NEW_SALARY COMPUTED BY

    (OLD_SALARY + OLD_SALARY * PERC_CHANGE / 100),

    PRIMARY KEY (EMP_NO, CHANGE_DATE, UPDATER_ID),

    FOREIGN KEY (EMP_NO) REFERENCES EMPLOYEE (EMP_NO));

    Данный пример создает таблицу, где среди других полей имеется вычисляемое (физически не существующее) поле NEW_SALARY, значение которого вычисляется по значениям двух других полей (OLD_SALARY и PERC_CHANGE).

    На диске приведен пример скрипта, создающего базу данных, осуществляющую ведение контактов между людьми и организациями.

  • Заключение


  • Итак, мы рассмотрели, как создавать таблицы с помощью SQL-выражений. Этот процесс, хотя и не столь удобен, как интерактивное средство Database Desktop, однако обладает наиболее гибкими возможностями по настройке Вашей системы и управления ее связями.

    Delphi очень серьезно поработали над

    Примеры вызовов API Windows в Delphi

    Содержание урока:

    1.0 Обзор

    1.1 Стандартная страница Палитры Компонент

    TMainMenu, TPopupMenu

    TMemo

    TListBox, TComboBox

    1.2 Страница “Additional” Палитры Компонент

    TSpeedButton

    TTabSet, TNoteBook

    TTabbedNoteBook

    1.3 Страница “System” Палитры Компонент

    TOLEContainer

    1.4 Страница “Data Access” Палитры Компонент

    TDataSource

    TTable, TQuery

    1.5 Страница “Data Controls” Палитры Компонент

    TDBGrid

    1.6 Окна в Delphi

    1.7 Обработка событий от клавиатуры

    2.0 Вызов методов дальних предков

    Обзор

    Разработчики библиотеки визуальных компонент (VCL) Delphi очень серьезно поработали над ее проектированием и воплощением в реальность. Как показывает практика, стандартного набора объектов обычно достаточно для создания реальных приложений. И, что более существенно, им (разработчикам) удалось решить очень сложную задачу, стоящую перед создателями любой среды визуального программирования - скрыть от программиста сложность и трудоемкость программирования в Windows и, в то же время, не лишать его возможности доступа к тем богатым возможностям системы, которые предоставляет Windows API.

    В данной главе на примерах показано, как с помощью вызовов Windows API можно управлять объектами из VCL. Кроме того, здесь же можно найти описание некоторых программных трюков, которые помогут придать вашей программе оригинальный вид.





  • 1.1 Стандартная страница Палитры Компонент

    Компоненты, расположенные на странице “Standard”, представляют из себя объектную оболочку для стандартных управляющих элементов Windows. Поэтому для них существуют ограничения, накладываемые самой системой. Например, 32Кб - максимальный размер текста в TMemo.

    TMainMenu, TPopupMenu

    Добавление картинки (BitMap) в меню.

    Для добавления в меню картинки можно использовать функцию API Windows SetMenuItemBitmaps(), например, следующим образом:



    implementation



    var

    BMP1, BMP2 : TBitMap;



    procedure TForm1.FormCreate(Sender: TObject);

    begin

    BMP1:=TBitMap.Create;

    BMP1.LoadFromFile('c:\images\uncheck.bmp');


    BMP2:=TBitMap.Create;

    BMP2.LoadFromFile('c:\images\check.bmp');

    SetMenuItemBitmaps(File1.Handle, 1, MF_BYPOSITION, BMP1.Handle, BMP2.Handle);

    end;

    procedure TForm1.FormDestroy(Sender: TObject);

    begin

    BMP1.Free;

    BMP2.Free;

    end;

    File1 это объект класса TMenuItem - пункт меню “File”. Значения параметров при вызове функции можно посмотреть в справочнике по Windows API.

    При уничтожении меню освобождения связанных с ним картинок не происходит и их надо уничтожать вручную.

    Вторая картинка BMP2 отображается рядом с пунктом меню, когда он выбран (Checked=True).

    TMemo

    Компонент класса TMemo может содержать до 32К текста (для Windows 3.x) вследствие ограничения Windows. В Delphi 2.0 предел увеличен до 64К (в декабрьской бета-версии).

    I. Определение позиции каретки в TMemo.

    Можено использовать сообщения Windows API EM_LINEFROMCHAR и EM_LINEINDEX для определения текущей позиции каретки.

    procedure TForm1.ShowPosition;

    var

    LineNum: longint;

    CharNum: longint;

    begin

    LineNum:= Memo1.Perform(EM_LINEFROMCHAR, Memo1.SelStart,0);

    CharNum:= Memo1.Perform(EM_LINEINDEX, LineNum, 0);

    Label1.Caption := 'Line : '+ IntToStr(LineNum+1);

    Label2.Caption := 'Position :' + IntToStr((Memo1.SelStart -

    CharNum)+1);

    end;

    Метод Perform, определенный в классе TControl, посылает сообщение своему же объекту, то есть его использование имеет тот же эффект, что и вызов функции API SendMessage():

    SendMessage(Memo1.Handle,EM_LINEFROMCHAR, Memo1.SelStart,0);

    II. Операция UnDo в TMemo.

    Отмена последнего редактирования (операция UnDo) в объекте класса TMemo выполняется так же с помощью сообщений, посылаемых в данный объект:

    procedure TForm1.UnDoClick(Sender: TObject);

    begin

    if Memo1.Perform(EM_CANUNDO, 0, 0)<>0 then

    Memo1.Perform(EM_UNDO, 0, 0);

    end;

    В справочнике по Windows API описаны сообщения, которые можно послать в объект TMemo для управления его поведением. Кроме вышеназванных, имеется еще несколько полезных:

    EM_EMPTYUNDOBUFFER

    Сбрасывает флажок UnDo

    EM_GETHANDLE


    Получает указатель на буфер с текстом

    EM_LINESCROLL

    Прокрутка текста в окне TMemo

    EM_SETHANDLE

    Установка указателя на буфер с текстом

    EM_SETTABSTOPS

    Устанавливает табуляцию в окне с текстом

    TListBox, TComboBox

    Windows накладывает ограничение на количество элементов в списке этих управляющих элементов. В случае Windows 3.x это количество равно 5440, в Windows’95 - 32767.

    I. Как получить горизонтальную прокрутку (scrollbar) в ListBox?

    Так же как в случае с TMemo, здесь можно использовать сообщения. Например, сообщение может быть отослано в момент создания формы:

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    ListBox1.Perform(LB_SETHORIZONTALEXTENT, 1000, Longint(0));

    end;

    Второй параметр в вызове - ширина прокрутки в точках.

    II.

    Вставка графики в ListBox.

    У класса TListBox

    (и TComboBox тоже) есть свойство Style,

    определяющее порядок рисования объекта. По-умолчанию оно установлено в lbStandard и за внешний вид объекта отвечает Windows. Если установить это значение в lbOwnerDrawFixed

    или lbOwnerDrawVariable, то можно несколько разнообразить внешний вид объекта. Давайте построим для примера ListBox, отображающий названия файлов формата .BMP из какой-либо директории вместе с их картинками.

    Прежде всего, оказывается, что вовсе не нужно заполнять ListBox вручную именами файлов, для этого достаточно послать ему сообщение:

    procedure TForm1.Button1Click(Sender: TObject);

    var

    s : String;

    begin

    s:='c:\windows\*.bmp'#0;

    ListBox1.Perform(LB_DIR, DDL_READWRITE, Longint(@s[1]));

    end;

    Здесь мы указали ListBox’у, какие файлы требуется отображать.

    Далее, как уже было сказано, свойство Style нужно установить в lbOwnerDrawFixed и создать обработчик события OnDrawItem:

    procedure TForm1.ListBox1DrawItem(Control: TWinControl;

    Index: Integer; Rect: TRect; State: TOwnerDrawState);

    var

    Bitmap: TBitmap;

    Offset: Integer;

    BMPRect : TRect;

    begin

    with (Control as TListBox).Canvas do

    begin

    {очищаем прямоугольник}

    FillRect(Rect);


    {считываем картинку}

    Bitmap:=TBitMap.Create;

    Bitmap.LoadFromFile('c:\windows\'+ListBox1.Items[Index]);

    if Bitmap <> nil then begin

    {вычисляем квадрат для показа картинки}

    BMPRect:=Bounds(Rect.Left + 2, Rect.Top + 2,

    Rect.Bottom-Rect.Top-2, Rect.Bottom-Rect.Top-2);

    {рисуем картинку}

    StretchDraw(BMPRect, BitMap);

    Offset := Rect.Bottom-Rect.Top + 6;

    end;

    {выводим текст}

    TextOut(Rect.Left+Offset,Rect.Top,Listbox1.Items[Index]);

    {не забыть освободить!}

    Bitmap.Free;

    end;

    end;

    Чтобы картинки получились побольше, значение свойства ItemHeight можно увеличить.

    Есть около двух десятков сообщений, которые можно послать в объекты класса TListBox и TComboBox. Подробнее о них можно узнать в справочнике по Windows API (on-line Help).





  • 1.2 Страница “Additional” Палитры Компонент

    Компоненты, размещенные на этой странице представляют из себя объектную оболочку для управляющих элементов, появившихся в более поздних версиях Windows.

    TSpeedButton

    I.

    Эмуляция потери фокуса.

    Особенность этой кнопки в том, что она никогда не получает фокус и, следовательно, при нажатии на нее, текущий активный элемент фокус не теряет. В некоторых случаях бывает необходимо эмулировать потерю фокуса активным объектом. Пример, при нажатии кнопки, данные из объектов типа TEdit записываются в файл, на событие OnExit для них (объектов) установлена процедура верификации. В этом случае надо вызывать обработчик в явном виде:

    procedure TForm1.SpeedButton1Click(Sender: TObject);

    begin

    if ActiveControl is TEdit then

    (ActiveControl as TEdit).OnExit(ActiveControl);

    end;

    Обработчик события должен быть определен, иначе возникнет GPF.

    II. Обработка двойного щелчка мышью.

    Если значение свойства GroupIndex равно 0, то двойное нажатие будет воспринято объектом как два одиночных. Если требуется, чтобы в результате двойного щелчка кнопка не фиксировалась в нажатом состоянии, то ее свойство AllowAllUp устанавливается в True, и в обработчике события кнопка возвращается в прежнее состояние:


    procedure TForm1.SpeedButton1DblClick(Sender: TObject);

    begin

    SpeedButton1.Down:= Not SpeedButton1.Down;

    Do_Something;

    end;

    TTabSet, TNoteBook

    Ограничения по количеству страниц для этих объектов - 16364 (еще одно “магическое число” из класса TList). Но, на самом деле, не имеет смысла создавать более сотни страниц.

    I. Переход на страницу по ее имени.

    Если объект типа TTabSet содержит большое количество страниц, то пролистывать их в поисках нужной - дело утомительное. Проще найти ее по имени. Предположим, что имя страницы вводится в Edit1 :

    procedure TMultPageDlg.Edit1Change(Sender: TObject);

    var

    i : Integer;

    s1, s2: String;

    begin

    s1:=Edit1.Text;

    if s1 = '' then Exit;

    for i:=TabSet.Tabs.Count-1 downto 0 do begin

    s2:=Copy(TabSet.Tabs[i], 1, Ord(s1[0]));

    if CompareText(s1, s2)<=0 then

    TabSet.TabIndex:=i;

    end;

    end;

    TTabbedNoteBook

    I.

    Добавление новых объектов во время выполнения программы.

    После создания нового объекта, нужно в его свойстве Parent указать требуемую страницу TabbedNotebook:

    ...

    var

    Btn : TButton;

    begin

    Btn := TButton.Create(Self);

    Btn.Parent:=TWinControl(TabbedNotebook1.Pages.Objects[1]);

    ...

    end;







  • 1.3 Страница “System” Палитры Компонент

    TOLEContainer

    TOLEContainer - компонент, который нужно использовать во время дизайна с осторожностью, поскольку вся информация о нем сохраняется в .DFM файле. И если попытаться использовать встроенный (embedded) OLE-объект большого размера, то при сохранении формы может возникнуть ошибка “Out of resource”.

    I.

    Хранение OLE-объектов в базе данных

    Использование технологии OLE для хранения информации в базе данных оправдано, если эта информация неоднородна. Иногда возникает необходимость одновременно сохранять в таблице и использовать в дальнейшем графические изображения, документы в формате Microsoft Word, звук и тому подобное. К сожалению, в стандартном наборе компонент отсутствует компонент вроде TDbOleContainer. Однако, обойтись без него можно достаточно просто.


    Для хранения OLE- объекта подойдет поле типа BLOB, а для работы с этим объектом - существующий компонент TOLEContainer. Если предполагается использовать таблицу на разных компьютерах, то OLE-объект нужно делать встроенным (embedded) и следить за тем, чтобы присутствовали соответствующие OLE-серверы.

    Итак, сперва нужно заполнить таблицу:

    {создание и инициализация OLE-объекта во время выполнения}

    procedure TForm1.OLEInitClick(Sender: TObject);

    var

    OLE : TOLEContainer;

    Info : Pointer;

    begin

    {создание OLE-контейнера}

    OLE:=TOLEContainer.Create(Self);

    OLE.Name:='Temp_OLE';

    OLE.Parent:=Self;

    {вызов диалога инициализации OLE-объекта}

    if InsertOLEObjectDlg(Self, 0, Info) then begin

    OLE.PInitInfo := Info;

    ReleaseOLEInitInfo(Info);

    end

    else

    OLE.Free;

    end;

    {уничтожение OLE-контейнера}

    procedure TForm1.OLEFreeClick(Sender: TObject);

    begin

    FindComponent('Temp_OLE').Free;

    end;

    {сохранение OLE-объекта в BLOB поле}

    procedure TForm1.InsertClick(Sender: TObject);

    var

    OLE : TOLEContainer;

    TBS : TBlobStream;

    begin

    OLE:=FindComponent('Temp_OLE') as TOLEContainer;

    if Assigned(OLE) then

    with OLE do begin

    {добавляем в таблицу новую запись}

    Table1.Insert;

    {создаем поток, связанный с BLOB полем}

    TBS:=TBlobStream.Create(Table1.FieldByName('OLE') as

    TBLOBField, bmReadWrite);

    {сохраняем OLE-объект в поток}

    SaveToStream(TBS as TStream);

    {уничтожаем поток}

    TBS.Free;

    Exit;

    end;

    end;

    {завершаем редактирование таблицы}

    procedure TForm1.PostClick(Sender: TObject);

    begin

    if Table1.State <> dsBrowse then Table1.Post;

    end;

    Вторая часть проблемы - чтение OLE-объекта из таблицы и обновление информации при его модификации. Здесь можно предложить динамически создавать OLE-контейнер и считывать OLE-объект из потока, связанного с BLOB полем. И делать это каждый раз при переходе на новую запись (перехватывать для DataSource событие OnDataChange). Либо вытаскивать информацию по запросу пользователя, а уничтожать OLE-контейнер при переходе на новую запись.


    {считывание OLE-объекта из таблицы}

    procedure TForm1.ViewClick(Sender: TObject);

    var

    OLE : TOLEContainer;

    TBS : TBLOBStream;

    begin

    {создаем OLE-контейнер}

    OLE:=TOLEContainer.Create(Self);

    OLE.Parent:=Self;

    OLE.Name:='Temp_OLE';

    {создаем поток для BLOB поля}

    TBS:=TBLOBStream.Create(Table1.FieldByName('OLE') as

    TBLOBField, bmRead);

    {инициализируем OLE-объект}

    OLE.LoadFromStream(TBS as TStream);

    {уничтожаем поток}

    TBS.Free;

    end;

    {сохранение модифицированного OLE-объекта}

    procedure TForm1.SaveClick(Sender: TObject);

    var

    TBS : TBLOBStream;

    OLE : TOLEContainer;

    begin

    {находим объект}

    OLE:=FindComponent('Temp_OLE') as TOLEContainer;

    if Assigned(OLE) then

    with OLE do

    {сохраняем, если был модифицирован}

    if Modified then begin

    Table1.Edit;

    {очищаем BLOB поле}

    TBLOBField(Table1.FieldByName('OLE')).Clear;

    {создаем поток}

    TBS:= TBlobStream.Create(Table1.FieldByName('OLE') as

    TBLOBField, bmReadWrite);

    {сохраняем объект}

    SaveToStream(TBS as TStream);

    {уничтожаем поток}

    TBS.Free;

    Table1.Post;

    end;

    end;

    {уничтожение OLE- контенера при переходе на другую запись}

    procedure TForm1.DataSource1DataChange(Sender: TObject;

    Field: TField);

    begin

    if Table1.State = dsBrowse then

    FindComponent('Temp_OLE').Free;

    end;

    В примерах выше предполагается наличие таблицы с BLOB полем, которое называется “OLE”.





  • 1.4 Страница “Data Access” Палитры Компонент

    Компоненты, расположенные на данной странице представляют из себя объектную оболочку для BDE (Borland Database Engine) - библиотеки доступа к данным. Компоненты TTable, TQuery, TStoredProc, TDataBase и TBatchMove (а так же TSession, который включен в Палитру в версии Delphi 2.0) имеют свои низкоуровневые аналоги. Хотя эти объекты достаточно полно реализуют возможности BDE, тем не менее, существуют некоторые задачи, решить которые можно только с помощью прямых вызовов функций BDE. Например, сменить пароль в таблице Paradox или упаковать таблицу dBase. Но об этом позже, а сейчас - о компонентах на странице “Data Access”.






    TDataSource

    I.

    Определение момента перехода на другую запись.

    Если в момент вызова обработчика события OnDataChange

    для TDataSource

    состояние его DataSet есть dsBrowse, то произошел переход на другую запись.

    procedure TForm1.DataSource1DataChange(Sender: TObject;

    Field: TField);

    begin

    if DataSource1.DataSet.State = dsBrowse then

    Do_Somehing;

    end;

    TTable, TQuery

    I. Проблемы при создании таблиц.

    Как известно, класс TTable имеет метод для создания таблиц CreateTable, который использует при этом структуры FieldDefs

    и IndexDefs. Однако, некоторые возможности не реализованы (видимо из соображений общности - TTable должен работать одинаково с таблицами разных форматов). Два примера на эту тему: используя TTable нельзя создать автоинкрементное поле для таблицы формата Paradox и нельзя указать разрядность и число знаков после запятой для числового поля в таблице dBase. В подобных случаях лучше использовать Local SQL (и компонент TQuery). Выражение SQL, решающее первую проблему:

  • CREATE TABLE “PDX_TBL.DB”


  • (

    KOD AUTOINC,

    FIELD1 CHAR(10),

    PRIMARY KEY (KOD)

    )

    Выражение SQL, решающее вторую проблему:

  • CREATE TABLE “DB_TBL.DBF”


  • (

    KOD INTEGER,

    FIELD1 DECIMAL (10,3)

    )

    II.

    Добавление записи в DataSet, не имеющий первичного ключа.

    Не вдаваясь в теорию реляционных СУБД, скажу, что BDE не очень хорошо работает с наборами данных, у которых нет первичного ключа и нельзя однозначно идентифицировать запись. Для программиста, использующего компонент TTable

    для доступа к таблице без первичного ключа, проблемы возникают при добавлении в такую таблицу новой записи. Для того, чтобы состояние TTable

    соответствовало состоянию физической таблицы, ее требуется переоткрыть (Close, а потом Open). Метод Refresh не поможет - он проходит только при наличии первичного ключа. После пероткрытия возникает проблема, как позиционироваться на запись, которая была текущей до переоткрытия. Найти ее можно только перебором записей, поскольку закладки (BookMark) для такой таблицы нестабильны.


    В случае с объектом класса TQuery, работающим с живым набором данных (live dataset), ситуация аналогичная. Не имеет значения, что запрос выполняется для таблицы, имеющей первичный ключ - результатом такого запроса все равно является набор данных, у которого нет ни первичного ключа, ни индексов.

    III.

    Определение номера текущей записи.

    Понятие номера (физического) текущей записи имеет некоторый смысл при работе с таблицами в формате dBase. Этот номер меняется достаточно редко, только при упаковке удаленных записей в таблице. В случае таблиц Paradox можно говорить только о логическом номере записи: место записи меняется при добавлении/удалении новой записи или при наложении индекса. Для данных на SQL сервере понятия “номер записи” не имеет смысла вообще (этого понятия нет в теории реляционных баз данных).

    Так как компоненты TQuery и TTable должны работать одинаково с данными различного формата, то у них нет свойства, отражающего номер записи.

    Так как же быть программисту, если он работает с данными в формате dBase и желает, все-таки, что бы номер записи присутствовал на форме? Ему придется использовать прямой вызов функции BDE (подключив сперва к программе модули DBITYPES и DBIPROCS):

    function TForm1.GetRecNo : Longint;

    var

    Pr : RECProps;

    begin

    dbiGetRecord(Table1.Handle, dbiNoLock, NIL, @Pr);

    Result:=Pr.iPhyRecNum;

    end;

    Функция GetRecNo

    возвращает номер записи, ее можно использовать, например, для определения вычисляемого поля (обработчик OnCalcFields) и отражать это поле в DBGrid первым.

    Более подробно о вызовах BDE будет рассказано позже.





  • 1.5 Страница “Data Controls” Палитры Компонент

    TDBGrid

    Это наиболее применяемый в программах компонент для отображения данных. Понятно, что задачи бывают очень разными и требования к TDBGrid также очень разнообразны. Но в первой версии Delphi этот объект достаточно прост, он не умеет отображать графические и мемо-поля, в него нельзя поместить CheckBox или ComboBox. Есть достаточно много претензий к данному компоненту, но есть уверенность, что в Delphi 2.0 многие проблемы, связанные с этим компонентом, решены. Однако, многое можно сделать и с той версией DBGrid, что поставляется с Delphi 1.0.


    I.

    Унаследованные свойства.

    Многие проблемы можно решить простым переносом некоторых свойств и методов из раздела protected в декларации класса TDBGrid в разделы public и published.

    Лучше всего создать новый класс - наследника от TDBGrid:

    TNewDBGrid = class(TDBGrid)

    public

    {прямоугольник для указанной ячейки}

    function CellRect(ARow, AСol : Integer) : TRect;

    {текущий столбец}

    property Col;

    {текущая строка}

    property Row;

    {счетчик столбцов}

    property ColCount;

    {ширина столбцов}

    property ColWidths;

    {счетчик строк}

    property RowCount;

    {высота строк}

    property RowHeights;

    published

    {количество “зафиксированных” столбцов}

    property FixedCols;

    {обработчики событий от мыши}

    property OnClick;

    property OnMouseMove;

    property OnMouseUp;

    property OnMouseDown;

    end;

    . . .

    function TNewDBGrid.CellRect(ARow, Acol : Integer) : TRect;

    begin

    Result:= inherited CellRect(ARow, ACol);

    end;

    В примере приведены не все возможные методы и свойства для публикации. Некоторые могут быть полезны. Информацию об этих свойствах и методах можно подчерпнуть из исходных текстов библиотеки и в пункте меню среды Delphi “View|Browser”. Но некоторые свойства, вроде FixedRows, работают не так как ожидается, поскольку их значения переопределяется внутри объекта.

    II.

    Вертикальный ScrollBar.

    Обычно возникает вопрос, почему позиция движка в вертикальном ScrollBar’е не соответствует позиции текущей записи в таблице. Ответ частично был дан в предыдущем пункте: не всегда можно говорить о номере текущей записи. Но при желании можно заставить ScrollBar отображать позицию текущей записи в таблице, разумеется, только в случае локальных данных. Например так:

    procedure TForm1.DataSource1DataChange(Sender: TObject;

    Field: TField);

    begin

    if Table1.State = dsBrowse then begin

    SetScrollRange(DBGrid1.Handle, SB_VERT, 0,

    Table1.RecordCount-1, False);

    SetScrollPos(DBGrid1.Handle, SB_VERT, GetRecNo-1, False);

    end;

    end;

    Процедура GetRecNo (приведена в предыдущем пункте) возвращает номер записи. Недостаток данного способа состоит в том, что при переходе на другую запись движок будет перерисовываться два раза, один раз в процедуре Paint, а второй раз в OnDataChange.


    III.

    Обработчик события OnDrawDataCell.

    Большое количество задач можно решить, если использовать событие перерисовки ячейки в DBGrid. Например: выделить цветом отрицательные значения или целиком колонку, вывести картинку в ячейке DBGrid, поместить DBCheckBox или DBComboBox в ячейку и многое другое. Ниже приведен вариант, в котором отрицательные значения выделяются красным цветом:

    procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const

    Rect: TRect; Field: TField; State: TGridDrawState);

    begin

    with DBGrid1.Canvas do begin

    if (Not (gdFocused in State)) and

    (Field.FieldName='BALANCE') and

    (Field.AsFloat<0) then begin

    Brush.Color:=clRed;

    Font.Color:=clWhite;

    end;

    FillRect(Rect);

    TextOut(Rect.Left, Rect.Top, Field.Text);

    end;

    end;





  • 1.6 Окна в Delphi

    Большинство видимых компонентов в Delphi (все компоненты, имеющие предком класс TWinControl) являются для Windows полноценными окнами. При доступе к этим компонентам из Windows API используется свойство Handle каждого компонента. Ниже приводится несколько “фокусов”, которые можно произвести с окнами компонентов, включая TForm.




  • “Перетаскивание” окна без помощи Caption.


  • Достаточно известен способ “перетаскивания” окна приложения без Caption (caption - поле заголовка вверху формы), этот способ описан и в разделе технической информации на CD-ROM с Delphi 1.02.

    Для решения этой задачи нужно переопределить обработчик события WM_NCHITTEST для окна следующим образом:

    type

    TForm1 = class(TForm)

    . . .

    private

    procedure WMNCHitTest(var M: TWMNCHitTest);

    message wm_NCHitTest;

    . . .

    end;

    . . .

    procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);

    begin

    {вызов унаследованного обработчика события}

    inherited;

    {если событие произошло в клиентской области,}

    if M.Result = htClient then

    {то пусть Windows думает, что это произошло на Caption}

    M.Result := htCaption;

    end;

    Теперь окно можно будет переместить на экране, даже если оно совсем не имеет Caption. Однако, неприятно то, что для такого окна не будут вызываться обработчики событий, связанных с мышкой (OnClick, OnMouseMove и т.д.).


    Все, что было сейчас проделано с формой ( объектом класса TForm) можно применить и к остальным оконным компонентам. Нам никто не запрещает и для них переопределить обработку события WM_NCHITTEST.

    II. Объекты, перетаскиваемые во время работы программы.

    Проще всего было бы решить эту задачу переопределением соответствующего обработчика и добавлением нового свойства для класса TWinControl. Однако, после этого перекомпилировать библиотеку будет трудно, так как не все модули предоставлены в исходных текстах. Поэтому, приведу решение задачи на примере класса TMemo.

    type

    TMoveMemo = class(TMemo)

    private

    FMoveable : Boolean;

    procedure WMNCHitTest(var M : TWMNCHitTest);

    message WM_NCHitTest;

    published

    property Moveable:Boolean read FMoveable write FMoveable;

    end;

    . . .

    procedure TMoveMemo.WMNCHitTest(var M : TWMNCHitTest);

    begin

    inherited;

    if (not (csDesigning in ComponentState)) and FMoveable then

    if M.Result = htClient then

    M.Result:=htCaption;

    end;

    Свойство Moveable

    определяет, можно ли объект перетаскивать. Если бы все оконные объекты имели такой обработчик события и свойство Moveable, то было бы очень просто построить приложение с изменяемым во время работы программы пользовательским интерфейсом.

    III.

    Внешний вид окна.

    Как известно, внешний вид окна в Windows определяется при его создании параметром Style (тип Longint). Полный список возможных значений этого параметра можно найти в справочнике по Windows API, все они начинаются с префикса WS_ (например, WS_CAPTION, WS_MAXIMIZED).

    Изменить внешний вид окна во время выполнения программы можно, если изменить значение его параметра Style. Для этого используется вызов функции API SetWindowLong(). Конечно, в случае формы (TForm) можно обойтись свойствами этого класса (но не всегда). Но в случае с обычными оконными компонентами получается забавный эффект. Попробуйте изменить стиль обычной кнопки (TButton):

    procedure TForm1.Button2Click(Sender: TObject);

    var

    Style : Longint;

    begin

    {старый стиль окна}


    Style:=GetWindowLong(Button1.Handle, GWL_STYLE);

    {меняем стиль окна}

    Style:=Style or WS_OVERLAPPEDWINDOW;

    SetWindowLong(Button1.Handle, GWL_STYLE, Style);

    {обновление окна (Invalidate не сработает)}

    SetWindowPos(Button1.Handle, HWND_TOP, 0, 0, 0, 0,

    SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_DRAWFRAME

    or SWP_NOACTIVATE);

    end;

    Указать свой стиль окна во время его создания можно, если переопределить для любого оконного объекта (включая TForm) процедуру CreateParams:

    type

    TForm1 = class(TForm)

    . . .

    procedure CreateParams(var Params:TCreateParams);

    override;

    . . .

    end;

    . . .

    procedure TForm1.CreateParams(var Params: TCreateParams);

    begin

    inherited CreateParams(Params);

    Params.Style:=Params.Style and not WS_CAPTION;

    end;

    IV.

    Распахивание (maximize) и захлопывание (minimize) окон.

    Иногда нужно, чтобы окно при нажатии кнопки “Maximize” распахивалось не на весь экран, а только на часть его. Вспомните редактор исходных текстов в среде Delphi. Эта задача, как и многие другие, решается написанием обработчика соответствующего события - WM_GETMINMAXINFO. Это сообщение посылается системой в окно при максимизации окна или при изменении его размеров с помощью рамки. Окно возвращает требуемые размеры и положение.

    TForm1 = class(TForm)

    . . .

    private

    procedure WMGetMinMaxInfo(var M: TWMGetMinMaxInfo);

    message WM_GETMINMAXINFO;

    . . .

    end;

    . . .

    procedure TForm1.WMGetMinMaxInfo(var M: TWMGetMinMaxInfo);

    begin

    {на всякий случай}

    inherited;

    {указываем верхнюю границу окна ниже другого окна}

    M.MinMaxInfo^.PTMaxPosition.Y := Form2.Top+Form2.Height;

    end;

    С помощью обработчика данного события устанавливается не только место и размер распахнутого полностью окна, но и минимальные/максимальные размеры окна при изменении их с помощью рамки. Пример, установка в обработчике события WM_GETMINMAXINFO

    m.minmaxinfo^.ptMinTrackSize.y := 100;

    m.minmaxinfo^.ptMaxTrackSize.y := 300;

    не даст сделать вертикальный размер окна менее 100 и более 300 точек.








  • 1.7 Обработка событий от клавиатуры

    I. Эмуляция нажатия клавиши.

    Внутри приложения это выполняется достаточно просто с помощью вызова функции Windows API SendMessage() (можно воспользоваться и методом Perform того объекта (или формы), кому посылается сообщение о нажатой клавише).

    Код

    Memo1.Perform(WM_CHAR, Ord(‘A’), 0);

    или

    SendMessage(Memo1.Handle, WM_CHAR, Ord(‘A’), 0);

    приведет к печати символа “A” в объекте Memo1.

    II.

    Перехват нажатий клавиши внутри приложения.

    Задача решается очень просто. Можно у формы установить свойство KeyPreview в True и обрабатывать событие OnKeyPress. Второй способ - перехватывать событие OnMessage для объекта Application.





    III.

    Перехват нажатия клавиши в Windows.

    Существуют приложения, которым необходимо перехватывать все нажатия клавиш в Windows, даже если в данный момент активно другое приложение. Это может быть, например, программа, переключающая раскладку клавиатуры, резидентный словарь или программа, выполняющая иные действия по нажатию “горячей” комбинации клавиш.

    Перехват всех событий в Windows (в том числе и событий от клавиатуры) выполняется с помощью вызова функции SetWindowsHook().

    Данная функция регистрирует в системе Windows ловушку (hook) для определенного типа событий/сообщений. Ловушка - это пользовательская процедура, которая будет обрабатывать указанное событие.

    Основное здесь то, что эта процедура должна всегда присутствовать в памяти Windows. Поэтому ловушку помещают в DLL и загружают эту DLL из программы. Пока хоть одна программа использует DLL, та не может быть выгружена из памяти. Приведем пример такой DLL и программы, ее использующей. В примере ловушка перехватывает нажатие клавиш на клавиатуре, проверяет их и, если это клавиши “+” или “-”, посылает соответствующее сообщение в конкретное приложение (окно). Окно ищется по имени его класса (“TForm1”) и заголовку (caption, “XXX”).

    {текст библиотеки}

    library SendKey;

    uses

    WinTypes, WinProcs, Messages;

    const


    {пользовательские сообщения}

    wm_NextShow_Event = wm_User + 133;

    wm_PrevShow_Event = wm_User + 134;

    {handle для ловушки}

    HookHandle: hHook = 0;

    var

    SaveExitProc : Pointer;

    {собственно ловушка}

    function Key_Hook(Code: integer; wParam: word; lParam: Longint): Longint; export;

    var

    H: HWND;

    begin

    {если Code>=0, то ловушка может обработать событие}

    if Code >= 0 then

    begin

    {это те клавиши?}

    if ((wParam = VK_ADD)or(wParam = VK_SUBTRACT)) and

    (lParam and $40000000 = 0) then

    begin

    { ищем окно по имени класса и по заголовку}

    H := FindWindow('TForm1', 'XXX');

    {посылаем сообщение}

    if wParam = VK_ADD then

    SendMessage(H, wm_NextShow_Event, 0, 0)

    else

    SendMessage(H, wm_PrevShow_Event, 0, 0);

    end;

    {если 0, то система должна дальше обработать это событие}

    {если 1 - нет}

    Result:=0;

    end

    else

    {если Code<0, то нужно вызвать следующую ловушку}

    Result := CallNextHookEx(HookHandle,Code, wParam, lParam);

    end;

    {при выгрузке DLL надо снять ловушку}

    procedure LocalExitProc; far;

    begin

    if HookHandle<>0 then

    begin

    UnhookWindowsHookEx(HookHandle);

    ExitProc := SaveExitProc;

    end;

    end;

    {инициализация DLL при загрузке ее в память}

    begin

    {устанавливаем ловушку}

    HookHandle := SetWindowsHookEx(wh_Keyboard, Key_Hook,

    hInstance, 0);

    if HookHandle = 0 then

    MessageBox(0, 'Unable to set hook!', 'Error', mb_Ok)

    else begin

    SaveExitProc := ExitProc;

    ExitProc := @LocalExitProc;

    end;

    end.

    Размер такой DLL в скомпилированном виде будет около 3Кб, поскольку в ней не используются объекты из VCL.

    Далее приведен код модуля в Delphi, который загружает DLL и обрабатывает сообщения от ловушки, просто отображая их в Label1.

    unit Unit1;

    interface

    uses

    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

    {пользовательские сообщения}

    const

    wm_NextShow_Event = wm_User + 133;

    wm_PrevShow_Event = wm_User + 134;

    type

    TForm1 = class(TForm)

    Label1: TLabel;

    procedure FormCreate(Sender: TObject);


    private

    {обработчики сообщений}

    procedure WM_NextMSG (Var M : TMessage);

    message wm_NextShow_Event;

    procedure WM_PrevMSG (Var M : TMessage);

    message wm_PrevShow_Event;

    end;

    var

    Form1: TForm1;

    P : Pointer;

    implementation

    {$R *.DFM}

    {загрузка DLL}

    function Key_Hook : Longint; far; external 'SendKey';

    procedure TForm1.WM_NextMSG (Var M : TMessage);

    begin

    Label1.Caption:='Next message';

    end;

    procedure TForm1.WM_PrevMSG (Var M : TMessage);

    begin

    Label1.Caption:='Previous message';

    end;

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    { если не использовать вызов процедуры из DLL в программе,

    то компилятор удалит загрузку DLL из программы}

    P:=@Key_Hook;

    end;

    end.

    Конечно, свойство Caption в этой форме должно быть установлено в “XXX”.





  • 2.0 Вызов методов дальних предков

    Проблема в следующем. Допустим, есть иерархия классов, у которых перекрывается (override) один и тот же виртуальный (или динамический - не важно) метод, и в одной из реализаций этого метода вы хотите вызвать виртуальный метод предка своего предка (“дедушки”, “прадедушки” и т.д.). Новая объектная модель Delphi допускает только вызов методов непосредственного предка (с помощью ключевого слова inherited) либо вызов методов класса с префиксом - типом класса (например, TLevel1.ClassName).

    Эта задача стандартными средствами не решается. Но сделать требуемый вызов можно. Причем, способом, показанным ниже, можно вызвать любой

    метод для любого класса, однако, в этом случае вся ответственность за правильность работы с методами и полями ложится на программиста. В примере, приведенном ниже, в методе VirtualFunction класса

    TLevel3 напрямую вызывается метод класса

    TLevel1 (метод “деда”). В функции Level1Always

    всегда вызывается метод класса TLevel1 для любого его наследника.

    {декларация классов}

    TLevel1 = class(TComponent)

    public

    function VirtualFunction: string; virtual;

    end;

    TLevel2 = class(TLevel1)

    public

    function VirtualFunction: string; override;

    end;

    TLevel3 = class(TLevel2)


    public

    function VirtualFunction: string; override;

    end;

    function Level1Always(MyLevel: TLevel1): string;

    implementation

    type

    PClass = ^TClass;

    {виртуальная функция “дедушки”}

    function TLevel1.VirtualFunction: string;

    begin

    Result := 'Level1';

    end;

    {виртуальная функция “отца”, вызывает унаследованный метод “дедушки”}

    function TLevel2.VirtualFunction: string;

    begin

    Result := inherited VirtualFunction+' Level2';

    end;

    { виртуальная функция самого младшего класса, вызывает

    напрямую метод “дедушки”, минуя “отца”}

    function TLevel3.VirtualFunction: string;

    var

    ClassOld: TClass;

    begin

    ClassOld := PClass(Self)^;

    PClass(Self)^ := Self.ClassParent.ClassParent;

    Result := VirtualFunction + ' Level3';

    PClass(Self)^ := ClassOld;

    end;

    function Level1Always(MyObject: TObject): string;

    var

    ClassOld: TClass;

    begin

    ClassOld := PClass(MyObject)^;

    PClass(MyObject)^ := TLevel1;

    Result := (MyObject as TLevel1).VirtualFunction;

    PClass(MyObject)^ := ClassOld;

    end;

    Как же это работает? Стандартные, так называемые объектные типы (object types - class of ...), на самом деле представляют из себя указатель на VMT (Virtual Method Table) - таблицу виртуальных методов, который (указатель) лежит по смещению 0 в экземпляре класса. Воспользовавшись этим, мы сначала сохраняем 'старый тип класса' - указатель на VMT, присваиваем ему указатель на VMT нужного класса, делаем вызов и восстанавливаем все как было. Причем нигде не требуется, чтобы один из этих классов был бы порожден от другого, т.е. функция Level1Always вызовет требуемый метод для любого экземпляра любого класса.

    Если в функции Level1Always попробовать сделать вызов

    Result := MyObject.VirtualFunction;

    то будет ошибка на стадии компиляции, так как у класса TObject нет метода VirtualFunction.

    Другой вызов

    Result := (MyObject as TLevel3).VirtualFunction;

    будет пропущен компилятором, но вызовет Run-time ошибку, даже если передается экземпляр класса TLevel3 или один из его потомков, так как информация о типе объекта была изменена.

    Динамически распределяемые (dynamic) методы можно вызывать точно таким же образом, т.к. информация о них тоже хранится в VMT.

    Статические методы объектов вызываются гораздо более простым способом, например

    var

    MyLevel3: TLevel3;

    ...

    (MyLevel3 as TLevel1).SomeMethode;

    вызовет метод класса TLevel1 даже если у MyLevel3

    есть свой такой же метод.

    Методы TReport

  • Методы TReport включают:

  • CloseReport прекращает печать отчета.
    CloseApplication
    прекращает выполнение ReportSmith Runtime, если он запущен.
    Connect служит для установления соединения с SQL БД.
    Print - функция, проверяет, идет ли печать в данный момент.
    RecalcReport
    пересчитывает и перепечатывает отчет с новым значением для переменной отчета, предварительно измененной методом SetVariable.
    Run
    запускает ReportSmith Runtime, выполняет отчет указанный в свойстве ReportName, и посылает отчет на принтер.
    RunMacro - вызывает выполнение в ReportSmith макроса (программы, написанной на ReportSmith Baisc).
    SetVariable
    изменяет значение переменной отчета. Параметр Name определяет, какая переменная изменяется, и параметр Value определяет новое значение. После вызова метода SetVariable, ваше приложение может вызывать метод RecalcReport, который пересчитывает и перепечатывает отчет с новым значением переменой.


  • Модель исключительных ситуаций в Delphi

  • Модель исключительных ситуаций в Object Pascal является невозобновляемой(non-resumable). При возникновении исключительной ситуации Вы уже не сможете вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая(resumable) модель). Невозобновляемые исключительные ситуации разрушают стек, поскольку они сканируют его в поисках обработчика; в возобновляемой модели необходимо сохранять стек, состояние регистров процессора в точке возникновения ошибки и выполнять поиск обработчика и его выполнение в отдельном стеке. Возобновляемую систему обработки исключительных ситуаций гораздо труднее создать и применять, нежели невозобновляемую.


  • Мультимедиа в Delphi

  • В Delphi есть компонент TMediaPlayer, который дает Вам доступ ко всем основным возможностям программирования мультимедиа. Данный компонент очень прост в использовании. Фактически, он настолько прост, что многим начинающим программистам будет проще создать свою первую программу, проигрывающую видео или музыку, нежели показывающую классическую надпись "Hello World".

  • Простоту использования можно воспринимать двояко:
    · С одной стороны - это дает возможность любому создавать мультимедиа приложения.
    · С другой стороны, можно обнаружить, что в компоненте реализованы не все возможности. Если Вы захотите использовать низкоуровневые функции, то придется копаться достаточно глубоко, используя язык Delphi.
    В данном уроке не описываются подробности внутренних вызовов мультимедийных функций при работе компонента. Все что нужно знать - это то, что компонент называется TMediaPlayer, и что он дает доступ к набору подпрограмм, созданных Microsoft и называемых Media Control Interface (MCI). Эти подпрограммы дают программисту простой доступ к широкому кругу устройств мультимедиа. Собственно работа с TMediaPlayer интуитивно понятна и очевидна.

    Некоторые технические характеристики InterBase

    Отличия Local InterBase от InterBase для других платформ, в частности, от InterBase для Windows NT:
    Local InterBase не поддерживает:

  • функции, определяемые пользователем (UDF).

  • BLOB фильтры

  • сигнализатор событий (event alerters)

  • запись через журнал (Write Ahead Log (WAL))

  • "отключение" и "включение" базы данных (database shutdown or restart)

  • ведение теневой базы данных (database shadowing)

  • Все остальные функции полностью поддерживаются, совпадает даже структура хранения базы на диске.
    Максимальный размер базы данных
    Реально ограничение на размер накладывается временем обработки запросов, временем резервного копирования, восстановления базы и т.д. Рекомендуется не более 10 GB.
    Максимальное количество физических файлов, из которых может состоять база
    В системных таблицах InterBase поле, описывающее из каких файлов состоит база данных, включая все shadow, имеет тип SHORT. Соответственно не более 65,536.
    Максимальное количество таблиц в базе данных
    65,536. Таблицы нумеруются с использованием типа данных SHORT.
    Максимальное количество записей в таблице и полей в записи
    В записи может быть не более 1000 полей. Количество записей в таблице не ограничено.
    Максимальный размер записи и поля
    Запись не может быть больше 64К байт (не считая размера BLOB). Поле не может быть больше 32К байт, размер поля типа BLOB не ограничен.
    Максимальное количество индексов в таблице и базе
    В базе может быть 64K индексов. В одной таблице - 64 индекса.
    Максимальное количество уровней вложенности SQL запроса
    16 уровней вложенности.
    Максимальное количество полей в составном индексе
    Составной индекс может включать в себя не более 16 полей.
    Максимальный размер stored procedure или trigger
    Stored procedure или trigger может иметь размер кода не более 48K байт.
    Количество UDF, определенных в одной базе
    Длина имени UDF не более 31 символа. Соответственно максимальное количество UDF в базе ограниченно количеством уникальных имен в пределах этой длины.





  • InterBase Interactive SQL


  • В поставке Delphi есть две утилиты для доступа к базам данных и администрации сервера InterBase. Утилита Windows ISQL позволяет интерактивно выполнять SQL запросы к базе данных и получать результат. Это требуется в двух случаях: для отладки SQL выражения и для управления данными и их структурой.


  • Кроме того, создать базу данных, хранимые процедуры, триггеры, и т.п. также удобнее с помощью ISQL. ISQL позволяет обращаться как к данным на удаленном сервере, так и к локальным (к Local InterBase).

    Рассмотрим порядок работы с этой программой. Прежде, чем начать работу, нужно либо установить соединение с имеющейся базой данных, либо создать новую базу.

    Установка соединения

    После запуска ISQL выберите пункт меню “File|Connect to Database…”, появится диалог (см. рис.1), в котором нужно выбрать сервер (удаленный или локальный, в данном случае мы обращаемся к Local InterBase), файл базы данных, указать имя пользователя (SYSDBA - имя системного администратора) и пароль (masterkey - пароль по умолчанию). Если все указано правильно, то по нажатию клавиши “OK” установится соединение с базой данных и можно приступать к дальнейшей работе.

    Некоторые технические характеристики InterBase


    Рис. A: Диалог соединения с базой данных.

    Создание новой базы данных

    Эту операцию можно выполнить в пункте меню “File|Create Database” (см. рис.2). В диалоге нужно указать имя файла (c:\bases\new_base.gdb), имя и пароль системного администратора (SYSDBA и masterkey), и, при необходимости, дополнительные параметры. В данном случае создается база данных, поддерживающая русскую кодовую страницу WIN1251. Если Вы собираетесь работать из ISQL с базой данных в русской кодировке, то перед установкой соединения нужно в пункте меню “Session|Advanced Settings” установить “Character set on connect” в WIN1251.

    Некоторые технические характеристики InterBase


    Рис. B: Диалог создания новой базы данных

    Получение информации о структуре базы данных

    В ISQL можно получить полную информацию о структуре базы данных: список таблиц и их структуры, списки и текст триггеров, хранимых процедур и т.п. Эту операцию можно выполнить в пункте меню View или Extract. Например, для базы данных из поставки Delphi (лежит в \IBLOCAL\EXAMPLES\EMPLOYEE.GDB), попробуем выбрать “Extract|SQL Metadata for Table” для таблицы COUNTRY. В окошке ISQL Output появится текст SQL запроса, который создавал данную таблицу:


    /* Extract Table COUNTRY */

    /* Domain definitions */

    CREATE DOMAIN COUNTRYNAME AS VARCHAR(15);

    /* Table: COUNTRY, Owner: SYSDBA */

    CREATE TABLE COUNTRY (COUNTRY COUNTRYNAME NOT NULL,

    CURRENCY VARCHAR(10) NOT NULL,

    PRIMARY KEY (COUNTRY));

    Выполнение SQL запросов

    Текст SQL запроса вводится в окошке “SQL Statement”. Для запуска его на выполнение, нажмите кнопку “Run”. На рис.3 приведен результат работы примерного запроса.

    Некоторые технические характеристики InterBase


    Рис. C: Окно ISQL с текстом и результатом выполнения SQL запроса.

  • InterBase Server Manager


  • Утилита предназначена для администрирования InterBase. С ее помощью можно выполнить следующие операции:





  • определить пользователей и их пароли


  • произвести резервное копирование


  • удалить “мусор” из базы


  • завершить/откатить зависшие транзакции


  • произвести проверку базы на наличие ошибок


  • Некоторые технические характеристики InterBase


    Рис. D: Утилита для администрирования InterBase

    Резервное копирование

    Соответствующий диалог показан на рис. 5

    Некоторые технические характеристики InterBase


    Рис. E: Диалог резервного копирования базы данных.

    Обычно, операционные системы сами предоставляют возможности по сохранению баз данных в архивах. Однако, при резервном копировании, проведенном с помощью Server Manager, выполняются дополнительные операции. При этом:





  • Увеличивается быстродействие базы. В процессе копирования/восстановления происходит “сбор мусора” - в базе данных освобождается место, занятое удаленными записями. Тем самым уменьшается физический размер базы. При восстановлении можно изменить размер страницы и разбить базу на несколько файлов.




  • Резервное копирование может выполняться на работающей базе, для этого не надо отключать пользователей от нее. При этом, изменения, вносимые в базу данных после начала процесса копирования, не записываются в резервный файл.




  • Данные можно перенести на другую операционную систему. Различные компьютеры имеют собственные форматы файлов баз данных и эти файлы нельзя просто перенести на другую операционную систему. Для выполнения этой операции нужно создать резервную копию базы в транспортном


  • формате.

    Номерами

    столбцов.
    Способ упорядочивания определяется дополнительными зарезервированными словами ASC и DESC. Способом по умолчанию - если ничего не указано - является упорядочивание “по возрастанию” (ASC). Если же указано слово “DESC”, то упорядочивание будет производиться “по убыванию”.
    Подчеркнем еще раз, что предложение ORDER BY должно указываться в самом конце запроса.





  • Упорядочивание с использованием имен столбцов

  • SELECT first_name, last_name, dept_no,

  • job_code, salary
    FROM employee
    ORDER BY last_name получить список сотрудников,

    упорядоченный по фамилиям

    в алфавитном порядке
    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY
    ============ ============= ======= ======== ===========
    Janet Baldwin 110 Sales 61637.81
    Oliver H. Bender 000 CEO 212850.00
    Ann Bennet 120 Admin 22935.00
    Dana Bishop 621 Eng 62550.00
    Kelly Brown 600 Admin 27000.00
    Jennifer M. Burbank 622 Eng 53167.50
    Kevin Cook 670 Dir 111262.50
    Roger De Souza 623 Eng 69482.62
    Roberto Ferrari 125 SRep 99000000.00
    ...
    SELECT first_name, last_name, dept_no,
    job_code, salary
    FROM employee
    ORDER BY last_name DESC получить список сотрудников,

    упорядоченный по фамилиям

    в порядке, обратном алфавитному
    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY
    ============ ============= ======= ======== ===========
    Katherine Young 623 Mngr 67241.25
    Bruce Young 621 Eng 97500.00
    Michael Yanowski 100 SRep 44000.00
    Takashi Yamamoto 115 SRep 7480000.00
    Randy Williams 672 Mngr 56295.00
    K. J. Weston 130 SRep 86292.94
    Claudia Sutherland 140 SRep 100914.00
    Walter Steadman 900 CFO 116100.00
    Willie Stansbury 120 Eng 39224.06
    Roger Reeves 120 Sales 33620.62
    ...
    Столбец, определяющий порядок вывода строк, не обязательно дожен присутствовать в списке выбираемых элементов (столбцов):
    SELECT first_name, last_name, dept_no,
    job_code
    FROM employee
    ORDER BY salary получить список сотрудников,

    упорядоченный по их зарплате
    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE
    =============== =============== ======= ========
    Ann Bennet 120 Admin

    Kelly Brown 600 Admin

    Sue Anne O'Brien 670 Admin

    Mark Guckenheimer 622 Eng

    Roger Reeves 120 Sales

    Bill Parker 623 Eng



  • Упорядочивание с использованием номеров столбцов








  • SELECT first_name, last_name, dept_no,


  • job_code, salary * 1.1

    FROM employee

    ORDER BY 5 получить список сотрудников,

    упорядоченный по их зарплате

    с 10% надбавкой

    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE

    ============ ============= ======= ======== ===========

    Ann Bennet 120 Admin 25228.5

    Kelly Brown 600 Admin 29700

    Sue Anne O'Brien 670 Admin 34402.5

    Mark Guckenheimer 622 Eng 35200

    Roger Reeves 120 Sales 36982.6875

    Bill Parker 623 Eng 38500

    Допускается использование нескольких уровней вложенности при упорядочивании выводимой информации по столбцам; при этом разрешается смешивать оба способа.







  • SELECT first_name, last_name, dept_no,


  • job_code, salary * 1.1

    FROM employee

    ORDER BY dept_no, 5 DESC, last_name

    получить список сотрудников,

    упорядоченный сначала по

    номерам отделов,

    в отделах - по убыванию их

    зарплаты (с 10%),

    а в пределах одной зарплаты - по фамилиям

    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE

    =========== ========== ======= ======== ===============

    Oliver H. Bender 000 CEO 234135

    Terri Lee 000 Admin 59172.3

    Mary S. MacDonald 100 VP 122388.75

    Michael Yanowski 100 SRep 48400.000000001

    Luke Leung 110 SRep 75685.5

    Janet Baldwin 110 Sales 67801.59375

    Takashi Yamamoto 115 SRep 8228000.0000001

    Yuki Ichida 115 Eng 6600000.0000001













  • Устранение дублирования (модификатор DISTINCT)


  • Дублированными являются такие строки в результирующей таблице, в которых идентичен каждый


  • столбец.

    Иногда

    (в зависимости от задачи) бывает необходимо устранить все повторы строк из результирующего набора. Этой цели служит модификатор DISTINCT. Данный модификатор может быть указан только один раз в списке выбираемых элементов и действует на весь список.

    SELECT job_code

    FROM employee получить список должностей сотрудников

    JOB_CODE

    ========


    VP

    Eng

    Eng

    Mktg

    Mngr

    SRep

    Admin

    Finan

    Mngr

    Mngr

    Eng

    ...

    Данный пример некорректно решает задачу “получения” списка должностей сотрудников предприятия, так как в нем имеются многочисленные повторы, затрудняющие восприятие информации. Тот же запрос, включающий модификатор DISTINCT, устраняющий дублирование, дает верный результат.

    SELECT DISTINCT job_code

    FROM employee

    получить список должностей сотрудников

    JOB_CODE

    ========

    Admin

    CEO

    CFO

    Dir

    Doc

    Eng

    Finan

    Mktg

    Mngr

    PRel

    SRep

    Sales

    VP

    Два следующих примера показывают, что модификатор DISTINCT действует на всю строку сразу.

    SELECT first_name, last_name

    FROM employee

    WHERE first_name = "Roger" получить список служащих,

    имена которых - Roger

    FIRST_NAME LAST_NAME

    =============== ====================

    Roger De Souza

    Roger Reeves

    SELECT DISTINCT first_name, last_name

    FROM employee

    WHERE first_name = "Roger" получить список служащих,

    имена которых - Roger

    FIRST_NAME LAST_NAME

    =============== ====================

    Roger De Souza

    Roger Reeves

  • Соединение (JOIN)


  • Операция соединения используется в языке SQL для вывода связанной

    информации, хранящейся в нескольких таблицах, в одном запросе. В этом проявляется одна из наиболее важных особенностей запросов SQL - способность определять связи между многочисленными таблицами и выводить информацию из них в рамках этих связей. Именно эта операция придает гибкость и легкость языку SQL.

    После изучения этого раздела мы будем способны:





  • соединять данные из нескольких таблиц в единую результирующую таблицу;




  • задавать имена столбцов двумя способами;




  • записывать внешние соединения;




  • создавать соединения таблицы с собой.


  • Операции соединения подразделяются на два вида - внутренние и внешние. Оба вида соединений задаются в предложении WHERE запроса SELECT с помощью специального условия соединения. Внешние соединения (о которых мы поговорим позднее) поддерживаются стандартом ANSI-92 и содержат зарезервированное слово “JOIN”, в то время как внутренние соединения (или просто соединения) могут задаваться как без использования такого слова (в стандарте ANSI-89), так и с использованием слова “JOIN” (в стандарте ANSI-92).


    Связывание производится, как правило, по первичному ключу одной таблицы и внешнему ключу другой таблицы - для каждой пары таблиц. При этом очень важно учитывать все поля внешнего ключа, иначе результат будет искажен. Соединяемые поля могут (но не обязаны!) присутствовать в списке выбираемых элементов. Предложение WHERE может содержать множественные условия соединений. Условие соединения может также комбинироваться с другими предикатами в предложении WHERE.









  • Внутренние соединения


  • Внутреннее соединение возвращает только те строки, для которых условие соединения принимает значение true.







  • SELECT first_name, last_name, department


  • FROM employee, department

    WHERE job_code = "VP" получить список сотрудников,

    состоящих в должности “вице-

    президент”, а также названия

    их отделов

    FIRST_NAME LAST_NAME DEPARTMENT

    =============== ================ ======================

    Robert Nelson Corporate Headquarters

    Mary S. MacDonald Corporate Headquarters

    Robert Nelson Sales and Marketing

    Mary S. MacDonald Sales and Marketing

    Robert Nelson Engineering

    Mary S. MacDonald Engineering

    Robert Nelson Finance

    Mary S. MacDonald Finance

    ...

    Этот запрос (“без соединения”) возвращает неверный результат, так как имеющиеся между таблицами связи не задействованы. Отсюда и появляется дублирование информации в результирующей таблице. Правильный результат дает запрос с использованием операции соединения:







  • SELECT first_name, last_name, department


  • FROM employee, department

    WHERE job_code = "VP"

    AND employee.dept_no = department.dept_no

    имена таблиц

    получить список сотрудников,

    состоящих в должности “вице-

    президент”, а также названия

    их отделов



  • FIRST_NAME LAST_NAME DEPARTMENT


  • =============== ================ ======================

    Robert Nelson Engineering

    Mary S. MacDonald Sales and Marketing

    В вышеприведенном запросе использовался способ непосредственного указания таблиц с помощью их имен. Возможен (а иногда и просто необходим) также способ указания таблиц с помощью алиасов (псевдонимов). При этом алиасы определяются в предложении FROM запроса SELECT и представляют собой любой допустимый идентификатор, написание которого подчиняется таким же правилам, что и написание имен таблиц. Потребность в алиасах таблиц возникает тогда, когда названия столбцов, используемых в условиях соединения двух (или более) таблиц, совпадают, а названия таблиц слишком длинны...


    Замечание 1: в одном запросе нельзя смешивать использование написания имен таблиц и их алиасов.

    Замечание 2: алиасы таблиц могут совпадать с их именами.







  • SELECT first_name, last_name, department


  • FROM employee e, department d

    WHERE job_code = "VP"

    AND e.dept_no = d.dept_no

    алиасы таблиц

    получить список сотрудников,

    состоящих в должности “вице-

    президент”, а также названия

    их отделов



  • FIRST_NAME LAST_NAME DEPARTMENT


  • =============== ================ ======================

    Robert Nelson Engineering

    Mary S. MacDonald Sales and Marketing

    А вот пример запроса, соединяющего сразу три таблицы:







  • SELECT first_name, last_name, job_title,


  • department

    FROM employee e, department d, job j

    WHERE d.mngr_no = e.emp_no

    AND e.job_code = j.job_code

    AND e.job_grade = j.job_grade

    AND e.job_country = j.job_country

    получить список сотрудников

    с названиями их должностей

    и названиями отделов

    FIRST_NAME LAST_NAME JOB_TITLE DEPARTMENT

    ========== ============ ======================= ======================

    Robert Nelson Vice President Engineering

    Phil Forest Manager Quality Assurance

    K. J. Weston Sales Representative Field Office: East Coast

    Katherine Young Manager Customer Support

    Chris Papadopoulos Manager Research and Development

    Janet Baldwin Sales Co-ordinator Pacific Rim Headquarters

    Roger Reeves Sales Co-ordinator European Headquarters

    Walter Steadman Chief Financial Officer Finance

    В данном примере последние три условия необходимы в силу того, что первичный ключ в таблице JOB состоит из трех полей - см. рис.1.

    Мы рассмотрели внутренние соединения с использованием стандарта ANSI-89. Теперь опишем новый (ANSI-92) стандарт:





  • условия соединения записываются в предложении FROM, в котором слева и справа от зарезервированного слова “JOIN” указываются соединяемые таблицы;




  • условия поиска, основанные на правой


  • таблице, помещаются в предложение ON;



  • условия поиска, основанные на левой


  • таблице, помещаются в предложение WHERE.








  • SELECT first_name, last_name, department


  • FROM employee e JOIN department d

    ON e.dept_no = d.dept_no

    AND department = "Customer Support"

    WHERE last_name starting with "P"

    получить список служащих

    (а заодно и название отдела),

    являющихся сотрудниками отдела

    “Customer Support”, фамилии кото-

    рых начинаются с буквы “P”

    FIRST_NAME LAST_NAME DEPARTMENT

    ============= =============== ===================

    Leslie Phong Customer Support

    Bill Parker Customer Support









  • Самосоединения


  • В некоторых задачах необходимо получить информацию, выбранную особым образом только из одной таблицы. Для этого используются так называемые самосоединения, или рефлексивные соединения. Это не отдельный вид соединения, а просто соединение таблицы с собой с помощью алиасов. Самосоединения полезны в случаях, когда нужно получить пары аналогичных элементов из одной и той же таблицы.


  • SELECT one.last_name, two.last_name,

    one.hire_date

    FROM employee one, employee two

    WHERE one.hire_date = two.hire_date

    AND one.emp_no < two.emp_no

    получить пары фамилий сотрудников,

    которые приняты на работу в один

    и тот же день

    LAST_NAME LAST_NAME HIRE_DATE

    ==================== ==================== ===========

    Nelson Young 28-DEC-1988

    Reeves Stansbury 25-APR-1991

    Bishop MacDonald 1-JUN-1992

    Brown Ichida 4-FEB-1993

    SELECT d1.department, d2.department, d1.budget

    FROM department d1, department d2

    WHERE d1.budget = d2.budget

    AND d1.dept_no < d2.dept_no

    получить список пар отделов с

    одинаковыми годовыми бюджетами

    DEPARTMENT DEPARTMENT BUDGET

    ======================== ========================= =========

    Software Development Finance 400000.00

    Field Office: East Coast Field Office: Canada 500000.00

    Field Office: Japan Field Office: East Coast 500000.00

    Field Office: Japan Field Office: Canada 500000.00

    Field Office: Japan Field Office: Switzerland 500000.00

    Field Office: Singapore Quality Assurance 300000.00

    Field Office: Switzerland Field Office: East Coast 500000.00


  • Внешние соединения


  • Напомним, что внутреннее соединение возвращает только те строки, для которых условие соединения принимает значение true. Иногда требуется включить в результирующий набор большее количество строк.

    Вспомним, запрос вида







  • SELECT first_name, last_name, department


  • FROM employee e, department d

    WHERE e.dept_no = d.dept_no

    возвращает только те строки, для которых условие соединения (e.dept_no = d.dept_no) принимает значение true.

    Внешнее соединение возвращает все

    строки из одной таблицы и только те строки из другой таблицы, для которых условие соединения принимает значение true. Строки второй таблицы, не удовлетворяющие условию соединения (т.е. имеющие значение false), получают значение null в результирующем наборе.

    Существует два вида внешнего соединения: LEFT JOIN и RIGHT JOIN.

    В левом соединении (LEFT JOIN) запрос возвращает все строки из левой таблицы (т.е. таблицы, стоящей слева

    от зарезервированного словосочетания “LEFT JOIN”) и только те из правой таблицы, которые удовлетворяют условию соединения. Если же в правой таблице не найдется строк, удовлетворяющих заданному условию, то в результате они замещаются значениями null.

    Для правого соединения - все наоборот.







  • SELECT first_name, last_name, department


  • FROM employee e LEFT JOIN department d

    ON e.dept_no = d.dept_no

    получить список сотрудников

    и название их отделов,

    включая сотрудников, еще

    не назначенных ни в какой отдел

    FIRST_NAME LAST_NAME DEPARTMENT

    =============== ============== =====================

    Robert Nelson Engineering

    Bruce Young Software Development

    Kim Lambert Field Office: East Coast

    Leslie Johnson Marketing

    Phil Forest Quality Assurance

    ...

    В данном запросе все сотрудники оказались распределены по отделам, иначе названия отделов заместились бы значением null.

    А вот пример правого соединения:







  • SELECT first_name, last_name, department


  • FROM employee e RIGHT JOIN department d

    ON e.dept_no = d.dept_no

    получить список сотрудников


    и название их отделов,

    включая отделы, в которые еще

    не назначены сотрудники

    FIRST_NAME LAST_NAME DEPARTMENT

    =============== ============= =========================

    Terri Lee Corporate Headquarters

    Oliver H. Bender Corporate Headquarters

    Mary S. MacDonald Sales and Marketing

    Michael Yanowski Sales and Marketing

    Robert Nelson Engineering

    Kelly Brown Engineering

    Stewart Hall Finance

    Walter Steadman Finance

    Leslie Johnson Marketing

    Carol Nordstrom Marketing

    Software Products Div.

    Bruce Young Software Development

    ...

    В результирующий набор входит и отдел “Software Products Div.” (а также отдел “Field Office: Singapore”, не представленный здесь), в котором еще нет ни одного сотрудника.

    Объединение

    (Union).
    Операция выборки позволяет получить все строки (записи) либо часть строк одной таблицы.



  • SELECT * FROM country Получить все строки

  • таблицы Country
    COUNTRY CURRENCY
    =============== ==========
    USA Dollar
    England Pound
    Canada CdnDlr
    Switzerland SFranc
    Japan Yen
    Italy Lira
    France FFranc
    Germany D-Mark
    Australia ADollar
    Hong Kong HKDollar
    Netherlands Guilder
    Belgium BFranc
    Austria Schilling
    Fiji FDollar
    В этом примере и далее - для большей наглядности - все зарезервированные слова языка SQL будем писать большими буквами. Красным цветом будем записывать предложения SQL, а светло-синим - результаты выполнения запросов.



  • SELECT * FROM country

  • WHERE currency = “Dollar” Получить подмножество строк таблицы Country, удовлетворяющее условию Currency = “Dollar”
    Результат последней операции выглядит следующим образом:

  • COUNTRY CURRENCY

  • =============== ==========
    USA Dollar
    Операция проекции позволяет выделить подмножество столбцов таблицы. Например:



  • SELECT currency FROM country Получить список

  • денежных единиц
    CURRENCY
    ==========
    Dollar
    Pound
    CdnDlr
    SFranc
    Yen
    Lira
    FFranc
    D-Mark
    ADollar
    HKDollar
    Guilder
    BFranc
    Schilling
    FDollar
    На практике очень часто требуется получить некое подмножество столбцов и строк таблицы, т.е. выполнить комбинацию Restriction и Projection. Для этого достаточно перечислить столбцы таблицы и наложить ограничения на строки.



  • SELECT currency FROM country

  • WHERE country = “Japan” Найти денежную

    единицу Японии
    CURRENCY
    ==========
    Yen

  • SELECT first_name, last_name

  • FROM employee
    WHERE first_name = "Roger" Получить фамилии

    работников,

    которых зовут “Roger”
    FIRST_NAME LAST_NAME
    =============== ====================
    Roger De Souza
    Roger Reeves
    Эти примеры иллюстрируют общую форму команды SELECT в языке SQL (для одной таблицы):
  • SELECT (выбрать) специфицированные поля

  • FROM (из) специфицированной таблицы
    WHERE (где) некоторое специфицированное условие является истинным

    Операция соединения позволяет соединять строки из более чем одной таблицы (по некоторому условию) для образования новых строк данных.







  • SELECT first_name, last_name, proj_name


  • FROM employee, project

    WHERE emp_no = team_leader Получить список

    руководителей проектов

    FIRST_NAME LAST_NAME PROJ_NAME

    ============== ================= ====================

    Ashok Ramanathan Video Database

    Pete Fisher DigiPizza

    Chris Papadopoulos AutoMap

    Bruce Young MapBrowser port

    Mary S. MacDonald Marketing project 3

    Операция объединения позволяет объединять результаты отдельных запросов по нескольким таблицам в единую результирующую таблицу. Таким образом, предложение UNION объединяет вывод двух или более SQL-запросов в единый набор строк и столбцов.







  • SELECT first_name, last_name, job_country


  • FROM employee

    WHERE job_country = "France"

    UNION

    SELECT contact_first, contact_last, country

    FROM customer

    WHERE country = "France" Получить список

    работников и заказчиков,

    проживающих во Франции

    FIRST_NAME LAST_NAME JOB_COUNTRY

    =============== ================= ===============

    Jacques Glon France

    Michelle Roche France

    Для справки, приведем общую форму команды SELECT, учитывающую возможность соединения нескольких таблиц и объединения результатов:

  • SELECT [DISTINCT] список_выбираемых_элементов (полей)


  • FROM список_таблиц (или представлений)

    [WHERE предикат]

    [GROUP BY поле (или поля) [HAVING предикат]]

    [UNION другое_выражение_Select]

    [ORDER BY поле (или поля) или номер (номера)];

    Рис. 2: Общий формат команды SELECT

    Отметим, что под предикатом понимается некоторое специфицированное условие (отбора), значение которого имеет булевский тип. Квадратные скобки означают необязательность использования дополнительных конструкций команды. Точка с запятой является стандартным терминатором команды. Отметим, что в WISQL и в компоненте TQuery ставить конечный терминатор не обязательно. При этом там, где допустим один пробел между элементами, разрешено ставить любое количество пробелов и пустых строк - выполняя желаемое форматирование для большей наглядности.


    Гибкость и мощь языка SQL состоит в том, что он позволяет объединить все операции реляционной алгебры в одной конструкции, “вытаскивая” таким образом любую требуемую информацию, что очень часто и происходит на практике.












  • Команда SELECT






  • Простейшие конструкции команды SELECT


  • Итак, начнем с рассмотрения простейших конструкций языка SQL. После такого рассмотрения мы научимся:





  • назначать поля, которые должны быть выбраны




  • назначать к выборке “все поля”




  • управлять “вертикальным” и “горизонтальным” порядком выбираемых полей




  • подставлять собственные заголовки полей в результирующей таблице




  • производить вычисления в списке выбираемых элементов




  • использовать литералы в списке выбираемых элементов




  • ограничивать число возвращаемых строк




  • формировать сложные условия поиска, используя реляционные и логические операторы




  • устранять одинаковые строки из результата.


  • Список выбираемых элементов может содержать следующее:





  • имена полей




  • *




  • вычисления




  • литералы




  • функции




  • агрегирующие конструкции










  • Список полей


  • SELECT first_name, last_name, phone_no


  • FROM phone_list получить список

    имен, фамилий и служебных телефонов

    всех работников предприятия

    FIRST_NAME LAST_NAME PHONE_NO

    ============= ==================== ====================

    Terri Lee (408) 555-1234

    Oliver H. Bender (408) 555-1234

    Mary S. MacDonald (415) 555-1234

    Michael Yanowski (415) 555-1234

    Robert Nelson (408) 555-1234

    Kelly Brown (408) 555-1234

    Stewart Hall (408) 555-1234

    ...

    Отметим, что PHONE_LIST - это виртуальная таблица (представление), созданная в InterBase и основанная на информации из двух таблиц - EMPLOYEE и DEPARTMENT. Она не показана на рис.1, однако, как мы уже указывали в общей структуре команды SELECT, к ней можно обращаться так же, как и к “настоящей” таблице.

  • Все поля


  • SELECT *


  • FROM phone_list получить список служебных телефонов

    всех работников предприятия


    со всей необходимой информацией

    EMP_NO FIRST_NAME LAST_NAME PHONE_EXT LOCATION PHONE_NO

    ====== ========== ========= ========= ============= ==============

    12 Terri Lee 256 Monterey (408) 555-1234

    105 Oliver H. Bender 255 Monterey (408) 555-1234

    85 Mary S. MacDonald 477 San Francisco (415) 555-1234

    127 Michael Yanowski 492 San Francisco (415) 555-1234

    2 Robert Nelson 250 Monterey (408) 555-1234

    109 Kelly Brown 202 Monterey (408) 555-1234

    14 Stewart Hall 227 Monterey (408) 555-1234

    ...

  • Все поля в произвольном порядке


  • SELECT first_name, last_name, phone_no,


  • location, phone_ext, emp_no

    FROM phone_list получить список служебных телефонов

    всех работников предприятия

    со всей необходимой информацией,

    расположив их в требуемом порядке

    FIRST_NAME LAST_NAME PHONE_NO LOCATION PHONE_EXT EMP_NO

    ========== ========= ============== ============= ========= ======

    Terri Lee (408) 555-1234 Monterey 256 12

    Oliver H. Bender (408) 555-1234 Monterey 255 105

    Mary S. MacDonald (415) 555-1234 San Francisco 477 85

    Michael Yanowski (415) 555-1234 San Francisco 492 127

    Robert Nelson (408) 555-1234 Monterey 250 2

    Kelly Brown (408) 555-1234 Monterey 202 109

    Stewart Hall (408) 555-1234 Monterey 227 14

    ...

  • Блобы


  • Получение информации о BLOb выглядит совершенно аналогично обычным полям. Полученные значения можно отображать с использованием data-aware компонент Delphi, например, TDBMemo


  • или TDBGrid. Однако, в последнем случае придется самому прорисовывать содержимое блоба (например, через OnDrawDataCell). Подробнее об этом см. на уроке, посвященном работе с полями.

    SELECT job_requirement

    FROM job получить список

    должностных требований

    к кандидатам на работу

    JOB_REQUIREMENT:

    No specific requirements.

    JOB_REQUIREMENT:

    15+ years in finance or 5+ years as a CFO

    with a proven track record.

    MBA or J.D. degree.

    ...

  • Вычисления


  • SELECT emp_no, salary, salary * 1.15


  • FROM employee получить список номеров

    служащих и их зарплату,

    в том числе увеличенную на 15%

    EMP_NO SALARY


    ====== ====================== ======================

    2 105900.00 121785

    4 97500.00 112125

    5 102750.00 118162.5

    8 64635.00 74330.25

    9 75060.00 86319

    11 86292.94 99236.87812499999

    12 53793.00 61861.95

    14 69482.62 79905.01874999999

    ...

    Порядок вычисления выражений подчиняется общепринятым правилам: сначала выполняется умножение и деление, а затем - сложение и вычитание. Операции одного уровня выполняются слева направо. Разрешено применять скобки для изменения порядка вычислений.

    Например, в выражении col1 + col2 * col3

    сначала находится произведение значений столбцов col2

    и col3, а затем результат этого умножения складывается со значением столбца col1. А в выражении (col1 + col2) * col3 сначала выполняется сложение значений столбцов col1

    и col2, и только после этого результат умножается на значение столбца col3.

  • Литералы


  • Для придания большей наглядности получаемому результату можно использовать литералы. Литералы - это строковые константы, которые применяются наряду с наименованиями столбцов и, таким образом, выступают в роли “псевдостолбцов”. Строка символов, представляющая собой литерал, должна быть заключена в одинарные или двойные скобки.


  • SELECT first_name, "получает", salary, "долларов в год"

    FROM employee получить список сотрудников

    и их зарплату

    FIRST_NAME SALARY

    =========== ======== ========== ==============

    Robert получает 105900.00 долларов в год

    Bruce получает 97500.00 долларов в год

    Kim получает 102750.00 долларов в год

    Leslie получает 64635.00 долларов в год

    Phil получает 75060.00 долларов в год

    K. J. получает 86292.94 долларов в год

    Terri получает 53793.00 долларов в год

    ...

  • Конкатенация


  • Имеется возможность соединять два или более столбца, имеющие строковый тип, друг с другом, а также соединять их с литералами. Для этого используется операция конкатенации (||).


  • SELECT "сотрудник " || first_name || " " || last_name

    FROM employee получить список всех сотрудников

    ==============================================


    сотрудник Robert Nelson

    сотрудник Bruce Young

    сотрудник Kim Lambert

    сотрудник Leslie Johnson

    сотрудник Phil Forest

    сотрудник K. J. Weston

    сотрудник Terri Lee

    сотрудник Stewart Hall

    ...

  • Использование квалификатора AS


  • Для придания наглядности получаемым результатам наряду с литералами в списке выбираемых элементов можно использовать квалификатор AS. Данный квалификатор заменяет в результирующей таблице существующее название столбца на заданное. Это наиболее эффективный и простой способ создания заголовков (к сожалению, InterBase, как уже отмечалось, не поддерживает использование русских букв в наименовании столбцов).


  • SELECT count(*) AS number

    FROM employee подсчитать количество служащих

    NUMBER

    ===========

    42

    SELECT "сотрудник " || first_name || " " || last_name AS employee_list

    FROM employee получить список всех сотрудников

    EMPLOYEE_LIST

    ==============================================

    сотрудник Robert Nelson

    сотрудник Bruce Young

    сотрудник Kim Lambert

    сотрудник Leslie Johnson

    сотрудник Phil Forest

    сотрудник K. J. Weston

    сотрудник Terri Lee

    сотрудник Stewart Hall

    ...

  • Работа с датами


  • Мы уже рассказывали о типах данных, имеющихся в различных СУБД, в том числе и в InterBase. В разных системах имеется различное число встроенных функций, упрощающих работу с датами, строками и другими типами данных. InterBase, к сожалению, обладает достаточно ограниченным набором таких функций. Однако, поскольку язык SQL, реализованный в InterBase, соответствует стандарту, то в нем имеются возможности конвертации дат в строки и гибкой работы с датами. Внутренне дата в InterBase содержит значения даты и времени. Внешне дата может быть представлена строками различных форматов, например:



  • “October 27, 1995”


  • “27-OCT-1994”


  • “10-27-95”


  • “10/27/95”


  • “27.10.95”


  • Кроме абсолютных дат, в SQL-выражениях можно также пользоваться относительным заданием дат:



  • “yesterday” вчера


  • “today” сегодня


  • “now” сейчас (включая время)


  • “tomorrow” завтра



  • Дата может неявно конвертироваться в строку (из строки), если:





  • строка, представляющая дату, имеет один из вышеперечисленных форматов;




  • выражение не содержит неоднозначностей в толковании типов столбцов.








  • SELECT first_name, last_name, hire_date


  • FROM employee

    WHERE hire_date > '1-1-94' получить список сотрудников,

    принятых на работу после

    1 января 1994 года

    FIRST_NAME LAST_NAME HIRE_DATE

    =============== ==================== ===========

    Pierre Osborne 3-JAN-1994

    John Montgomery 30-MAR-1994

    Mark Guckenheimer 2-MAY-1994

    Значения дат можно сравнивать друг с другом, сравнивать с относительными датами, вычитать одну дату из другой.







  • SELECT first_name, last_name, hire_date


  • FROM employee

    WHERE 'today' - hire_date > 365 * 7 + 1

    получить список служащих,

    проработавших на предприятии

    к настоящему времени

    более 7 лет

    FIRST_NAME LAST_NAME HIRE_DATE

    =============== ==================== ===========

    Robert Nelson 28-DEC-1988

    Bruce Young 28-DEC-1988









  • Агрегатные функции


  • К агрегирующим функциям относятся функции вычисления суммы (SUM), максимального (SUM) и минимального (MIN) значений столбцов, арифметического среднего (AVG), а также количества строк, удовлетворяющих заданному условию (COUNT).


  • SELECT count(*), sum (budget), avg (budget),

    min (budget), max (budget)

    FROM department

    WHERE head_dept = 100 вычислить: количество отделов,

    являющихся подразделениями

    отдела 100 (Маркетинг и продажи),

    их суммарный, средний, мини- мальный и максимальный бюджеты

    COUNT SUM AVG MIN MAX

    ====== =========== ========== ========== ===========

    5 3800000.00 760000.00 500000.00 1500000.00

  • Предложение FROM команды SELECT


  • В предложении FROM перечисляются все объекты (один или несколько), из которых производится выборка данных (рис.2). Каждая таблица или представление, о которых упоминается в запросе, должны быть перечислены в предложении FROM.










  • Ограничения на число выводимых строк


  • Число возвращаемых в результате запроса строк может быть ограничено путем использования предложения WHERE, содержащего условия отбора (предикат, рис.2). Условие отбора для отдельных строк может принимать значения true, false или unnown. При этом запрос возвращает в качестве результата только те строки (записи), для которых предикат имеет значение true.


    Типы предикатов, используемых в предложении WHERE:



  • сравнение с использованием реляционных операторов






  • = равно


  • <> не равно

    != не равно

    > больше

    < меньше

    >= больше или равно

    <= меньше или равно



  • BETWEEN


  • IN


  • LIKE


  • CONTAINING


  • IS NULL


  • EXIST


  • ANY


  • ALL










  • Операции сравнения


  • Рассмотрим операции сравнения. Реляционные операторы могут использоваться с различными элементами. При этом важно соблюдать следующее правило: элементы должны иметь сравнимые типы. Если в базе данных определены домены, то сравниваемые элементы должны относиться к одному домену.

    Что же может быть элементом сравнения? Элементом сравнения может выступать:





  • значение поля




  • литерал




  • арифметическое выражение




  • агрегирующая функция




  • другая встроенная функция




  • значение (значения), возвращаемые подзапросом.


  • При сравнении литералов конечные пробелы игнорируются. Так, предложение WHERE first_name = ‘Петр ‘

    будет иметь тот же результат, что и предложение WHERE first_name = ‘Петр’.







  • SELECT first_name, last_name, dept_no


  • FROM employee

    WHERE job_code = "Admin" получить список сотрудников

    (и номера их отделов),

    занимающих должность

    администраторов

    FIRST_NAME LAST_NAME DEPT_NO

    =============== ==================== =======

    Terri Lee 000

    Ann Bennet 120

    Sue Anne O'Brien 670

    Kelly Brown 600



  • SELECT first_name, last_name, dept_no,


  • job_country

    FROM employee

    WHERE job_country <> "USA" получить список сотрудников

    (а также номера их отделов

    и страну),

    работающих вне США

    FIRST_NAME LAST_NAME DEPT_NO JOB_COUNTRY

    =============== ================ ======= ==============

    Ann Bennet 120 England

    Roger Reeves 120 England

    Willie Stansbury 120 England

    Claudia Sutherland 140 Canada

    Yuki Ichida 115 Japan

    Takashi Yamamoto 115 Japan

    Roberto Ferrari 125 Italy

    Jacques Glon 123 France

    Pierre Osborne 121 Switzerland









  • BETWEEN


  • Предикат BETWEEN задает диапазон значений, для которого выражение принимает значение true. Разрешено также использовать конструкцию NOT BETWEEN.



  • SELECT first_name, last_name, salary

    FROM employee

    WHERE salary BETWEEN 20000 AND 30000

    получить список сотрудников,

    годовая зарплата которых

    больше 20000 и меньше 30000

    FIRST_NAME LAST_NAME SALARY

    =============== ========== ===============

    Ann Bennet 22935.00

    Kelly Brown 27000.00

    Тот же запрос с использованием операторов сравнения будет выглядеть следующим образом:

    SELECT first_name, last_name, salary

    FROM employee

    WHERE salary >= 20000

    AND salary <= 30000 получить список сотрудников,

    годовая зарплата которых

    больше 20000 и меньше 30000

    FIRST_NAME LAST_NAME SALARY

    =============== ========== ===============

    Ann Bennet 22935.00

    Kelly Brown 27000.00

    Запрос с предикатом BETWEEN может иметь следующий вид:

    SELECT first_name, last_name, salary

    FROM employee

    WHERE last_name BETWEEN "Nelson" AND "Osborne"

    получить список сотрудников,

    фамилии которых начинаются

    с “Nelson”

    и заканчиваются “Osborne”

    FIRST_NAME LAST_NAME SALARY

    =============== =============== ================

    Robert Nelson 105900.00

    Carol Nordstrom 42742.50

    Sue Anne O'Brien 31275.00

    Pierre Osborne 110000.00

    Значения, определяющие нижний и верхний диапазоны, могут не являться реальными величинами из базы данных. И это очень удобно - ведь мы не всегда можем указать точные значения диапазонов!

    SELECT first_name, last_name, salary

    FROM employee

    WHERE last_name BETWEEN "Nel" AND "Osb"

    получить список сотрудников,

    фамилии которых находятся

    между “Nel” и “Osb”

    FIRST_NAME LAST_NAME SALARY

    =============== =============== ================

    Robert Nelson 105900.00

    Carol Nordstrom 42742.50

    Sue Anne O'Brien 31275.00

    В данном примере значений “Nel” и “Osb” в базе данных нет. Однако, все сотрудники, входящие в диапазон, в нижней части которого начало фамилий совпадает с “Nel” (т.е. выполняется условие “больше или равно”), а в верхней части фамилия не более “Osb” (т.е. выполняется условие “меньше или равно” - а именно “O”, “Os”, “Osb”), попадут в выборку. Отметим, что при выборке с использованием предиката BETWEEN поле, на которое накладывается диапазон, считается упорядоченным по возрастанию.


    Предикат BETWEEN с отрицанием NOT ( NOT BETWEEN) позволяет получить выборку записей, указанные поля которых имеют значения меньше нижней границы и больше верхней границы.

    SELECT first_name, last_name, hire_date

    FROM employee

    WHERE hire_date NOT BETWEEN "1-JAN-1989" AND "31-DEC-1993" получить список самых “старых”

    и самых “молодых” (по времени

    поступления на работу)

    сотрудников

    FIRST_NAME LAST_NAME HIRE_DATE

    =============== ================ ===========

    Robert Nelson 28-DEC-1988

    Bruce Young 28-DEC-1988

    Pierre Osborne 3-JAN-1994

    John Montgomery 30-MAR-1994

    Mark Guckenheimer 2-MAY-1994

  • IN


  • Предикат IN проверяет, входит ли заданное значение, предшествующее ключевому слову “IN” (например, значение столбца или функция от него) в указанный в скобках список. Если заданное проверяемое значение равно какому-либо элементу в списке, то предикат принимает значение true. Разрешено также использовать конструкцию NOT IN.


  • SELECT first_name, last_name, job_code

    FROM employee

    WHERE job_code IN ("VP", "Admin", "Finan")

    получить список сотрудников,

    занимающих должности

    “вице-президент”, “администратор”,

    “финансовый директор”

    FIRST_NAME LAST_NAME JOB_CODE

    =============== ================ ========

    Robert Nelson VP

    Terri Lee Admin

    Stewart Hall Finan

    Ann Bennet Admin

    Sue Anne O'Brien Admin

    Mary S. MacDonald VP

    Kelly Brown Admin

    А вот пример запроса, использующего предикат NOT IN:

    SELECT first_name, last_name, job_country

    FROM employee

    WHERE job_country NOT IN

    ("USA", "Japan", "England")

    получить список сотрудников,

    работающих не в США, не в Японии

    и не в Великобритании

    FIRST_NAME LAST_NAME JOB_COUNTRY

    =============== ================ ===============

    Claudia Sutherland Canada

    Roberto Ferrari Italy

    Jacques Glon France

    Pierre Osborne Switzerland

  • LIKE


  • Предикат LIKE используется только с символьными данными. Он проверяет, соответствует ли данное символьное значение строке с указанной маской. В качестве маски используются все разрешенные символы (с учетом верхнего и нижнего регистров), а также специальные символы:



  • % - замещает любое количество символов (в том числе и 0),

    _ - замещает только один символ.

    Разрешено также использовать конструкцию NOT LIKE.

    SELECT first_name, last_name

    FROM employee

    WHERE last_name LIKE "F%"

    получить список сотрудников,

    фамилии которых начинаются с буквы “F”

    FIRST_NAME LAST_NAME

    =============== ====================

    Phil Forest

    Pete Fisher

    Roberto Ferrari

    SELECT first_name, last_name

    FROM employee

    WHERE first_name LIKE "%er" получить список сотрудников,

    имена которых заканчиваются буквами “er”

    FIRST_NAME LAST_NAME

    =============== ====================

    Roger De Souza

    Roger Reeves

    Walter Steadman

    А такой запрос позволяет решить проблему произношения (и написания) имени:

    SELECT first_name, last_name

    FROM employee

    WHERE first_name LIKE "Jacq_es"

    найти сотрудника(ов),

    в имени которого

    неизвестно произношение

    буквы перед окончанием “es”

    FIRST_NAME LAST_NAME

    =============== ====================

    Jacques Glon

    Что делать, если требуется найти строку, которая содержит указанные выше специальные символы (“%”, “_”) в качестве информационных символов? Есть выход! Для этого с помощью ключевого слова ESCAPE

    нужно определить так называемый escape-символ, который, будучи поставленным перед символом “%” или “_”, укажет, что этот символ является информационным. Escape-символ не может быть символом “\” (обратная косая черта) и, вообще говоря, должен представлять собой символ, никогда не появляющийся в упоминаемом столбце как информационный символ. Часто для этих целей используются символы “@” и “~”.

    SELECT first_name, last_name

    FROM employee

    WHERE first_name LIKE "%@_%" ESCAPE "@"

    получить список сотрудников,

    в имени которых содержится “_”

    (знак подчеркивания)

  • CONTAINING


  • Предикат CONTAINING аналогичен предикату LIKE, за исключением того, что он не чувствителен к регистру букв. Разрешено также использовать конструкцию NOT CONTAINING.


  • SELECT first_name, last_name

    FROM employee


    WHERE last_name CONTAINING "ne"

    получить список сотрудников,

    фамилии которых содержат буквы

    “ne”, “Ne”, “NE”, “nE”

    FIRST_NAME LAST_NAME

    =============== ====================

    Robert Nelson

    Ann Bennet

    Pierre Osborne

  • IS NULL


  • В SQL-запросах NULL


  • означает, что значение столбца неизвестно. Поисковые условия, в которых значение столбца сравнивается с NULL, всегда принимают значение unknown

    (и, соответственно, приводят к ошибке), в противоположность true

    или false, т.е.

    WHERE dept_no = NULL

    или даже

    WHERE NULL = NULL.

    Предикат IS NULL

    принимает значение true

    только тогда, когда выражение слева от ключевых слов “IS NULL” имеет значение null

    (пусто, не определено). Разрешено также использовать конструкцию IS NOT NULL, которая означает “не пусто”, “имеет какое-либо значение”.

    SELECT department, mngr_no

    FROM department

    WHERE mngr_no IS NULL получить список отделов,

    в которых еще не назначены

    начальники

    DEPARTMENT MNGR_NO

    ========================= =======

    Marketing

    Software Products Div.

    Software Development

    Field Office: Singapore

    Предикаты EXIST, ANY, ALL, SOME, SINGULAR мы рассмотрим в разделе, рассказывающем о подзапросах.

  • Логические операторы


  • К логическим операторам относятся известные операторы AND, OR, NOT, позволяющие выполнять различные логические действия: логическое умножение (AND, “пересечение условий”), логическое сложение (OR, “объединение условий”), логическое отрицание (NOT, “отрицание условий”). В наших примерах мы уже применяли оператор AND. Использование этих операторов позволяет гибко “настроить” условия отбора записей.

    Оператор AND

    означает, что общий предикат будет истинным только тогда, когда условия, связанные по “AND”, будут истинны.

    Оператор OR

    означает, что общий предикат будет истинным, когда хотя бы одно из условий, связанных по “OR”, будет истинным.

    Оператор NOT

    означает, что общий предикат будет истинным, когда условие, перед которым стоит этот оператор, будет ложным.


    В одном предикате логические операторы выполняются в следующем порядке: сначала выполняется оператор NOT, затем - AND и только после этого - оператор OR. Для изменения порядка выполнения операторов разрешается использовать скобки.







  • SELECT first_name, last_name, dept_no,


  • job_code, salary

    FROM employee

    WHERE dept_no = 622

    OR job_code = "Eng"

    AND salary <= 40000

    ORDER BY last_name получить список служащих,

    занятых в отделе 622

    или

    на должности “инженер” с зарплатой

    не выше 40000

    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

    ============ ============= ======= ======== ===========

    Jennifer M. Burbank 622 Eng 53167.50

    Phil Forest 622 Mngr 75060.00

    T.J. Green 621 Eng 36000.00

    Mark Guckenheimer 622 Eng 32000.00

    John Montgomery 672 Eng 35000.00

    Bill Parker 623 Eng 35000.00

    Willie Stansbury 120 Eng 39224.06



  • SELECT first_name, last_name, dept_no,


  • job_code, salary

    FROM employee

    WHERE (dept_no = 622

    OR job_code = "Eng")

    AND salary <= 40000

    ORDER BY last_name получить список служащих,

    занятых в отделе 622

    или на должности “инженер”,

    зарплата которых не выше 40000

    FIRST_NAME LAST_NAME DEPT_NO JOB_CODE SALARY

    ============ ============= ======= ======== ===========

    T.J. Green 621 Eng 36000.00

    Mark Guckenheimer 622 Eng 32000.00

    John Montgomery 672 Eng 35000.00

    Bill Parker 623 Eng 35000.00

    Willie Stansbury 120 Eng 39224.06









  • Преобразование типов (CAST)


  • В SQL имеется возможность преобразовать значение столбца или функции к другому типу для более гибкого использования операций сравнения. Для этого используется функция CAST.


  • Типы данных могут быть конвертированы в соответствии со следующей таблицей:

    Из типа данных В тип данных

    ---------------------------------------

    NUMERIC CHAR, VARCHAR, DATE

    CHAR, VARCHAR NUMERIC, DATE

    DATE CHAR, VARCHAR, DATE

    SELECT first_name, last_name, dept_no

    FROM employee

    WHERE CAST(dept_no AS char(20))

    CONTAINING "00" получить список сотрудников,

    занятых в отделах,


    номера которых содержат “00”

    FIRST_NAME LAST_NAME DEPT_NO

    =============== ==================== =======

    Robert Nelson 600

    Terri Lee 000

    Stewart Hall 900

    Walter Steadman 900

    Mary S. MacDonald 100

    Oliver H. Bender 000

    Kelly Brown 600

    Michael Yanowski 100

  • Изменение порядка выводимых строк (ORDER BY)


  • Порядок выводимых строк может быть изменен с помощью опционального (дополнительного) предложения ORDER BY в конце SQL-запроса. Это предложение имеет вид:

  • ORDER BY <порядок строк> [ASC | DESC]


  • Порядок строк может задаваться одним из двух способов:



    Объект Session

  • Объект Session, имеющий тип TSession создается автоматически в программе, работающей с базами данных (в этом случае Delphi подключает в программу модуль DB). Вам не нужно заботиться о создании и уничтожении данного объекта, но его методы и свойства могут быть полезны в некоторых случаях. В этом компоненте содержится информация обо всех базах данных, с которыми работает программа. Ее можно найти в свойстве DataBases. Со свойством KeepConnections

  • данного объекта мы уже знакомы. Это свойство определяет, нужно ли сохранять соединение с базой, если в программе нет ни одной открытой таблицы из этой базы. NetDir -
    директория, в которой лежит общий сетевой файл PDOXUSRS.NET, необходимый BDE. PrivateDir - директория для хранения временных файлов.
    С помощью методов объекта Session можно получить информацию о настройках BDE, например, список всех псевдонимов, драйверов баз данных или список всех таблиц в базе.
    Еще одно важное назначение объекта Session - доступ с его помощью к таблицам Paradox, защищенным паролем. Прежде, чем открыть такую таблицу, требуется выполнить метод AddPassword :
    Session.AddPassword(‘my_pass’);
    Удалить пароль можно с помощью метода RemovePassword или RemoveAllPasswords.

    Объект TOLEContainer

  • Объект TOLEContainer находится на странице System Палитры Компонент и нужен для создания приложений OLE-контейнеров. TOLEContainer скрывает все сложности, связанные с внутренней организацией OLE и предоставляет программисту достаточно простой интерфейс. Построим простейшее приложение с использованием OLE объекта. Создайте новый проект и поместите на форму TOLEContainer, в Инспекторе Объектов дважды щелкните мышкой на свойство ObjClass или ObjDoc - появится стандартный диалог Windows “Insert Object” (см. рис.1)

  • Объект TOLEContainer

    В этом диалоге есть список всех зарегистрированных в системе OLE-серверов (регистрация происходит при инсталляции программы). Тип OLE-объекта определяется как раз тем сервером, который Вы укажете. Если Вы создаете новый объект (Create New), то при нажатии кнопки OK запустится программа OLE-сервер, в которой и формируется новый объект. После выхода из программы-сервера новый OLE объект включается
    (embedded object) в программу. OLE объект можно создать используя уже имеющийся файл в формате одного из OLE-серверов. Для этого нужно выбрать пункт Create from File (см. рис.2)
    Объект TOLEContainer

    Выбранный объект можно как включить в приложение, так и присоединить, отметив пункт Link.
    Итак, давайте при создании нашего проекта создадим новый объект, выбрав для этого, например, Microsoft Word Document (рис.1). Нажмите OK и после того, как запустится MS Word, наберите там любой текст (“Это OLE-объект Microsoft Word document”). Для завершения работы в меню есть специальный пункт “File|Close and Return to Form1” (Win’95+MS Word 7.0). Запустите проект, он будет выглядеть примерно так:
    Объект TOLEContainer

    Щелкните дважды мышкой на OLE-контейнер - запустится MS Word с документом из OLE-объекта, который можно редактировать, при этом все изменения сохраняются в OLE-объекте.
    !!! Если во время дизайна Вы выбираете объект для включения в OLE-контейнер, то он полностью записывается в файл формы (FORM1.DFM) и в дальнейшем прикомпилируется к EXE файлу. В случае очень больших объектов это может привести во время дизайна к длительным паузам и даже к возникновению ошибки “Out of resource”. Поэтому рекомендуется большие объекты делать присоединенными (linked).
    TOLEContainer позволяет отображать в программе объект в его непосредственном виде (с различной степенью увеличения или уменьшения - свойство Zoom) или в виде пиктограммы, определяемой в диалоге на рис.1 (Display as Icon).
    Выбор OLE-объекта может происходить не только во время дизайна, но и во время выполнения программы (об этом чуть ниже). Результаты работы с этим объектом можно сохранить в виде файла и в следующий раз восстановить его оттуда, для этого TOLEContainer имеет два метода SaveToFile и LoadFromFile.

    Объект TPaintBox

  • На странице System Палитры Компонент есть объект TPaintBox, который можно использовать для построения приложений типа графического редактора или, например, в качестве места построения графиков (если, конечно, у Вас нет для этого специальных компонент третьих фирм). Никаких ключевых свойств, кроме Canvas, TPaintBox не имеет, собственно, этот объект является просто канвой для рисования. Важно, что координаты указателя мыши, передаваемые в обработчики соответствующих событий (OnMouseMove и др.), являются относительными, т.е. это смещение мыши относительно левого верхнего угла объекта TPaintBox, а не относительно левого верхнего угла формы.


  • Обновление (Refresh)

  • Как Вы уже знаете, любая таблица, которую Вы открываете всегда “подвержена изменению”. Короче говоря, Вы должны расценить таблицу скорее как меняющуюся, чем как статическую сущность. Даже если Вы - единственное лицо, использующее данную TTable, и даже если Вы не работаете в сети, всегда существует возможность того, что программа с которой Вы работаете, может иметь два различных пути изменения данных в таблице. В результате, Вы должны всегда знать, необходимо ли Вам обновить вид таблицы на экране.

  • Функция Refresh
    связана с функцией Open, в том смысле что она считывает данные, или некоторую часть данных, связанных с данной таблицей. Например, когда Вы открываете таблицу, Delphi считывает данные непосредственно из файла БД. Аналогично, когда Вы Регенерируете таблицу, Delphi считывает данные напрямую из таблицы. Поэтому Вы можете использовать эту функцию, чтобы перепрочитать таблицу, если Вы думаете что она могла измениться. Быстрее и эффективнее, вызывать Refresh, чем вызывать Close и затем Open.
    Имейте ввиду, однако, что обновление TTable может иногда привести к неожиданным результатам. Например, если a пользователь рассматривает запись, которая уже была удалена, то она исчезнет с экрана в тот момент, когда будет вызван Refresh. Аналогично, если некий другой пользователь редактировал данные, то вызов Refresh приведет к динамическому изменению данных. Конечно маловероятно, что один пользователь будет изменять или удалять запись в то время, как другой просматривает ее, но это возможно.

    Обработка сообщений Windows в Delphi

    Конечно, нельзя придумать такую библиотеку объектов, которые бы полностью соответствовали потребностям программистов. Всегда возникнет необходимость дополнения или изменения свойств и поведения объектов. В этом случае, так же, как и при создании своих собственных компонент в Delphi, часто требуется обрабатывать сообщения Windows. Поскольку Object Pascal является развитием и продолжением Borland Pascal 7.0, то это выполняется сходным с BP способом.
    Общий синтаксис для декларации обработчика сообщений Windows:
    procedure Handler_Name(var Msg : MessageType);
    message WM_XXXXX;
    Handler_Name обозначает имя метода; Msg - имя передаваемого параметра; MessageType - какой либо тип записи, подходящий для данного сообщения; директива message указывает, что данный метод является обработчиком сообщения; WM_XXXXX - константа или выражение, которое определяет номер обрабатываемого сообщения Windows.
    Рассмотрим обработку сообщений на примере. Например, при нажатии правой кнопки мыши на форме в программе появляется всплывающее меню (pop-up menu, если оно было привязано к этой форме). Программист может захотеть привязать к правой кнопке какое-нибудь другое событие. Это можно сделать так:
    type
    TForm1 = class(TForm)
    PopupMenu1: TPopupMenu;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    private
    { Private declarations }
    procedure WMRButtonDown(var Msg : TWMMouse); message
    WM_RBUTTONDOWN;
    public
    { Public declarations }
    end;
    Подчеркнут код, добавленный в декларацию объекта TForm1 вручную. Далее, в секции implementation нужно написать обработчик:
    procedure TForm1.WMRButtonDown(var Msg : TWMMouse);
    begin
    MessageDlg('Right mouse button click.', mtInformation,



  • [mbOK], 0);

  • end;
    В данном случае при нажатии правой кнопки мыши будет появляться диалог.
    Вообще-то, у класса TForm уже есть унаследованный от дальнего предка обработчик данного события, который называется точно также и вызывает то самое pop-up меню. Если в новом обработчике сообщения нужно выполнить действия, которые производились в старом, то для этого применяется ключевое слово inherited. Если слегка модифицировать наш обработчик, то после диалога будет появляться pop-up меню:

    procedure TForm1.WMRButtonDown(var Msg : TWMMouse);

    begin

    MessageDlg('Right mouse button click.', mtInformation,







  • [mbOK], 0);


  • inherited;

    end;

    Однако, есть еще способ обработки всех сообщений, которые получает приложение. Для этого используется свойство OnMessage объекта Application, который автоматически создается при запуске программы. Если определен обработчик события OnMessage, то он получает управление при любом событии, сообщение о котором направлено в программу. Следующий код будет приводить к появлению диалога при двойном щелчке мыши на любом объекте в приложении.

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    Application.OnMessage:=AOM;

    end;

    procedure TForm1.AOM(var Msg: TMsg; var Handled: Boolean);

    begin

    Handled:=False;

    if Msg.Message = WM_LBUTTONDBLCLK then begin

    MessageDlg('Double click.', mtInformation, [mbOK], 0);

    Handled:=True;

    end;

    end;

    Конечно, в обработчике нельзя выполнять операции, требующие длительного времени, поскольку это приведет к замедлению выполнения всего приложения.

    ОБЗОР

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


  • Использовании Windows


  • Простейших программных конструкциях таких, как переменные, циклы и функции





  • Delphi позволяет легко и просто включать в программу такие мультимедийные объекты, как звуки, видео и музыку. В данном уроке обсуждается, как это сделать, используя встроенный в Delphi компонент TMediaPlayer. Подробно рассматриваются управление этим компонентом в программе и получение информации о текущем состоянии.



  • В данной статье дается обзор стандартных и дополнительных компонент из Палитры Компонент Delphi (стр. Standard и Additional), страницы диалогов (Dialogs), системных компонент (стр. System), страницы объектов в формате Visual Basic (VBX). Компоненты, осуществляющие доступ к данным и отображение их на экране будут рассматриваться позднее.



  • Из статьи Вы узнаете основные сведения об OLE, некоторые вещи относительно OLE 2 и OLE Automation. В статье рассказывается об использовании объекта TOLEContainer для построения OLE приложения в Delphi.



  • В данной статье рассказывается о возможных способах вывода информации на печать из программы, созданной в Delphi. Рассматривается вывод документа в текстовом режиме принтера, вывод графики с помощью объекта TPrinter и печать содержимого формы. О выводе на печать отчетов с помощью генератора отчетов ReportSmith рассказывается ниже.



  • С целью поддержки структурной обработки исключительных ситуаций (exception) в Delphi введены новые расширения языка Pascal. В данной статье будет дано описание того, что из себя представляет такая обработка, почему она полезна, будут приведены соответствующий синтаксис Object Pascal и примеры использования исключительных ситуаций в Delphi.



  • Одна из ключевых целей среды визуального программирования - скрыть от пользователя сложность программирования в Windows. При этом, однако, хочется, чтобы такая среда не была упрощена слишком, не до такой степени, что программисты потеряют доступ к самой операционной системе.

  • Программирование, ориентированное на события - неотъемлемая черта Windows. Некоторые программные среды для быстрой разработки приложений (RAD) пытаются скрыть от пользователя эту черту совсем, как будто она настолько сложна, что большинство не могут ее понять. Истина заключается в том, что событийное программирование само по себе не так уж сложно. Однако, есть некоторые особенности воплощения данной концепции в Windows, которые в некоторых ситуациях могут вызвать затруднения.
    Delphi предоставляет полный доступ к подструктуре событий, предоставляемой Windows. С другой стороны, Delphi упрощает программирование обработчиков таких событий.
    В данном уроке приводится несколько примеров того, как обрабатывать события в Delphi, дается более детальное объяснение работы системы, ориентированной на события.


  • В данной статье приводятся основные факты о DDEML и показывается, как можно использовать DDE в программе. Предмет данной статьи технически сложен, однако библиотека Delphi упрощает наиболее трудные аспекты программирования DDE .

  • В статье предполагается, что читатель может знать очень мало о предмете. Цель статьи - научить его использовать концепцию DDE при создании приложений в среде Delphi.

    Open или ExecSQL?

  • После того, как составлен SQL запрос, есть два различных способа выполнить его. Если Вы хотите получить курсор, то нужно вызывать Open. Если выражение SQL не подразумевает возвращение курсора, то нужно вызывать ExecSQL. Например, если происходит вставка, удаление или обновление данных (т.е. SQL запросы INSERT, DELETE, UPDATE), то нужно вызывать ExecSQL. Тоже самое можно сказать по-другому: Open вызывается при запросе типа SELECT, а ExecSQL - во всех остальных случаях.

  • Вот типичный SQL запрос, который используется для удаления записи из таблицы:
    delete from Country where Name = ‘Argentina’;
    Этот запрос удалил бы любую запись из таблицы COUNTRY, которая имеет значение "Argentina" в поле Имя.
    Не трудно заметить, что это тот случай, когда удобно использовать параметризованный запрос. Например, неплохо было бы менять имя страны, которую требуется удалить:
    delete from Country where Name = :CountryName
    В этом случае переменная :CountryName может быть изменена во время выполнения:
    Query2.Prepare;
    Query2.Params[0] := ‘Argentina’;
    Query2.ExecSQL;
    Код сначала вызывает Prepare, чтобы сообщить Delphi что он должен разобрать SQL запрос и подготовить свойство Params. Следующим шагом присваивается значение свойству Params и затем выполняется подготовленный SQL запрос. Обратите внимание, что он выполняется через ExecSQL, а не Open.
    Программа INSQUERY из примеров Delphi демонстрирует эту технику (проект C:\DELPHI\DEMOS\DB\INSQUERY.DPR)

    Основные понятия о TDataSource

  • Класс TDataSource используется в качестве проводника между TTable или TQuery и компонентами, визуализирующими данные, типа TDBGrid, TDBEdit и TDBComboBox (data-aware components). В большинстве случаев, все, что нужно сделать с DataSource - это указать в свойстве DataSet соответствующий TTable или TQuery. Затем, у data-aware компонента в свойстве DataSource указывается TDataSource, который используется в настоящее время.

  • TDataSource также имеет свойство Enabled, и оно может быть полезно всякий раз, когда Вы хотите временно отсоединить, например, DBGrid от таблицы или запроса. Эти требуется, например, если нужно программно пройти через все записи в таблице. Ведь, если таблица связана с визуальными компонентами (DBGrid, DBEdit и т.п.), то каждый раз, когда Вы вызываете метод TTable.Next, визуальные компоненты будут перерисовываться. Даже если само сканирование в таблице двух или трех тысяч записей не займет много времени, то может потребоваться значительно больше времени, чтобы столько же раз перерисовать визуальные компоненты. В случаях подобных этому, лучше всего установить поле DataSource.Eabled в False. Это позволит Вам просканировать записи без перерисовки визуальных компонент. Это единственная операция может увеличить скорость в некоторых случаях на несколько тысяч процентов.
    Свойство TDataSource.AutoEdit указывает, переходит ли DataSet автоматически в режим редактирования при вводе текста в data-aware объекте.

    Основные понятия о TQuery

    Предыдущий Урок был, в основном, посвящен объекту TTable, который служит для доступа к данным. При использовании TTable, возможен доступ ко всему набору записей из одной таблицы. В отличие от TTable, TQuery позволяет произвольным образом (в рамках SQL) выбрать набор данных для работы с ним. Во многом, методика работы с объектом TQuery похожа на методику работы с TTable, однако есть свои особенности.
    Вы может создать SQL запрос используя компонент TQuery следующим способом:

  • Назначите Псевдоним (Alias) DatabaseName.

  • Используйте свойство SQL чтобы ввести SQL запрос типа

  • “Select * from Country”.
  • Установите свойство Active в True

  • Если обращение идет к локальным данным, то вместо псевдонима можно указать полный путь к каталогу, где находятся таблицы.
    Две основных вещи, которые Вы должны понять прежде, чем перейти дальше:


  • Этот урок не является учебником для начинающих по SQL, а, скорее, описанием объекта TQuery и основных задач, которые Вы можете решить с его помощью. Если Вы не знаете ничто об SQL, Вы все же сможете воспользоваться этой статьей, и, в конце концов, приобретете некоторое понимание основ SQL. Однако, для полного изучения языка, Вы должны обратиться к любой из большого количества книг и документов, доступных по этому предмету.


  • Delphi использует pass through SQL, поэтому для разных SQL серверов синтаксис может быть несколько разным. Версия SQL для локальных таблиц (Local SQL) очень сильно урезан, по сравнению со стандартом. Чтобы узнать о его возможностях, Вы должны прочитать не только эту статью, но также файл LOCALSQL.HLP.

  • Вы увидите, что объект TQuery один из наиболее полезных и гибких компонентов, доступных в Delphi. С ним Вы сможете воспользоваться всей мощью, предоставляемой лидерами среди промышленных SQL серверов, вроде InrterBase, Oracle или Sybase.




  • Основы DDE

  • Аббревиатура DDEML обозначает Dynamic Data Exchange Management Library (библиотека управления динамическим обменом данными). DDEML это надстройка над сложной системой сообщений, называемой Dynamic Data Exchange (DDE). Библиотека, содержащая DDE била разработана для усиления возможностей первоначальной системы сообщений Windows.

  • DDE дает возможность перейти через рамки приложения и взаимодействовать с другими приложениями и системами Windows.
    Dynamic Data Exchange получило свое имя потому, что позволяет двум приложениям обмениваться данными (текстовыми, через глобальную память) динамически во время выполнения. Связь между двумя программами можно установить таким образом, что изменения в одном приложении будут отражаться во втором. Например, если Вы меняете число в электронной таблице, то во втором приложении данные обновятся автоматически и отобразят изменения. Кроме того, с помощью DDE можно из своего приложения управлять другими приложениями такими, как Word for Windows, Report Smith, Excel и др.
    Надеюсь, что данное краткое вступление поможет понять что предмет обсуждения представляет интерес. Далее рассказывается, как использовать компоненты Delphi для построения DDE приложений.

    Основы OLE

  • Прежде, чем перейти к рассмотрению основ OLE, потребуется изучить терминологию.

  • Аббревиатура OLE обозначает Objects Linked and Embedded (Присоединенные И Встроенные Объекты - ПИВО J ). Данные, разделяемые между приложениями называются OLE объектом. Приложение, которое может содержать OLE объекты, называют OLE контейнером (OLE Container). Приложение, данные из которого можно включить в OLE контейнер в виде OLE объекта, называют OLE сервером.
    Например, MicroSoft Word может включать в документ графические объекты, аудио- и видеоклипы и множество других объектов (такой документ иногда называют составным документом - compound document ).
    Как следует из названия, OLE объекты можно либо присоединить к OLE контейнеру, либо включить в него. В первом случае данные будут храниться в файле на диске, любое приложение будет иметь доступ к этим данным и сможет вносить изменения. Во втором случае данные включаются в OLE контейнер и только он сможет просматривать и модифицировать эти данные.
    OLE является дальнейшим развитием идеи разделяемых между приложениями данных. Если с помощью DDE можно было работать с текстом, то OLE позволяет легко встроить в приложение обработку любых типов данных. Как и в случае с DDE, для правильной работы приложения-клиента (OLE контейнера) требуется наличие приложения OLE сервера. Каждый раз, когда в программе-клиенте пользователь обращается к OLE объекту с целью просмотра или редактирования данных (обычно двойной щелчок мышкой на объекте), запускается приложение-сервер, в котором и происходит работа с данными.
    В природе существует несколько видов OLE, отличающихся по способу активации OLE сервера. OLE версии 1 запускает сервер в отдельном окне. OLE 2 реализует то, что называется in-place activation and editing. В данном случае сервер запускается “внутри” приложения-клиента, модифицирует вид системного меню, линейки инструментов и др. Развитие идеи OLE привело к появлению OLE automation - приложение-клиент может выполнить часть кода сервера. Тип OLE объекта, помещенного в программу-клиент, определяется тем, какую версию OLE поддерживает сервер.

    Открытие и закрытие DataSet

    В этой главе Вы узнаете некоторые факты об открытии и закрытии DataSet.
    Если Вы используете TTable для доступа к таблице, то при открытии данной таблицы заполняются некоторые свойства TTable (количество записей RecordCount, описание структуры таблицы и т.д.).
    Прежде всего, Вы должны поместить во время дизайна на форму объект TTable и указать, с какой таблицей хотите работать. Для этого нужно заполнить в Инспекторе объектов свойства DatabaseName и TableName. В DatabaseName можно либо указать директорию, в которой лежат таблицы в формате dBase или Paradox (например, C:\DELPHI\DEMOS\DATA), либо выбрать из списка псевдоним базы данных (DBDEMOS). Псевдоним базы данных (Alias) определяется в утилите Database Engine Configuration. Теперь, если свойство Active установить в True, то при запуске приложения таблица будет открываться автоматически.
    Имеются два различных способа открыть таблицу во время выполнения программы. Вы можете написать следующую строку кода:
    Table1.Open;
    Или, если Вы предпочитаете, то можете установить свойство Active равное True:
    Table1.Active := True;
    Нет никакого различия между результатом производимым этими двумя операциями. Метод Open, однако, сам заканчивается установкой свойства Active в True, так что может быть даже чуть более эффективно использовать свойство Active напрямую.
    Также, как имеются два способа открыть a таблицу, так и есть два способа закрыть ее. Самый простой способ просто вызывать Close:
    Table1.Close;
    Или, если Вы желаете, Вы можете написать:
    Table1.Active := False;
    Еще раз повторим, что нет никакой существенной разницы между двумя этими способами. Вы должны только помнить, что Open и Close это методы (процедуры), а Active - свойство.
    Навигация (Перемещение по записям)
    После открытия a таблицы, следующим шагом Вы должны узнать как перемещаться по записям внутри него.
    Следующий обширный набор методов и свойства TDataSet обеспечивает все , что Вам нужно для доступа к любой конкретной записи внутри таблицы:
    procedure First;

    procedure Last;

    procedure Next;

    procedure Prior;

    property BOF: Boolean read FBOF;

    property EOF: Boolean read FEOF;

    procedure MoveBy(Distance: Integer);

    Дадим краткий обзор их функциональных возможностей:



  • Вызов Table1.First перемещает Вас к первой записи в таблице.


  • Table1.Last перемещает Вас к последней записи.


  • Table1.Next перемещает Вас на одну запись вперед.


  • Table1.Prior перемещает Вас на одну запись Назад.


  • Вы можете проверять свойства BOF или EOF, чтобы понять, находитесь ли Вы в начале или в конце таблицы.


  • Процедура MoveBy перемещает Вас на N записей вперед или назад в таблице. Нет никакого функционального различия между запросом Table1.Next и вызовом Table1.MoveBy(1). Аналогично, вызов Table1.Prior имеет тот же самый результат, что и вызов Table1.MoveBy(-1).


  • Чтобы начать использовать эти навигационные методы, Вы должны поместить TTable, TDataSource и TDBGrid на форму, также, как Вы делали это в предыдущем уроке. Присоедините DBGrid1 к DataSource1, и DataSource1 к Table1. Затем установите свойства таблицы:



  • в DatabaseName имя подкаталога, где находятся демонстрационные таблицы (или псевдоним DBDEMOS);


  • в TableName установите имя таблицы CUSTOMER.


  • Если Вы запустили программу, которая содержит видимый элемент TDBGrid, то увидите, что можно перемещаться по записям таблицы с помощью полос прокрутки (scrollbar) на нижней и правой сторонах DBGrid.

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

    Поместите две кнопки на форму и назовите их Next и Prior, как показано на рис.2.

    Открытие и закрытие DataSet


    Рис.2 : Next и Prior кнопки позволяют Вам перемещаться по БД.

    Дважды щелкните на кнопке Next - появится заготовка обработчика события:

    procedure TForm1.NextClick(Sender: TObject);

    begin

    end;

    Теперь добавьте a одну строчку кода так, чтобы процедура выглядела так:

    procedure TForm1.NextClick(Sender: TObject);

    begin

    Table1.Next;

    Отслеживание состояния DataSet

    В предыдущей части Вы узнали, как использовать TDataSource, чтобы узнать текущее состоянии TDataSet. Использование DataSource - это простой путь выполнения данной задачи. Однако, если Вы хотите отслеживать эти события без использования DataSource, то можете написать свои обработчики событий TTable и TQuery:
    property OnOpen
    property OnClose
    property BeforeInsert
    property AfterInsert
    property BeforeEdit
    property AfterEdit
    property BeforePost
    property AfterPost
    property OnCancel
    property OnDelete
    property OnNewRecord
    Большинство этих свойств очевидны. Событие BeforePost функционально подобно событию TDataSource.OnUpdateData, которое объяснено выше. Другими словами, программа STATE работала бы точно также, если бы Вы отвечали не на DataSource1.OnUpdateData а на Table1.BeforePost. Конечно, в первом случае Вы должен иметь TDataSource на форме, в то время, как во втором этого не требуется.

    Печать в текстовом режиме

    Если Вам нужно напечатать на принтере документ в текстовом режиме, то это делается следующим образом. С принтером Вы работаете, как с обычным текстовым файлом, за исключением того, что вместо процедуры AssignFile нужно вызывать процедуру AssignPrn. В примере на принтер выводится одна строка текста:
    procedure TForm1.Button1Click(Sender: TObject);
    var
    To_Prn : TextFile;
    begin
    AssignPrn(To_Prn);
    Rewrite(To_Prn);
    Writeln(To_Prn, 'Printer in Text Mode');
    CloseFile(To_Prn);
    end;
    Здесь необходимо, видимо, пояснить, что по сравнению с BP 7.0 в Delphi изменены названия некоторых функций и переменных в модуле System :



  • AssignFile вместо Assign


  • CloseFile вместо Close


  • TextFile вместо Text





  • Передача параметров через TDataSource

  • В предыдущем Уроке Вы видели способ создания отношения однин-ко-многим между двумя таблицами. Теперь речь пойдет о выполнении того же самого действия с использованием объекта TQuery. Этот способ более гибок в том отношении, что он не требует индексации по полям связи.

  • Объект TQuery имеет свойство DataSource, которое может использоваться для того, чтобы создать связь с другим DataSet. Не имеет значения, является ли другой DataSet объектом TTable, TQuery, или некоторый другим потомком TDataSet. Все что нужно для установления соединения - это удостовериться, что у того DataSet есть связанный с ним DataSource.
    Предположим, что Вы хотите создать связь между таблицами ORDERS и CUSTOMERS так, что каждый раз, когда Вы просматриваете конкретную запись о заказчике, будут видны только заказы, связанные с ним.
    Рассмотрите следующий параметризованный запрос:
    select * from Orders where CustNo = :CustNo
    В этом запросе :CustNo - связывающая переменная, которой должно быть присвоено значение из некоторого источника. Delphi позволяет использовать поле TQuery.DataSource чтобы указать другой DataSet, который предоставит эту информацию автоматически. Другими словами, вместо того, чтобы использовать свойство Params и “вручную” присваивать значения переменной, эти значения переменной могут быть просто взяты автоматически из другой таблицы. Кроме того, Delphi всегда сначала пытается выполнить параметризованный запрос используя свойство DataSource, и только потом (если не было найдено какое-то значение параметра) будет пытаться получить значение переменной из свойства Params. При получении данных из DataSource считается, что после двоеточия стоит имя поля из DataSource. При изменении текущей записи в главном DataSet запрос будет автоматически пересчитываться.
    Давайте переделаем пример из прошлого урока (LINKTBL - связывание двух таблиц). Создайте новый проект, положите на форму один набор TTable, TDataSource и TDBGrid. Привяжите его к таблице CUSTOMER. Положите на форму второй набор - TQuery, TDataSource и TDBGrid и свяжите объекты между собой. (см рис.4).
    В свойстве SQL наберите текст запроса:
    select * from Orders where CustNo = :CustNo
    В свойстве DatabaseName для Query1 укажите DBDEMOS.
    В свойстве DataSource для Query1 укажите DataSource1.
    Поставьте Active = True и запустите программу.
    Передача параметров через TDataSource

    Рис.4: Программа LINKQRY - связанные курсоры с помощью SQL

    Передача переменной в отчет

  • Следующий код показывает, как передать переменную в отчет. В примере строковой переменной отчета 'City' присваивается значение 'Bombey'. Подразумевается, что есть готовый отчет с данной переменной.

  • Поместите компонент TReport на форму и установите требуемые свойства для вызова печати отчета. Напишите обработчик OnClick для кнопки Button1 на форме (кнопка - для простоты) :
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    Report1.InitialValues.Clear;
    Report1.InitialValues.Add('@City=');
    Report1.Run;
    end;

    Подробнее об Инспекторе Объектов

  • Ранее мы вкратце рассмотрели Инспектор Объектов (Object Inspector). Теперь нужно исследовать этот важный инструмент глубже. Основное для понимания Инспектора Объектов состоит в том, что он используется для изменения характеристик любого объекта, брошенного на форму. Кроме того, и для изменения свойств самой формы.

  • Лучший путь для изучения Инспектора объектов - поработать с ним. Для начала откройте новый проект, выбрав пункт меню File | New Project. Затем положите на форму объекты TMemo, TButton, и TListBox, как показано на рис.9.
    Подробнее об Инспекторе Объектов

    Рис.9: Простой объект TForm с компонентами TMemo, TButton, и TListBox.
    Сперва рассмотрим работу со свойствами на примере свойства Ctl3D (по умолчанию включено). Выберите форму, щелкнув на ней мышкой, перейдите в Инспектор Объектов и несколько раз с помощью двойных щелчков мышью переключите значение свойства Ctl3D. Заметьте, что это действие радикально меняет внешний вид формы. Изменение свойства Ctl3D формы автоматически изменяет свойство Ctl3D каждого дочернего окна, помещенного на форму.
    Вернитесь на форму и поставьте значение Ctl3D в True. Теперь нажмите клавишу и щелкните на TMemo и затем на TListBox. Теперь оба объекта имеют по краям маленькие квадратики, показывающие, что объекты выбраны.
    Подробнее об Инспекторе Объектов

    Выбрав два или более объектов одновременно, Вы можете выполнить большое число операций над ними. Например, передвигать по форме. Затем попробуйте выбрать пункт меню Edit | Size и установить оба поля Ширину(Width) и Высоту(Height) в Grow to Largest, как показано на рис.10. Теперь оба объекта стали одинакового размера. Затем выберите пункт меню Edit | Align и поставьте в выравнивании по
    горизонтали значение Center (см. рис.11).
    Подробнее об Инспекторе Объектов

    Поскольку Вы выбрали сразу два компонента, то содержимое Инспектора Объектов изменится - он будет показывать только те поля, которые являются общими для объектов. Это означает то, что изменения в свойствах, произведенные Вами повлияют не на один, а на все выбранные объекты.
    Рассмотрим изменение свойств объектов на примере свойства Color. Есть три способа изменить его значение в Инспекторе Объектов. Первый - просто напечатать имя цвета (clRed) или номер цвета. Второй путь - нажать на маленькую стрелку справа и выбрать цвет из списка. Третий путь - дважды щелкнуть на поле ввода свойства Color. При этом появится диалог выбора цвета.

    Свойство Font работает на манер свойства Color. Чтобы это посмотреть, сначала выберите свойство Font для объекта TMemo и дважды щелкните мышкой на поле ввода. Появится диалог настройки шрифта, как показано на рис.12. Выберите, например, шрифт New Times Roman и установите какой-нибудь очень большой размер, например 72. Затем измените цвет фонта с помощью ComboBox’а в нижнем правом углу окна диалога. Когда Вы нажмете кнопку OK, Вы увидите, что вид текста в объекте TMemo радикально изменился.

    Подробнее об Инспекторе Объектов


    Рис.12: Диалог выбора шрифта позволяет Вам задать тип шрифта, размер, и цвет.

    В завершение краткого экскурса по Инспектору Объектов дважды щелкните на свойство Items объекта ListBox. Появится диалог, в котором Вы можете ввести строки для отображения в ListBox. Напечатайте несколько слов, по одному на каждой строке, и нажмите кнопку OK. Текст отобразится в ListBox’е.

    Поля

  • В большинстве случаев, когда Вы хотите получить доступ из программы к индивидуальные полям записи, Вы можете использовать одно из следующих свойств или методов, каждый из которых принадлежат TDataSet:

  • property Fields[Index: Integer];
    function FieldByName(const FieldName: string): TField;
    property FieldCount;
    Свойство FieldCount возвращает число полей в текущей структуре записи. Если Вы хотите программным путем прочитать имена полей, то используйте свойство Fields для доступа к ним:
    var
    S: String;
    begin
    S := Fields[0].FieldName;
    end;
    Если Вы работали с записью в которой первое поле называется CustNo, тогда код показанный выше поместит строку “CustNo” в переменную S. Если Вы хотите получить доступ к имени второго поля в вышеупомянутом примере, тогда Вы могли бы написать:
    S := Fields[1].FieldName;
    Короче говоря, индекс передаваемый в Fields
    (начинающийся с нуля), и определяет номер поля к которому Вы получите доступ, т.е. первое поле - ноль, второе один, и так далее.
    Если Вы хотите прочитать текущее содержание конкретного поля конкретной записи, то Вы можете использовать свойство Fields или метод FieldsByName. Для того, чтобы найти значение
    первого поля записи, прочитайте первый элемент массива Fields:
    S := Fields[0].AsString;
    Предположим, что первое поле в записи содержит номер заказчика, тогда код, показанный выше, возвратил бы строку типа “1021”, “1031” или “2058”. Если Вы хотели получить доступ к этот переменный, как к числовой величине, тогда Вы могли бы использовать AsInteger вместо AsString. Аналогично, свойство Fields включают AsBoolean, AsFloat и AsDate.
    Если хотите, Вы можете использовать функцию FieldsByName
    вместо свойства Fields:
    S := FieldsByName(‘CustNo’).AsString;
    Как показано в примерах выше, и FieldsByName, и Fields
    возвращают те же самые данные. Два различных синтаксиса используются исключительно для того, чтобы обеспечить программистов гибким и удобным набором инструментов для программного доступа к содержимому DataSet.
    Давайте посмотрим на простом примере, как можно использовать доступ к полям таблицы во время выполнения программы. Создайте новый проект, положите на форму объект TTable, два объекта ListBox и две кнопки - “Fields” и “Values” (см рис.4).

    Соедините объект TTable с таблицей CUSTOMER, которая поставляется вместе с Delphi (DBDEMOS), не забудьте открыть таблицу (Active = True).

    Поля


    Рис.4: Программа FLDS показывает, как использовать свойство Fields.

    Сделайте Double click на кнопке Fields и создайте a метод который выглядит так:

    procedure TForm1.FieldsClick(Sender: TObject);

    var

    i: Integer;

    begin

    ListBox1.Clear;

    for i := 0 to Table1.FieldCount - 1 do

    ListBox1.Items.Add(Table1.Fields[i].FieldName);

    end;

    Обработчик события начинается с очистки первого ListBox1, затем он проходит через все поля, добавляя их имена один за другим в ListBox1. Заметьте, что цикл показанный здесь пробегает от 0 до FieldCount - 1. Если Вы забудете вычесть единицу из FieldCount, то Вы получите ошибку “List Index Out of Bounds”, так как Вы будете пытаться прочесть имя поля которое не существует.

    Предположим, что Вы ввели код правильно, и заполнили ListBox1 именами всех полей в текущей структуре записи.

    В Delphi существуют и другие средства которые позволяют Вам получить ту же самую информацию, но это самый простой способ доступа к именам полей в Run Time.

    Свойство Fields позволяет Вам получить доступ не только именам полей записи, но также и к содержимому полей. В нашем примере, для второй кнопки напишем:

    procedure TForm1.ValuesClick(Sender: TObject);

    var

    i: Integer;

    begin

    ListBox2.Clear;

    for i := 0 to Table1.FieldCount - 1 do

    ListBox2.Items.Add(Table1.Fields[i].AsString);

    end;

    Этот код добавляет содержимое каждого из полей во второй listbox. Обратите внимание, что вновь счетчик изменяется от нуля до FieldCount - 1.

    Свойство Fields позволяет Вам выбрать тип результата написав Fields[N].AsString. Этот и несколько связанных методов обеспечивают a простой и гибкий способ доступа к данным, связанными с конкретным полем. Вот список доступных методов который Вы можете найти в описании класса TField:

    property AsBoolean

    property AsFloat

    property AsInteger

    property AsString

    property AsDateTime

    Всякий раз (когда это имеет смысл), Delphi сможет сделать преобразования. Например, Delphi может преобразовывать поле Boolean к Integer или Float, или поле Integer к String. Но не будет преобразовывать String к Integer, хотя и может преобразовывать Float к Integer. BLOB и Memo поля - специальные случаи, и мы их рассмотрим позже. Если Вы хотите работать с полями Date или DateTime, то можете использовать AsString и AsFloat для доступа к ним.


    Как было объяснено выше, свойство FieldByName позволяет Вам получить доступ к содержимому определенного поля просто указав имя этого поля:

    S := Table1.FieldByName(‘CustNo’).AsString;

    Это - удобная технология, которая имеет несколько преимуществ, когда используется соответствующим образом. Например, если Вы не уверены в местонахождении поля, или если Вы думаете, что структура записи, с которой Вы работаете могла измениться, и следовательно, местонахождение поля не определено.

    Понимание событий

    Событийное программирование есть не только в Windows, и данную черту можно реализовать не только в операционной системе. Например, любая DOS программа может быть основана на простом цикле, работающем все время жизни программы в памяти. Ниже вы найдете гипотетический пример, как данный код может выглядеть:
    begin
    InitializeMemory;
    repeat
    CheckForMouseEvent(Events);
    CheckForKeyPress(Events)
    HandleEvents(Events);
    until Done := True;
    DisposeMemory;
    end.
    Это типичный пример программы, ориентированной на события. Она начинается и заканчивается инициализацией и освобождением памяти. В программе присутствует простой цикл repeat..until, который проверяет появление событий от мыши и клавиатуры и затем дает возможность программисту ответить на эти события.
    Переменная Events может быть записью с простой структурой:
    TEvent = record
    X, Y: Integer;
    MouseButton: TButton;
    Key: Word;
    end;
    Тип TButton, указанный выше, можно декларировать так:
    TButton = (lButton, rButton);
    Эти структуры позволяют вам проследить, где находится мышь, каково состояние ее кнопок, и значение нажатой клавиши на клавиатуре. Конечно, это пример очень простой структуры, но заложенные здесь принципы отражают то, что происходит внутри Windows или внутри других систем, ориентированных на события, вроде Turbo Vision. Если бы программа, приведенная выше, была редактором текста, то обработчик HandleEvent для такой программы мог бы иметь вид:
    procedure HandleEvent(Events: TEvent);
    begin
    case Events.Key of
    ’A’..’z’: Write(Events.Key);
    EnterKey: Write(CarriageReturn);
    EscapeKey: Done := True;
    end;
    end;
    Согласно коду выше, программа будет печатать букву ‘a’ при нажатии этой клавиши и перейдет на новую строку, если нажата клавиша ‘Enter’. Нажатие ‘Esc’ приведет к завершению программы.
    Код такого типа может быть очень удобным, в частности, если вы пишете программу, в которой требуется анимация. Например, если нужно перемещать несколько картинок по экрану, то может понадобиться сдвинуть их на несколько точек, затем проверить, нажимал ли пользователь кнопки. Если такое событие было, то его можно обработать, если нет, то двигать дальше.

    Надеюсь, что приведенный пример дает некоторое понимание работы ориентированной на события системы. Единственное, что осталось пропущенным - почему система Windows так спроектирована.

    Одной из основных причин, почему Microsoft сделал Windows по такой схеме, является тот факт, что множество задач работает в среде одновременно. В многозадачных системах операционная система должна знать, щелкнул ли пользователь мышкой на определенное окно. Если это окно было частично перекрыто другим, то это становится известно операционной системе и она перемещает окно на передний план. Понятно, что неудобно заставлять само окно выполнять эти действия. Операционной системе лучше обрабатывать все нажатия клавиш и кнопок на мыши и затем передавать их в остальные программы в виде событий.

    Если кратко, программист в Windows почти никогда не должен напрямую проверять “железо”. Система выполняет эту задачу и передает информацию программе в виде сообщений.

    Когда пользователь щелкает мышкой, операционная система обрабатывает это событие и передает его в окно, которое должно обработать данное событие. Созданное сообщение, в этом случае, пересылается в некую процедуру DefWindowProc окна (default window procedure). DefWindowProc - аналог процедуры HandleEvent из примера, приведенного выше.

    Каждое окно в Windows имеет свою DefWindowProc. Чтобы полностью понять данное утверждение, представьте, что каждая кнопка, каждый ListBox, каждое поле ввода и т.д. на самом деле являются окнами и имеют свою процедуру DefWindowProc. Это очень гибкая и мощная система, но она может заставить программиста писать очень сложный код. Delphi дает возможность быть защищенным от такой структуры программы.

    Почти все, что происходит в Windows принимает форму сообщений и, если вы хотите их использовать в Delphi (в большей мере это необходимо при написании своих собственных компонент), то нужно понять, как эти сообщения работают.

    Если посмотреть на DefWindowProc в справочнике по Windows API, то можно увидеть следующее определение:


    function DefWindowProc(Wnd: HWnd; Msg, wParam: Word;

    lParam: LongInt);

    Каждое сообщение, посылаемое в окно, состоит из четырех частей: первая часть - handle окна, получающего сообщение, Msg сообщает, что произошло а третья и четвертая части (wParam и lParam) содержат дополнительную информацию о событии. Вместе эти четыре части являются аналогом показанной выше структуры TEvent.

    Вторая часть сообщения имеет длину 16 бит и сообщает, что за событие произошло. Например, если нажата левая кнопка на мыши, то переменная Msg содержит значение WM_LBUTTONDOWN. Существуют десятки различного типа cообщений и они называются вроде WM_GETTEXT, WM_HSCROLL, WM_GETTEXTLENGTH и т.п. Список всех сообщений можно видеть в справочнике по Windows API (on-line help).

    Последние две переменные, длиной 16 и 32 бита, называются wParam и lParam. Они сообщают программисту важную дополнительную информацию о каждом событии. Например при нажатии кнопки мыши, lParam содержит координаты указателя мыши.

    Одна из хитростей заключается в том, как выделить нужную информацию из этих переменных. В большинстве случаев Delphi освобождает вас от необходимости выполнять данную задачу. Например, в обработчике события OnMouseDown для формы вы просто используете координаты X и Y. Как программисту, вам не нужно прилагать усилия для получения сообщения и связанных с ним параметров. Все, что связано с событиями, представлено в простом и непосредственном виде:

    procedure TForm1.FormMouseDown(Sender: TObject;

    Button: TMouseButton;

    Shift: TShiftState;

    X, Y: Integer);

    Итак, если подвести итог, то должно стать ясным следующее:



  • Windows является системой ориентированной на события;


  • События в Windows принимают форму сообщений;


  • В недрах VCL Delphi сообщения Windows обрабатываются и преобразуются в более простую для программиста форму;


  • Обработка событий в Delphi сводится к написанию для каждого объекта своих обработчиков;


  • События в программе на Delphi вызываются не только сообщениями Windows, но и внутренними процессами.






  • Понятие DLL

  • Вспомним процесс программирования в DOS. Преобразование исходного текста программы в машинный код включал в себя два процесса - компиляцию и линковку. В процессе линковки, редактор связей, компоновавший отдельные модули программы, помещал в код программы не только объявления функций и процедур, но и их полный код. Вы готовили таким образом одну программу, другую, третью ... И везде код одних и тех же функций помещался в программу полностью (см. рис 1).

  • Рис.1 : Вызов функций при использовании статической компоновки
    В многозадачной среде такой подход был бы по меньшей мере безрассудным, так как очевидно, что огромное количество одних и тех же функций, отвечающих за прорисовку элементов пользовательского интерфейса, за доступ к системным ресурсам и т.п. дублировались бы полностью во всех приложениях, что привело бы к быстрому истощению самого дорогого ресурса - оперативной памяти. В качестве решения возникшей проблемы, еще на UNIX-подобных платформах была предложена концепция динамической компоновки (см. рис . 2).
    Рис.2: Вызов функций при использовании динамической компоновки
    Но, чем же отличаются Dynamic Link Library (DLL) от обычных приложений? Для понимания этого требуется уточнить понятия задачи (task), экземпляра (копии) приложения (instance) и модуля (module).
    При запуске нескольких экземпляров одного приложения, Windows загружает в оперативную память только одну копию кода и ресурсов - модуль приложения, создавая несколько отдельных сегментов данных, стека и очереди сообщений (см. рис. 3), каждый набор которых представляет из себя задачу, в понимании Windows. Копия приложения представляет из себя контекст, в котором выполняется модуль приложения.
    Задача 1 Задача 2
    Копия 1 приложения Копия 2 приложения
    Данные Данные
    Стек Стек
    Очередь сообщений Очередь сообщений
    Модуль приложения
    Код
    Ресурсы
    Рис.3 : Копии приложения и модуль приложения
    DLL - библиотека также является модулем. Она находится в памяти в единственном экземпляре и содержит сегмент кода и ресурсы, а также сегмент данных (см. рис. 4).

    DLL-библиотека

    Код

    Ресурсы

    Данные

    Рис.4 : Структура DLL в памяти

    DLL - библиотека, в отличие от приложения не имеет ни стека, ни очереди сообщений. Функции, помещенные в DLL, выполняются в контексте вызвавшего приложения, пользуясь его стеком. Но эти же функции используют сегмент данных, принадлежащий библиотеке, а не копии приложения.

    В силу такой организации DLL, экономия памяти достигается за счет того, что все запущенные приложения используют один модуль DLL, не включая те или иные стандартные функции в состав своих модулей.

    Часто, в виде DLL создаются отдельные наборы функций, объединенные по тем или иным логическим признакам, аналогично тому, как концептуально происходит планирование модулей ( в смысле unit ) в Pascal. Отличие заключается в том, что функции из модулей Pascal компонуются статически - на этапе линковки, а функции из DLL компонуются динамически, то есть в run-time.

    Пример использования Delphi + ReportSmith

  • Завершенное приложение Delphi + ReportSmith есть в примерах к данному уроку. Приложение позволяет выбрать имя отчета в диалоге открытия файлов и выполнить этот отчет. Код для кнопки PrintReport (Печатать отчета) показан ниже.

  • procedure TForm1.PrintReportClick(Sender: TObject);
    begin
    if OpenDialog1.Execute then begin
    Report1.ReportName := OpenDialog1.Filename;
    Report1.Run
    end
    end;

    Пример OLE приложения

  • Среди демонстрационных примеров, входящих в Delphi есть два, относящихся к работе с OLE-объектами (в директориях X:\DELPHI\DEMOS\OLE2 и X:\DELPHI\DEMOS\DOC\OLE2). Более полным является второй, который, кроме всего прочего является примером построения MDI приложения. Данная программа демонстрирует все основные возможности TOLEContainer и позволяет:

  • - создавать новый OLE контейнер во время выполнения программы;
    - инициализировать OLE объект либо в стандартном диалоге Windows “Insert Object”, либо с помощью Clipboard, либо с помощью техники “перенести и бросить” (drag-and-drop);
    - сохранить OLE объект в файле и восстановить его оттуда;
    Пример OLE приложения

    На рис.4 показан пример MDI приложения, содержащий два дочерних окна с OLE объектами. Для создания нового OLE объекта нужно выбрать пункт меню File|New и далее Edit|Insert Object. Появится стандартный диалог Windows для инициализации OLE объекта (см. рис.1). Если приложение OLE-сервер имеет возможность сохранять информацию об OLE объекте в Clipboard, то проинициализировать объект можно с помощью пункта меню Edit|Paste Special.
    Достаточно интересной является возможность применения техники drag-and-drop в применении к OLE объектам. Запустите MS Word (разместите его окно так, чтобы было видно и OLE приложение), наберите какой-нибудь текст, выделите его и с помощью мышки перетащите и бросьте на главное MDI окно приложения. Появится новое дочернее окно с OLE контейнером, содержащим этот текст. Программирование данной возможности достаточно сложно. Полное описание технологии построения данного OLE приложения есть в документации в коробке с Delphi (User’s guide), этому посвящена отдельная глава.

    Пример программы с мультимедиа

    В данной главе мы рассмотрим пример построения приложения с мультимедиа первого типа. Создайте новый проект (File | New Project). Поместите TMediaPlayer на форму; поместите компоненты TFileListBox, TDirectoryListBox, TDriveComboBox, TFilterComboBox для выбора файла. В свойстве FileList для DirectoryListBox1 и FilterComboBox1 поставьте FileListBox1. В св-ве DirList для DriveComboBox1 поставьте DirectoryListBox1. В св-ве Filter для FilterComboBox1 укажите требуемые расширения файлов:
    AVI File(*.avi)|*.avi
    WAVE File(*.wav)|*.wav
    MIDI file(*.MID)|*.mid
    Пусть по двойному щелчку мышкой в FileListBox1 выбранный файл будет воспроизводиться. В обработчике события OnDblClick для FileListBox1 укажите
    Procedure TForm1.FileListBox1DblClick(Sender:TObject);
    begin
    with MediaPlayer1 do
    begin
    Close;
    FileName:=FileListBox1.FileName;
    Open;
    Play;
    end;
    end;
    Внешний вид формы представлен на рис.4
    Пример программы с мультимедиа

    Рис.4: Начальный вид проекта
    Сохраните проект, запустите его, выберите нужный файл и дважды щелкните на него мышкой. MediaPlayer должен воспроизвести этот файл в отдельном окне.
    Как уже говорилось выше, видеоролик можно воспроизводить внутри формы, например, на панели. Давайте слегка модифицируем проект и добавим туда панель TPanel (см. рис.5). В св-ве Display для MediaPlayer1 укажите Panel1. Нужно убрать надпись с панели (Caption)
    и св-во BevelOuter = bvNone. Чтобы переключаться при воспроизведении с окна на панель - поместите TСheckBox на форму и в обработчике события OnClick для него запишите:
    procedure TForm1.CheckBox1Click(Sender: TObject);
    var
    Start_From : Longint;
    begin
    with MediaPlayer1 do begin
    if FileName='' then Exit;
    Start_From:=Position;
    Close;
    Panel1.Refresh;
    if CheckBox1.Checked then
    Display:=Panel1
    else
    Display:=NIL;
    Open;
    Position:=Start_From;
    Play;
    end;
    end;
    Запустите проект и воспроизведите видеоролик. Пощелкайте мышкой на CheckBox.
    Пример программы с мультимедиа



  • Пример Редактора Компонент

    В качестве примера давайте создадим Редактор Компонент для класса TButton. Этот Редактор будет показывать сообщение и изменять свойство Caption у объекта TButton. В данном примере это будет срабатывать и при двойном щелчке мыши, и через контекстное меню.
    Декларация нового класса Редактора Компонент:
    TButtonEditor = class(TComponentEditor)
    private
    procedure HiThere;
    public
    procedure Edit; override;
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): string; override;
    function GetVerbCount: Integer; override;
    end;
    Процедура HiThere и будет показывать сообщение и изменять свойство Caption:
    procedure TButtonEditor.HiThere;
    begin
    MessageDlg('Hi! It replaces Default Component Editor.',
    mtInformation, [mbOK], 0);
    (Component as TButton).Caption:='Hi!';
    Designer.Modified;
    end;
    Процедуры Edit и ExecuteVerb только вызывают HiThere:
    procedure TButtonEditor.Edit;
    begin
    HiThere;
    end;
    procedure TButtonEditor.ExecuteVerb(Index: Integer);
    begin
    if Index = 0 then HiThere;
    end;
    Процедуры GetVerb и GetVerbCount определяют вид контекстного меню:
    function TButtonEditor.GetVerb(Index: Integer): string;
    begin
    result:='&Get message ...'
    end;
    function TButtonEditor.GetVerbCount: Integer;
    begin
    result:=1;
    end;
    Здесь в контекстное меню добавляется один пункт “Get message …”.
    Редактор Компонент готов.
    Необходимо зарегистрировать новый Редактор Компонент, это делается аналогично регистрации Редактора Свойств, только проще:
    procedure Register;
    begin
    RegisterComponentEditor(TButton, TButtonEditor);
    end;
    После того, как Вы подключите новый Редактор Компонент в среду Delphi, а это делается в пункте меню “Options|Install Components”, создайте новый проект, положите на форму объект TButton и щелкните дважды на нем - появится диалог:
    Пример Редактора Компонент

    После того, как Вы нажмете “OK”, текст на кнопке изменится.
    Созданный нами Редактор Компонент заместит Редактор по умолчанию для всех объектов класса TButton и его наследников, например, TBitBtn.
    Полный текст Редактора Компонент приведен в файле SBEDIT.PAS в примерах к данному уроку.

    Пример создания компонента

    Для примера создадим новый класс, мутант TButton, в котором изменим значение по умолчанию свойства ShowHint на True и добавим новое свойство - счетчик нажатий на кнопку. Заготовка модуля для создания нового компонента уже есть (см. пункт Заготовка для нового компонента). Теперь исходный текст выглядит так:
    unit New_btn;
    interface
    uses
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
    Controls, Forms, Dialogs, StdCtrls;
    type
    TMyButton = class(TButton)
    private
    { Private declarations }
    FClickCount : Longint;
    protected
    { Protected declarations }
    public
    { Public declarations }
    constructor Create(AOwner : TComponent); override;
    procedure Click; override;
    property ClickCount : Longint read FClickCount write









  • FClickCount;

  • published
    { Published declarations }
    end;
    procedure Register;
    implementation
    constructor TMyButton.Create(AOwner : TComponent);
    begin
    inherited Create(AOwner);
    ShowHint:=True;
    FClickCount:=0;
    end;
    procedure TMyButton.Click;
    begin
    Inc(FClickCount);
    inherited Click;
    end;
    procedure Register;
    begin
    RegisterComponents('Samples', [TMyButton]);
    end;
    end.
    Для того, чтобы переопределить начальное значение свойства при создании объекта, нужно переписать конструктор Create, в котором и присвоить этому свойству нужное значение (не забыв перед этим вызвать конструктор предка).
    Новое свойство для подсчета нажатий на клавишу называется ClickCount. Его внутреннее поле для сохранения значения - FClickCount имеет тип Longint, емкости поля хватит надолго.






  • реализованы все три вышеописанных

    В примере (проект PRINTS.DPR, рис.1 ) реализованы все три вышеописанных ситуации.
    реализованы все три вышеописанных


    Примеры обработки исключительных ситуаций

  • Ниже приведены процедуры A,B и C, обсуждавшиеся ранее, воплощенные в новом синтаксисе Object Pascal:

  • type
    ESampleError = class(Exception);
    var
    ErrorCondition: Boolean;
    procedure C;
    begin
    writeln('Enter C');
    if (ErrorCondition) then
    begin
    writeln('Raising exception in C');
    raise ESampleError.Create('Error!');
    end;
    writeln('Exit C');
    end;
    procedure B;
    begin
    writeln('enter B');
    C;
    writeln('exit B');
    end;
    procedure A;
    begin
    writeln('Enter A');
    try
    writeln('Enter A''s try block');
    B;
    writeln('After B call');
    except
    on ESampleError do
    writeln('Inside A''s ESampleError handler');
    on ESomethingElse do
    writeln('Inside A''s ESomethingElse handler');
    end;
    writeln('Exit A');
    end;
    begin
    writeln('begin main');
    ErrorCondition := True;
    A;
    writeln('end main');
    end.
    При ErrorCondition = True программа выдаст:
    begin main
    Enter A
    Enter A's try block
    enter B
    Enter C
    Raising exception in C
    Inside A's ESampleError handler
    Exit A
    end main
    Возможно вас удивила декларация типа 'ESampleError =class' вместо '=object'; это еще одно новое расширение языка. Delphi вводит новую модель объектов, доступную через декларацию типа '=class'. Описание новой объектной модели дается в других уроках. Здесь же достаточно сказать, что исключительные ситуации (exceptions) являются классами, частью новой объектной модели.
    Процедура C проверяет наличие ошибки (в нашем случае это значение глобальной переменной) и, если она есть (а это так), C вызывает(raise) исключительную ситуацию класса ESampleError.
    Процедура A помещает часть кода в блок try..except.
    Первая часть этого блока содержит часть кода, аналогично конструкции begin..end. Эта часть кода завершается ключевым словом except, далее следует один или более обработчиков исключительных ситуаций on xxxx do yyyy, далее может быть включен необязательный блок else, вся конструкция заканчивается end;. В конструкции, назначающей определенную обработку для конкретной исключительной ситуации (on xxxx do yyyy), после резервного слова on указывается класс исключительной ситуации, а после do следует собственно код обработки данной ошибки. Если возникшая исключительная ситуация подходит по типу к указанному после on, то выполнение программы переходит сюда (на код после do). Исключительная ситуация подходит в том случае, если она того же класса, что указан в on, либо является его потомком. Например, в случае on EFileNotFound обрабатываться будет ситуация, когда файл не найден. А в случае on EFileIO - все ошибки при работе с файлами, в том числе и предыдущая ситуация. В блоке else обрабатываются все ошибки, не обработанные до этого.

    Приведенные в примере процедуры содержат код (строка с writeln), который отображает путь выполнения программы. Когда C вызывает exception, программа сразу переходит на обработчик ошибок в процедуре A, игнорируя оставшуюся часть кода в процедурах B и C.
    После того, как найден подходящий обработчик ошибки, поиск оканчивается. После выполнения кода обработчика, программа продолжает выполняться с оператора, стоящего после слова end блока try..except (в примере - writeln('Exit A')).
    Конструкция try..except подходит, если известно, какой тип ошибок нужно обрабатывать в конкретной ситуации. Но что делать, если требуется выполнить некоторые действия в любом случае, произошла ошибка или нет? Это тот случай, когда понадобится конструкция try..finally.
    Рассмотрим модифицированную процедуру B:
    procedure NewB;
    var
    P: Pointer;
    begin
    writeln('enter B');
    GetMem(P, 1000);
    C;
    FreeMem(P, 1000);
    writeln('exit B');
    end;
    Если C вызывает исключительную ситуацию, то программа уже не возвращается в процедуру B. А что же с теми 1000 байтами памяти, захваченными в B? Строка FreeMem(P,1000) не выполнится и Вы потеряете кусок памяти. Как это исправить? Нужно ненавязчиво включить процедуру B в процесс, например:
    procedure NewB;
    var
    P: Pointer;
    begin
    writeln('enter NewB');
    GetMem(P, 1000);
    try
    writeln('enter NewB''s try block');
    C;
    writeln('end of NewB''s try block');
    finally
    writeln('inside NewB''s finally block');
    FreeMem(P, 1000);
    end;
    writeln('exit NewB');
    end;
    Если в A поместить вызов NewB вместо B, то программа выведет сообщения следующим образом:
    begin main
    Enter A
    Enter A's try block
    enter NewB
    enter NewB's try block
    Enter C
    Raising exception in C
    inside NewB's finally block
    Inside A's ESampleError handler
    Exit A
    end main
    Код в блоке finally выполнится при любой ошибке, возникшей в соответствующем блоке try. Он же выполнится и в том случае, если ошибки не возникло. В любом случае память будет освобождена. Если возникла ошибка, то сначала выполняется блок finally, затем начинается поиск подходящего обработчика. В штатной ситуации, после блока finally


    программа переходит на следующее предложение после блока.
    Почему вызов GetMem не помещен внутрь блока try? Этот вызов может окончиться неудачно и вызвать exception EOutOfMemory. Если это произошло, то FreeMem попытается освободить память, которая не была распределена. Когда мы размещаем GetMem вне защищаемого участка, то предполагаем, что B сможет получить нужное количество памяти, а если нет, то более верхняя процедура получит уведомление EOutOfMemory.
    А что, если требуется в B распределить 4 области памяти по схеме все-или-ничего? Если первые две попытки удались, а третья провалилась, то как освободить захваченную область память? Можно так:
    procedure NewB;
    var
    p,q,r,s: Pointer;
    begin
    writeln('enter B');
    P := nil;
    Q := nil;
    R := nil;
    S := nil;
    try
    writeln('enter B''s try block');
    GetMem(P, 1000);
    GetMem(Q, 1000);
    GetMem(R, 1000);
    GetMem(S, 1000);
    C;
    writeln('end of B''s try block');
    finally
    writeln('inside B''s finally block');
    if P <> nil then FreeMem(P, 1000);
    if Q <> nil then FreeMem(Q, 1000);
    if R <> nil then FreeMem(R, 1000);
    if S <> nil then FreeMem(S, 1000);
    end;
    writeln('exit B');
    end;
    Установив сперва указатели в NIL, далее можно определить, успешно ли прошел вызов GetMem.
    Оба типа конструкции try можно использовать в любом месте, допускается вложенность любой глубины. Исключительную ситуацию можно вызывать внутри обработчика ошибки, конструкцию try можно использовать внутри обработчика исключительной ситуации.


  • как во время выполнения программы

    В первом примере (проект SHAPE.DPR, рис.1) показано, как во время выполнения программы можно изменять свойства объекта TShape. Изменение цвета объекта (событие OnChange для ColorGrid1):

  • procedure TForm1.ColorGrid1Change(Sender: TObject);

  • begin
    Shape1.Brush.Color:=ColorGrid1.ForeGroundColor;
    end;
    как во время выполнения программы

    Во втором примере (проект PIXELS.DPR, рис.2) показано, как осуществить доступ к отдельной точке на изображении (на канве). По нажатию кнопки “Fill” всем точкам изображения присваивается свой цвет:
    procedure TForm1.Button1Click(Sender: TObject);
    var
    i, j : Longint;
    begin
    Button1.Enabled:=False;
    with Canvas do
    for i:=1 to Width do begin
    Application.ProcessMessages;
    for j:=1 to Height do
    Pixels[i,j]:=i*j;
    end;
    Button1.Enabled:=True;
    end;
    как во время выполнения программы

    В третьей программе (проект DRAW.DPR, рис.3) приведен пример использования методов, выводящих изображение - Draw и StretchDraw:
    как во время выполнения программы

    Прорисовка изображений происходит в обработчике события OnPaint для формы:
    procedure TForm1.FormPaint(Sender: TObject);
    begin
    with Canvas do begin
    Draw(0,0, Image1.Picture.BitMap);
    StretchDraw(Rect(250,0,350,50),Image1.Picture.BitMap)
    end;
    end;

    Работа с Данными

  • Следующие методы позволяют Вам изменить данные, связанные с TTable:

  • procedure Append;
    procedure Insert;
    procedure Cancel;
    procedure Delete;
    procedure Edit;
    procedure Post;
    Все эти методы - часть TDataSet, они унаследованы и используются TTable и TQuery.
    Всякий раз, когда Вы хотите изменить данные, Вы должны сначала перевести DataSet в режим редактирования. Как Вы увидите, большинство визуальных компонент делают это автоматически, и когда Вы используете их, то совершенно не будете об этом заботиться. Однако, если Вы хотите изменить TTable программно, Вам придется использовать вышеупомянутые функции.
    Имеется a типичная последовательность, которую Вы могли бы использовать при изменении поля текущей записи:
    Table1.Edit;
    Table1.FieldByName(‘CustName’).AsString := ‘Fred’;
    Table1.Post;
    Первая строка переводит БД в режим редактирования. Следующая строка присваивает значение ‘Fred’ полю ‘CustName’. Наконец, данные записываются на диск, когда Вы вызываете Post.
    При использовании такого подхода, Вы всегда работаете с записями. Сам факт перемещения к следующей записи автоматически сохраняет ваши данные на диск. Например, следующий код будет иметь тот же самый эффект, что и код показанный выше, плюс этому будет перемещать Вас на следующую запись:
    Table1.Edit;
    Table1.FieldByName(‘CustNo’).AsInteger := 1234;
    Table1.Next;
    Общее правило, которому нужно следовать - всякий раз, когда Вы сдвигаетесь с текущей записи, введенные Вами данные будут записаны автоматически. Это означает, что вызовы First, Next, Prior и Last всегда выполняют Post, если Вы находились в режиме редактирования. Если Вы работаете с данными на сервере и транзакциями, тогда правила, приведенные здесь, не применяются. Однако, транзакции - это отдельный вопрос с их собственными специальными правилами, Вы увидите это, когда прочитаете о них в следующих уроках.
    Тем не менее, даже если Вы не работаете со транзакциями, Вы можете все же отменить результаты вашего редактирования в любое время, до тех пор, пока не вызвали напрямую или косвенно метод Post. Например, если Вы перевели таблицу в режим редактирования, и изменили данные в одном или более полей, Вы можете всегда вернуть запись в исходное состояние вызовом метода Cancel.

    Существуют два метода, названные Append и Insert, который Вы можете использовать всякий раз, когда Вы хотите добавить новую запись в DataSet. Очевидно имеет больше смысла использовать Append для DataSets которые не индексированы, но Delphi не будет генерировать exception если Вы используете Append на индексированной таблице. Фактически, всегда можно использовать и Append, и Insert.

    Продемонстрируем работу методов на простом примере. Чтобы создать программу, используйте TTable, TDataSource и TdbGrid. Открыть таблицу COUNTRY. Затем разместите две кнопки на форме и назовите их ‘Insert’ и ‘Delete’. Когда Вы все сделаете, то должна получиться программа, показанная на рис.5

    Работа с Данными


    Рис.5: Программа может вставлять и удалять запись из таблицы COUNTRY.

    Следующим шагом Вы должен связать код с кнопками Insert и Delete:

    procedure TForm1.InsertClick(Sender: TObject);

    begin

    Table1.Insert;

    Table1.FieldByName('Name').AsString := 'Russia';

    Table1.FieldByName('Capital').AsString := 'Moscow';

    Table1.Post;

    end;

    procedure TForm1.DeleteClick(Sender: TObject);

    begin

    Table1.Delete;

    end;

    Процедура показанная здесь сначала переводит таблицу в режим вставки (новая запись с незаполненными полями вставляется в текущую позицию dataset). После вставки пустой записи, следующим этапом нужно назначить значения одному или большему количеству полей. Существует, конечно, несколько различных путей присвоить эти значения. В нашей программе Вы могли бы просто ввести информацию в новую запись через DBGrid. Или Вы могли бы разместить на форме стандартную строку ввода (TEdit) и затем установить каждое поле равным значению, которое пользователь напечатал в этой строке:

    Table1.FieldByName(‘Name’).AsString := Edit1.Text;

    Можно было бы использовать компоненты, специально предназначенные для работы с данными в DataSet.

    Назначение этой главы, однако, состоит в том, чтобы показать, как вводить данные из программы. Поэтому, в примере вводимая информация скомпилирована прямо в код программы:

    Table1.FieldByName('Name').AsString := 'Russia';


    Один из интересных моментов в этом примере это то, что нажатие кнопки Insert дважды подряд автоматически вызывает exception ‘Key Violation’. Чтобы исправить эту ситуацию, Вы должны либо удалить текущую запись, или изменять поля Name и Capital вновь созданной записи.

    Просматривая код показанный выше, Вы увидите, что просто вставка записи и заполнения ее полей не достаточно для того, чтобы изменить физические данные на диске. Если Вы хотите, чтобы информация записалась на диск, Вы должны вызывать Post.

    Если после вызова Insert, Вы решаете отказаться от вставки новой записи, то Вы можете вызвать Cancel. Если Вы сделаете это прежде, чем Вы вызовете Post, то все что Вы ввели после вызова Insert будет отменено, и dataset будет находиться в состоянии, которое было до вызова Insert.

    Одно дополнительное свойство, которое Вы должны иметь в виду называется CanModify. Если CanModify возвращает False, то TTable находиться в состоянии ReadOnly. В противном случае CanModify возвращает True и Вы можете редактировать или добавлять записи в нее по желанию. CanModify - само по себе ‘read only’ свойство. Если Вы хотите установить DataSet в состояние только на чтение (Read Only), то Вы должны использовать свойство ReadOnly, не CanModify.

    Редактор Компонент

  • Редактор Компонент во многом похож на Редактор свойств, отличия в том, что его используют для изменений скорее всего объекта, нежели отдельного свойства.

  • Давайте взглянем на класс TComponentEditor в модуле DSGNINTF.PAS:
    TComponentEditor = class
    private
    FComponent: TComponent;
    FDesigner: TFormDesigner;
    public
    constructor Create(AComponent: TComponent;
    ADesigner: TFormDesigner); virtual;
    procedure Edit; virtual;
    procedure ExecuteVerb(Index: Integer); virtual;
    function GetVerb(Index: Integer): string; virtual;
    function GetVerbCount: Integer; virtual;
    procedure Copy; virtual;
    property Component: TComponent read FComponent;
    property Designer: TFormDesigner read FDesigner;
    end;
    Редактор Компонент создается для каждого выбранного объекта на форме основываясь на классе объекта. При двойном щелчке на объекте вызывается метод Edit Редактора Компонент. При вызове контекстного меню (popup menu) по правой кнопке мыши, то для построения этого меню вызываются методы GetVerbCount и GetVerb. Если в этом меню выбирается пункт, то вызывается метод ExecuteVerb. Copy
    вызывается при копировании компонента в Clipboard.
    Редактор Компонент по умолчанию (TDefaultEditor) при двойном щелчке на объекте создает (или переходит на) в Редакторе Исходного Текста заготовку для событий OnCreate, OnChanged или OnClick (какое первым попадется).
    При создании Редактора Компонент вы должны переопределить либо метод Edit, либо три следующих метода: GetVerb, GetVerbCount и ExecuteVerb. Можно переопределять все четыре метода.
    Если Редактор Компонент был вызван и изменил компонент, то всегда обязательно нужно вызвать метод Designer.Modified, чтобы Дизайнер об этом узнал.
    Методы и свойства TComponentEditor:
    Create(AComponent, ADesigner): Конструктор Редактора Компонент. AComponent - редактируемый компонент. ADesigner - интерфейс к Дизайнеру среды Delphi.
    Edit: Вызывается при двойном щелчке мышью на компоненте. Редактор Компонент может вызвать какой-нибудь диалог или эксперт.

    ExecuteVerb(Index): Выполняется, когда был выбран пункт номер Index из контекстного меню. Считается, что Редактор Компонент знает, как проинтерпретировать это значение.

    GetVerb(Index): Редактор Компонент должен вернуть в этом методе строку, которая будет показана в виде пункта контекстного меню. Можно использовать обычные для пунктов меню символы, например &.

    GetVerbCount: Возвращает число, которое определяет на какие значения будут отвечать методы GetVerb и ExecuteVerb. Например, если это число равно 3, то в меню будет добавлено три пункта со значениями Index от 0 до 2.

    Copy: Вызывается, когда компонент нужно скопировать в Clipboard. На самом деле, образы полей компонента уже находятся в Clipboard. Просто предоставляется возможность скопировать различные типы форматов, которые игнорируются Дизайнером, но которые могут быть распознаны другими приложениями.

    Редакторы свойств

  • Как Вы знаете, во время дизайна для настройки внешнего вида и поведения объекта нужно пользоваться Инспектором Объектов. Например, можно изменить цвет фона у объекта TLabel на форме.

  • Перейдем в окно Инспектора Объектов и выберем свойство Color - отметьте, что справа есть маленькая стрелка, она означает, что мы можем выбрать цвет из списка. Нажмите мышкой на эту стрелку (рис.1)
    Редакторы свойств

    Столкнувшись с этим в первый раз Вы могли подумать, что этот список цветов является некоей функцией, жестко заданной разработчиками среды программирования Delphi. В действительности, для свойства Color используется соответствующий Редактор Свойств. И Вам вовсе не нужно работать в компании Borland, чтобы создать подобные Редакторы Свойств. Точно так же, как Вы добавляете новые компоненты в Delphi, Вы можете добавить свой собственный Редактор Свойств в среду разработки.

    Регистрация Редактора Свойств

  • Новый Редактор Свойств готов, осталось только его зарегистрировать в среде Delphi. Для этого в интерфейсной части модуля с нашим редактором требуется поместить декларацию процедуры Register, а в части implementation

  • написать следующее:
    procedure Register;
    begin
    RegisterPropertyEditor(TypeInfo(String), TControl, 'Hint',
    THintProperty);
    end;
    Как уже сообщалось выше, один и тот же редактор свойств можно “привязать” к свойствам, в зависимости от их названия или типа объекта. Это определяется параметрами (второй и третий), которые передаются во время регистрации в процедуре RegisterPropertyEditor. Возможны четыре варианта:
    SHORT числовое поле длиной 2 байта, которое может содержать только целые числа в диапазоне от -32768 до 32767
    LONG числовое поле длиной 4 байта, которое может содержать целые числа в диапазоне от -2147483648 до 2147483648
    FLOAT числовое поле длиной 4 байта, значение которого может быть положительным и отрицательным. Диапазон чисел - от 3.4*10-38
    DOUBLE числовое поле длиной 8 байт (длина зависит от платформы), значение которого может быть положительным и отрицательным. Диапазон чисел - от 1.7*10-308
    CHAR строка символов фиксированной длины (0-32767 байт), содержащая любые печатаемые символы. Число символов зависит от Character Set, установленного в InterBase для данного поля или для всей базы данных (например, для символов в кодировке Unicode число символов будет в два раза меньше длины строки)
    VARCHAR строка символов переменной длины (0-32767 байт), содержащая любые печатаемые символы. Число символов также зависит от Character Set, установленного в InterBase для данного поля или для всей базы данных
    DATE поле даты длиной 8 байт, значение которого может быть от 1 января 100 года до 11 декабря 5941 года (время также содержится)
    BLOB поле, содержащее любую двоичную информацию. Может иметь любую длину. Database Desktop не имеет возможности редактировать поля типа BLOB
    ARRAY поле, содержащее массивы данных. InterBase позволяет определять массивы, имеющие размерность 16. Поле может иметь любую длину. Однако, Database Desktop не имеет возможности не только редактировать поля типа ARRAY, но и создавать их
    TEXT BLOB подтип BLOB-поля, содержащее только текстовую информацию. Может иметь любую длину. Database Desktop не имеет возможности редактировать поля типа TEXT BLOB

  • Пояснение к таблице. Если вы зарегистрировали Редактор и указали как класс компоненты, так и имя свойства, то данный редактор “привязывается” ко всем свойствам, которые:




  • имеют тип, указанный в первом параметре процедуры;


  • принадлежат компоненте, которая относится к классу (или его потомкам), указанному во втором параметре;


  • имеют имя, совпадающее с указанным в третьем параметре;

  • Если вместо типа класса в процедуре регистрации стоит Nil, а вместо имени свойства - пустая строка ‘’, то данный редактор “привязывается” ко всем свойствам, которые имеют тип, указанный в первом параметре, независимо от их имени или принадлежности к объекту какого-либо класса.
    Если указан только класс, то редактор относится ко всем свойствам указанного типа для объектов указанного класса.
    Если указано только имя, то редактор относится к свойствам указанного типа, которые имеют указанное имя.
    В нашем случае Редактор Свойств зарегистрирован для всех свойств, которые имеют тип String, относятся к компоненте класса TControl или наследника от него и имеют имя ‘Hint’.




  • Добавлена панель для воспроизведения видео и переключатель окно/панель.

    Во время выполнения программы может потребоваться отобразить текущее состояние объекта MediaPlayer и самого ролика (время, прошедшее с начала воспроизведения, длину ролика). Для этого у объекта TMediaPlayer есть соответствующие свойства и события: Length, Position, OnNotify и др. Давайте добавим в проект прогресс-индикатор (TGauge), который отобразит в процентах, сколько прошло времени (см. рис.6). Для обновления показаний индикатора можно воспользоваться таймером. Поместите на форму объект TTimer, установите для него Interval = 100 (100 миллисекунд). В обработчике события OnTimer нужно записать:
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
    with MediaPlayer1 do
    if FileName<>'' then
    Gauge1.Progress:=Round(100*Position/Length);
    end;
    Запустите проект, выберите файл (AVI) и щелкните на нем два раза мышкой. При воспроизведении ролика прогресс-индикатор должен отображать процент, соответствующий прошедшему времени (см. рис.6).
    Добавлена панель для воспроизведения видео и переключатель окно/панель.



  • Синтаксис обработки исключительных ситуаций

    Теперь, когда мы рассмотрели, что такое исключительные ситуации, давайте дадим ясную картину, как они применяются. Новое ключевое слово, добавленное в язык Object Pascal - try. Оно используется для обозначения первой части защищенного участка кода. Существует два типа защищенных участков:


  • try..except


  • try..finally

  • Первый тип используется для обработки исключительных ситуаций. Его синтаксис:
    try
    Statement 1;
    Statement 2;
    ...
    except
    on Exception1 do Statement;
    on Exception2 do Statement;
    ...
    else
    Statements; {default exception-handler}
    end;
    Для уверенности в том, что ресурсы, занятые вашим приложением, освободятся в любом случае, Вы можете использовать конструкцию второго типа. Код, расположенный в части finally, выполняется в любом случае, даже если возникает исключительная ситуация. Соответствующий синтаксис:
    try
    Statement1;
    Statement2;
    ...
    finally
    Statements; { These statements always execute }
    end;




  • События в Delphi

  • Объекты из библиотеки визуальных компонент (VCL) Delphi, равно как и объекты реального мира, имеют свой набор свойств и свое поведение - набор откликов на события, происходящие с ними. Список событий для данного объекта, на которые он реагирует, можно посмотреть, например, в Инспекторе Объектов на странице событий. (На самом деле, на этой странице представлен список свойств, которые имеют тип вроде TMouseMoveEvent и представляют из себя процедуры-обработчики событий. Существует соглашение по названиям данных свойств. Например, OnDblClick соответствует двойному щелчку мыши, а OnKeyUp - событию, когда нажатая клавиша была отпущена.) Среди набора событий для различных объектов из VCL есть как события, портируемые из Windows (MouseMove, KeyDown), так и события, порождаемые непосредственно в программе (DataChange для TDataSource).

  • Поведение объекта определяется тем, какие обработчики и для каких событий он имеет. Создание приложения в Delphi состоит из настройки свойств используемых объектов и создания обработчиков событий.
    Простейшие события, на которые иногда нужно реагировать - это, например, события, связанные с мышкой (они есть практически у всех видимых объектов) или событие Click для кнопки TButton. Предположим, что вы хотите перехватить щелчок левой кнопки мыши на форме. Чтобы сделать это - создайте новый проект, в Инспекторе Объектов выберите страницу событий и сделайте двойной щелчок на правой части для свойства OnClick. Вы получите заготовку для обработчика данного события:
    procedure TForm1.FormClick(Sender: TObject);
    begin
    end;
    Напишите здесь следующее:
    procedure TForm1.FormClick(Sender: TObject);
    begin
    MessageDlg('Hello', mtInformation, [mbOk], 0);
    end;
    Каждый раз , когда делается щелчок левой кнопки мыши над формой будет появляться окно диалога (см. рис.1).
    События в Delphi

    Рис.1: Диалог, появляющийся при щелчке мыши на форме.
    Код, приведенный выше, представляет из себя простейший случай ответа на событие в программе на Delphi. Он настолько прост, что многие программисты могут написать такой код и без понимания того, что они на самом деле отвечают на сообщение о событии, посланное им операционной системой. Хотя программист получает это событие через третьи руки, тем не менее он на него отвечает.

    Опытные программисты в Windows знают, что при возникновении события, операционная система передает вам не только уведомление о нем, но и некоторую связанную с ним информацию. Например, при возникновении события “нажата левая кнопка мыши” программа информируется о том, в каком месте это произошло. Если вы хотите получить доступ к такой информации, то должны вернуться в Инспектор Объектов и создать обработчик события OnMouseDown:
    procedure TForm1.FormMouseDown(Sender: TObject;
    Button: TMouseButton;
    Shift: TShiftState;
    X, Y: Integer);
    begin
    Canvas.TextOut(X, Y, 'X='+IntToStr(X)+' Y='+IntToStr(Y));
    end;
    Запустите программу, пощелкайте мышкой на форме:
    События в Delphi

    Рис.2
    Как видите, в Delphi очень просто отвечать на события. И не только на события, связанные с мышкой. Например, можно создать обработчик для OnKeyDown (нажата клавиша):
    procedure TForm1.FormKeyDown(Sender: TObject;
    var Key: Word;
    Shift: TShiftState);
    begin
    MessageDlg(Chr(Key), mtInformation, [mbOk], 0);
    end;
    Теперь, когда вы имеете начальные знания о программировании событий в Delphi, самое время вернуться назад и посмотреть на теорию, стоящую за тем кодом, что вы написали. После получения представления о том, как работает система, можно вернуться к среде Delphi и посмотреть, как использовать полностью имеющиеся возможности.

    Соглашения по наименованиям

    Если вы рассматривали исходные тексты VCL, то могли видеть, что они следуют нескольким простым соглашениям при определении новых классов. Delphi этого не требует, имена методов, свойств и т.п. могут быть любыми, компилятору это безразлично. Но если следовать этим соглашениям, то разработка новых компонентов и чтение исходных текстов станет существенно проще.
    Итак:


  • Все декларации типов начинаются на букву T. Еще раз, Delphi не требует этого, но это делает очевидным, что "TEdit", например, есть определение типа, а не переменная или поле класса.


  • Имена свойствам нужно давать легко читаемые и информативные. Нужно помнить, что пользователь будет их видеть в Инспекторе Объектов. И имя вроде "TextOrientation" много удобнее, нежели "TxtOr". То же самое относится к методам. Методы, доступные пользователю, должны иметь удобные названия.


  • При создании свойств типа Event, имя такого свойства должно начинаться с “On” (например, OnClick, OnCreate и т.д.).


  • Имя метода для чтения свойства должен начинаться со слова “Get”. Например, метод GetStyle должен выполнять чтение для свойства Style.


  • Имя метода для записи свойства должен начинаться со слова “Set”. Например, метод SetStyle должен выполнять запись в свойство Style.


  • Внутреннее поле для хранения данных свойства должно носить имя, начинающееся с буквы “F”. Например, свойство Handle могло бы храниться в поле FHandle.

  • Конечно же, есть исключения из правил. Иногда бывает удобнее их нарушить, например, класс TTable имеет свойства типа Event, которые называются BeforePost, AfterPost и т.п.




  • Сохранение OLE объекта в базе данных

    Иногда необходимо хранить OLE объекты не в файлах, а в базе данных (BLOB поле в таблице). Конечно, в данном случае OLE объект должен быть присоединенным (embedded) в целях переносимости. К сожалению, в стандартной поставке Delphi нет специального объекта типа TDBOLEContainer для данных целей, но OLE объект можно сохранять и восстанавливать с помощью методов SaveToStream и LoadFromStream. Например:
    procedure TOLEForm.SaveOLE(Sender: TObject);
    var
    BlSt : TBlobStream;
    begin
    With Table1 do
    BlSt:=TBlobStream.Create(BlobField(FieldByName('OLE')),
    bmReadWrite);
    OLEContainer.SaveToStream(BlSt as TStream);
    BlSt.Free;
    end;

    Сохранение программы

  • Вы приложили некоторые усилия по созданию программы и можете захотеть ее сохранить. Это позволит загрузить программу позже и снова с ней поработать.

  • Первый шаг - создать поддиректорию для программы. Лучше всего создать директорию, где будут храниться все Ваши программы и в ней - создать поддиректорию для данной конкретной программы. Например, Вы можете создать директорию MYCODE и внутри нее - вторую директорию TIPS1, которая содержала бы программу, над которой Вы только что работали.
    После создания поддиректории для хранения Вашей программы нужно выбрать пункт меню File | Save Project. Сохранить нужно будет два файла. Первый - модуль (unit), над которым Вы работали, второй - главный файл проекта, который "владеет" Вашей программой. Сохраните модуль под именем MAIN.PAS и проект под именем TIPS1.DPR. (Любой файл с расширением PAS и словом “unit” в начале является модулем.)

    Создание DLL в Delphi (экспорт)

    Для программирования DLL Delphi предоставляет ряд ключевых слов и правил синтаксиса. Главное - DLL в Delphi такой же проект как и программа.
    Рассмотрим шаблон DLL:
    library MyDll;
    uses
    <используемые модули>;
    <объявления и описания функций>
    exports
    <экспортируемые функции>
    begin
    <инициализационная часть>
    end.
    Имя файла проекта для такого шаблона должно быть MYDLL.DPR.
    !!!! К сожалению, в IDE Delphi автоматически генерируется только проект программы, поэтому Вам придется проект DLL готовить вручную. В Delphi 2.0 это неудобство устранено.
    Как и в программе, в DLL присутствует раздел uses. Инициализационная часть необязательна. В разделе же exports перечисляются функции, доступ к которым должен производится из внешних приложений.
    Экспортирование функций (и процедур ) может производится несколькими способами:


  • по номеру (индексу);

  • по имени.

  • В зависимости от этого используется различный синтаксис:
    {экспорт по индексу}
    procedure ExportByOrdinal; export;
    begin
    .....
    end;
    exports
    ExportByOrdinal index 10;
    {экспорт по имени}
    procedure ExportByName; export;
    begin
    .....
    end;
    exports
    ExportByName name 'MYEXPORTPROC'; { имя для экспорта может не совпадать с именем функции ! }
    Так как в Windows существует понятие "резидентных функций" DLL, то есть тех функций, которые находятся в памяти на протяжении всего времени существования DLL в памяти, в Delphi имеются средства для организации и такого рода экспорта:
    exports
    ExportByName name 'MYEXPORTPROC' resident;
    Стоит отметить тот факт, что поиск функций, экспортируемых по индексу, производится быстрее, чем при экспорте по имени. С другой стороны, экспорт по имени удобнее, особенно если Вы периодически дополняете и расширяете набор экспортируемых из DLL функций, при гарантии работы приложений, использующих DLL, и не хотите специально следить за соблюдением уникальности и соответствия индексов.
    Если же Вы будете экспортировать функции следующим образом:
    exports
    MyExportFunc1,
    MyExportFunc2,
    .....;
    то индексирование экспортируемых функций будет произведено Delphi автоматически, а такой экспорт будет считаться экспортом по имени, совпадающему с именем функции. Тогда объявление импортируемой функции в приложении должно совпадать по имени с объявлением функции в DLL. Что же касается директив, накладываемых уже на импортируемые функции, то об этом мы поговорим ниже.




  • Создание отчета

    В данной главе показан пример построения достаточно простого отчета на основе данных из таблиц, которые находятся в каталоге \DELPHI\DEMOS\DATA. В отчете для каждого заказчика будет выводиться список его заказов с сортировкой по имени заказчика. Для этого потребуется использовать таблицы ORDERS.DB (заказы) и CUSTOMER.DB (заказчики).
    Запустите ReportSmith. Он попросит вас открыть отчет (если отчет уже существует, то можно выбрать имя отчета). Чтобы построить новый отчет, нажмите кнопку Cancel и затем в меню ReportSmith выберите пункт File|New. ReportSmith попросит выбрать тип отчета, который вы хотите построить (см. рис.2). В нашем примере мы будем строить табличный отчет (Columnar report).
    Создание отчета

    Рис.2: Диалог выбора типа отчета
    Если данных в таблицах много, то лучше выбрать режим Draft прежде, чем нажать OK. В этом случае ReportSmith спросит, сколько записей вы хотите использовать при построении отчета. Когда отчет запускается на выполнение, то будут использоваться все записи или то число, которое вы определяете в свойстве MaxRecords.
    После выбора типа отчета укажите ReportSmith таблицу(ы), по которым вы хотите сделать отчет (см. рис.3).
    Создание отчета

    Рис. 3: Диалог добавления таблиц в отчет.
    Для добавления таблицы в отчет нажмите кнопку "Add table...", выберите тип таблицы Paradox (IDAPI) (см. рис.4), и выберите таблицу CUSTOMER.DB из каталога \DEMOS. Точно также добавьте таблицу ORDERS.DB. Следующим шагом нужно установить условия, по которым будет выполняться соединение таблиц при выполнении отчета. В данном случае они связаны по полю CustNo - код заказчика. Установки связи между таблицами происходит в соответствующем диалоге при нажатии кнопки “Add new link…” (см. рис.5). Предварительный этап закончен и можно нажать кнопку “Done”.
    Создание отчета

    Рис. 4: Диалог добавления таблицы в отчет
    Создание отчета

    Рис. 5: Определение связи между таблицами
    ReportSmith считает данные из таблиц и создаст начальный табличный отчет. В нашем отчете слишком большое количество столбцов и он не умещается по ширине на стандартной странице. Можно убрать ненужные колонки, выбирая столбец и нажимая клавишу Del. Удалите все столбцы кроме OrderNo, SaleDate, ShipDate, paymentMethod, AmountPaid. ReportSmith позволяет изменить ширину колонки с помощью мыши. Теперь отчет выглядит примерно так, как на рис.6.

    Создание отчета


    Рис. 6: Отчет с выбранными полями

    Однако, пока это не то, что нам нужно - в отчете нет информации о заказчике, для которого данный заказ предназначен. Прежде, чем добавить эту информацию нужно сгруппировать записи в отчете по принадлежности заказчику. Это делается в пункте меню Tools|Report Grouping… (см. рис.7). Просто выберите поле группировки (в нашем случае нужно сгруппировать записи по коду заказчика - поле CustNo) и нажмите кнопку “OK”. Следующим шагом добавим Header и Footer (т.е. поле перед группой и после нее) для каждой группы. Это выполняется в пункте меню Insert|Headers/Footers (см. рис.8).

    Создание отчета


    Рис. 7: Диалог группировки записей в отчете

    Создание отчета


    Рис. 8: Добавление Header/Footer для группы

    В дальнейшем, в Header для группы мы поместим информацию о заказчике, а в Footer - итоговую сумму всех заказов (т.е. сумму по полю AmountPaid для данной группы). А теперь наш отчет имеет вид, представленный на рис.9. (Для того, чтобы показывались названия полей в каждой группе нужно вызвать пункт меню Insert|Field Labels…)

    Создание отчета


    Рис. 9: Отчет с группами записей и полями перед и после них

    Для того чтобы добавить данные в Header нужно выбрать пункт меню Insert|Field. Появится соответствующий диалог (см. рис.10). В этом диалоге требуется указать поле, которое вы хотите вставить в отчет, нажать кнопку “Insert” и щелкнуть мышью в то место на отчете, куда его нужно поместить. В нашем отчете это будет поле Company и размещаться оно будет в заголовке группы (Header). Кроме того, если нужно, чтобы названия компаний в отчете шли в алфавитном порядке, то это можно указать в пункте меню Tools|Sorting… В диалоге укажите поля для сортировки - Company и OrderNo (номера заказов внутри каждой группы должны быть также упорядочены). После этого нажмите “Done”.

    Создание отчета


    Рис. 10: Диалог добавления поля в отчет

    Теперь добавим суммирующее поле в Footer для группы. Для этого выберите пункт меню Tools|Summary Fields… В диалоге нужно выбрать группу CustNo_Group, поле AmountPaid, операцию Sum и нажать “Add To Group”(см. рис.11). Далее, по аналогии с полем Company, добавьте суммирующее поле в Footer группы (в диалоге вставки поля пункта меню, рис.10, в верхнем ComboBox’е нужно выбрать Summary Fields).

    Отчет готов, его вид показан на рис.12.

    Создание отчета


    Рис. 11: Диалог определения полей суммирования

    Создание отчета


    Рис. 12: Готовый отчет.

    Создание Редактора Свойств

  • При создании нового Редактора Свойств, конечно, не нужно всегда переписывать его заново от базового класса TPropertyEditor. Может оказаться достаточным выбрать в качестве предка уже существующий для данного свойства редактор и переопределить некоторые его методы. Давайте рассмотрим простейший пример нового Редактора Свойств. Как Вы знаете, у всех видимых объектов есть свойство Hint - подсказка, появляющаяся во время выполнения программы, если задержать на некоторое время мышь на объекте. Это свойство имеет тип String и во время дизайна для его редактирования используется Редактор типа TStringProperty. Обычно, подсказка бывает однострочной, но в некоторых случаях ее нужно сделать многострочной. В принципе, здесь проблемы нет, достаточно во время выполнения программы присвоить свойству Hint нужное значение, например:

  • Button1.Hint:=’Line1’#13#10’Line2’;
    Теперь подсказка будет состоять из двух строк. Но это достаточно неудобно, более удобно было бы формировать многострочную подсказку во время дизайна, однако редактор TStringProperty такой возможности не дает. Давайте создадим новый редактор, который мог бы это сделать.
    В нашем случае будет достаточно выбрать в качестве предка редактор TStringProperty и переписать некоторые методы. Во-первых, нужно переопределить метод Edit, в котором будет вызываться диалог для ввода строк подсказки. Во-вторых, нужно переопределить функцию GetAttributes, которая возвращает набор параметров, описывающих данное свойство. В частности, должен быть установлен атрибут paDialog, при этом в Инспекторе Объектов у свойства появится кнопка ‘…’ для вызова диалога. И вообще-то нужно изменить метод GetValue, который используется для отображения значения свойства в Инспекторе Объектов.
    Назовем новый Редактор Свойств THintProperty, декларация нового класса:
    THintProperty = class(TStringProperty)
    public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue : String; override;
    procedure Edit; override;
    end;
    Рассмотрим по порядку методы нового класса.

    Функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (появляется кнопка ‘…’) и paReadOnly (свойство нельзя редактировать непосредственно в Инспекторе Объектов, а только в диалоге, вызываемом через кнопку ‘…’):

    function THintProperty.GetAttributes: TPropertyAttributes;

    begin

    Result := inherited GetAttributes + [paDialog,

    paReadOnly];

    end;

    Функция GetValue заменяет “неправильные” символы #10 и #13 (перевод каретки и переход на новую строку) на символ “>”:

    function THintProperty.GetValue : string;

    var

    i : Byte;

    begin

    result:=inherited GetValue;

    for i:=1 to Byte(result[0]) do

    if result[i]<#32 then result[i]:='>';

    end;

    Процедура Edit вызывает диалог для ввода строк подсказки. Диалог можно было бы нарисовать свой собственный, однако можно воспользоваться уже готовым. Несколько разных диалогов лежит в директории X:\DELPHI\SOURCE\LIB. Мы воспользуемся модулем STREDIT.PAS, в котором есть необходимый диалог редактирования строк. Итак, процедура Edit:

    procedure THintProperty.Edit;

    var

    HintEditDlg : TStrEditDlg;

    s : string;

    begin

    HintEditDlg:=TStrEditDlg.Create(Application);

    with HintEditDlg do

    try

    Memo.MaxLength := 254;

    s:=GetStrValue+#0;

    Memo.Lines.SetText(@s[1]);

    UpdateStatus(nil);

    ActiveControl := Memo;

    if ShowModal = mrOk then begin

    s:=StrPas(Memo.Lines.GetText);

    if s[0]>#2 then Dec(Byte(s[0]),2);

    SetStrValue(s);

    end;

    finally

    Free;

    end;

    end;

    Строка if s[0]>#2 then Dec(Byte(s[0]),2)

    нужна, так как Memo.Lines.GetText возвращает все строки с символами #13#10.

    Создание Связанных Курсоров (Linked cursors)

    Связанные курсоры позволяют программистам определить отношение один ко многим (one-to-many relationship). Например, иногда полезно связать таблицы CUSTOMER и ORDERS так, чтобы каждый раз, когда пользователь выбирает имя заказчика, то он видит список заказов связанных с этим заказчиком. Иначе говоря, когда пользователь выбирает запись о заказчике, то он может просматривать только заказы, сделанные этим заказчиком.
    Программа LINKTBL демонстрирует, как создать программу которая использует связанные курсоры. Чтобы создать программу заново, поместите два TTable, два TDataSources и два TDBGrid на форму. Присоедините первый набор таблице CUSTOMER, а второй к таблице ORDERS. Программа в этой стадии имеет вид, показанный на рис.8
    Создание Связанных Курсоров (Linked cursors)

    Рис.8: Программа LINKTBL показывает, как определить отношения между двумя таблицами.
    Следующий шаг должен связать таблицу ORDERS с таблицей CUSTOMER так, чтобы Вы видели только те заказы, которые связанные с текущей записью в таблице заказчиков. В первой таблице заказчик однозначно идентифицируется своим номером - поле CustNo. Во второй таблице принадлежность заказа определяется также номером заказчика в поле CustNo. Следовательно, таблицы нужно связывать по полю CustNo в обоих таблицах (поля могут иметь различное название, но должны быть совместимы по типу). Для этого, Вы должны сделать три шага, каждый из которых требует некоторого пояснения:

  • Установить свойство Table2.MasterSource = DataSource1

  • Установить свойство Table2.MasterField = CustNo

  • Установить свойство Table2.IndexName = CustNo

  • Если Вы теперь запустите программу, то увидите, что обе таблицы связаны вместе, и всякий раз, когда Вы перемещаетесь на новую запись в таблице CUSTOMER, Вы будете видеть только те записи в таблице ORDERS, которые принадлежат этому заказчику.
    Свойство MasterSource в Table2 определяет DataSource от которого Table2 может получить информацию. То есть, оно позволяет таблице ORDERS знать, какая запись в настоящее время является текущей в таблице CUSTOMERS.

    Создание таблиц с помощью компонента TTable

    Для создания таблиц компонент TTable имеет метод CreateTable. Этот метод создает новую пустую таблицу заданной структуры. Данный метод (процедура) может создавать только локальные
    таблицы формата dBase или Paradox.
    Компонент TTable можно поместить на форму в режиме проектирования или создать динамически во время выполнения. В последнем случае перед использованием его необходимо создать, например, с помощью следующей конструкции:
  • var

  • Table1: TTable;
    ...
    Table1:=TTable.Create(nil);
    ...
    Перед вызовом метода CreateTable необходимо установить значения свойств


  • TableType - тип таблицы


  • DatabaseName - база данных


  • TableName - имя таблицы


  • FieldDefs - массив описаний полей


  • IndexDefs - массив описаний индексов.

  • Свойство TableType имеет тип TTableType и определяет тип таблицы в базе данных. Если это свойство установлено в ttDefault, тип таблицы определяется по расширению файла, содержащего эту таблицу:


  • Расширение .DB или без расширения: таблица Paradox


  • Расширение .DBF : таблица dBASE


  • Расширение .TXT : таблица ASCII (текстовый файл).

  • Если значение свойства TableType не равно ttDefault, создаваемая таблица всегда будет иметь установленный тип, вне зависимости от расширения:


  • ttASCII: текстовый файл


  • ttDBase: таблица dBASE


  • ttParadox: таблица Paradox.

  • Свойство DatabaseName определяет базу данных, в которой находится таблица. Это свойство может содержать:


  • BDE алиас


  • директорий для локальных БД


  • директорий и имя файла базы данных для Local InterBase


  • локальный алиас, определенный через компонент TDatabase.

  • Свойство TableName определяет имя таблицы базы данных.
    Свойство FieldDefs (имеющее тип TFieldDefs) для существующей таблицы содержит информацию обо всех полях таблицы. Эта информация доступна только в режиме выполнения и хранится в виде массива экземпляров класса TFieldDef, хранящих данные о физических полях таблицы (т.о. вычисляемые на уровне клиента поля не имеют своего объекта TFieldDef). Число полей определяется свойством Count, а доступ к элементам массива осуществляется через свойство Items:

  • property Items[Index: Integer]: TFieldDef;


  • При создании таблицы, перед вызовом метода CreateTable, нужно сформировать эти элементы. Для этого у класса TFieldDefs имеется метод Add:

  • procedure Add(const Name: string; DataType: TFieldType; Size: Word; Required: Boolean);


  • Параметр Name, имеющий тип string, определяет имя поля. Параметр DataType (тип TFieldType) обозначает тип поля. Он может иметь одно из следующих значений, смысл которых ясен из их наименования:

  • TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes, ftBlob, ftMemo,


  • ftGraphic);

    Параметр Size

    (тип word) представляет собой размер поля. Этот параметр имеет смысл только для полей типа ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic, размер которых может сильно варьироваться. Поля остальных типов всегда имеют строго фиксированный размер, так что данный параметр для них не принимается во внимание. Четвертый параметр - Required - определяет, может ли поле иметь пустое значение при записи в базу данных. Если значение этого параметра - true, то поле является “требуемым”, т.е. не может иметь пустого значения. В противном случае поле не является “требуемым” и, следовательно, допускает запись значения NULL. Отметим, что в документации по Delphi и online-справочнике допущена ошибка - там отсутствует упоминание о четвертом параметре для метода Add.

    Если Вы желаете индексировать таблицу по одному или нескольким полям, используйте метод Add для свойства IndexDefs, которое, как можно догадаться, также является объектом, т.е. экземпляром класса TIndexDefs. Свойство IndexDefs для существующей таблицы содержит информацию обо всех индексах таблицы. Эта информация доступна только в режиме выполнения и хранится в виде массива экземпляров класса TIndexDef, хранящих данные об индексах таблицы. Число индексов определяется свойством Count, а доступ к элементам массива осуществляется через свойство Items:

  • property Items[Index: Integer]: TIndexDef;



  • Метод Add класса TIndexDefs имеет следующий вид:

  • procedure Add(const Name, Fields: string;


  • Options: TIndexOptions);

    Параметр Name, имеющий тип string, определяет имя индекса. Параметр Fields (также имеющий тип string) обозначает имя поля, которое должно быть индексировано, т.е. имя индексируемого поля. Составной индекс, использующий несколько полей, может быть задан списком имен полей, разделенных точкой с запятой “;”, например: ‘Field1;Field2;Field4’. Последний параметр - Options - определяет тип индекса. Он может иметь набор значений, описываемых типом TIndexOptions:

  • TIndexOptions = set of (ixPrimary, ixUnique, ixDescending,


  • ixCaseInsensitive, ixExpression);

    Поясним эти значения. ixPrimary обозначает первичный ключ, ixUnique - уникальный индекс, ixDescending - индекс, отсортированный по уменьшению значений (для строк - в порядке, обратном алфавитному), ixCaseInsensitive - индекс, “нечувствительный” к регистру букв, ixExpression - индекс по выражению. Отметим, что упоминание о последнем значении также отсутствует в документации и online-справочнике. Опция ixExpression позволяет для таблиц формата dBase создавать индекс по выражению. Для этого достаточно в параметре Fields указать желаемое выражение, например: 'Field1*Field2+Field3'. Вообще говоря, не все опции индексов применимы ко всем форматам таблиц. Ниже мы приведем список допустимых значений для таблиц dBase и Paradox:

  • Опции индексов dBASE Paradox


  • ---------------------------------------

    ixPrimary ь

    ixUnique ь ь

    ixDescending ь ь

    ixCaseInsensitive ь

    ixExpression ь

    Необходимо придерживаться указанного порядка применения опций индексов во избежание некорректной работы. Следует отметить, что для формата Paradox опция ixUnique может использоваться только вместе с опцией ixPrimary (см. пример на диске - Рис. 0-1).

    Итак, после заполнения всех указанных выше свойств и вызова методов Add для FieldDefs и IndexDefs необходимо вызвать метод класса TTable - CreateTable:

  • with Table1 do


  • begin

    DatabaseName:='dbdemos';


    TableName:='mytest';

    TableType:=ttParadox;

    {Создать поля}

    with FieldDefs do

    begin

    Add('Surname', ftString, 30, true);

    Add('Name', ftString, 25, true);

    Add('Patronymic', ftString, 25, true);

    Add('Age', ftInteger, 0, false);

    Add('Weight', ftFloat, 0, false);

    end;

    {Сгенерировать индексы}

    with IndexDefs do

    begin

    Add('I_Name', 'Surname;Name;Patronymic',

    [ixPrimary, ixUnique]);

    Add('I_Age', 'Age', [ixCaseInsensitive]);

    end;

    CreateTable;

    end;

    Создание таблиц с помощью компонента TTable


    Индексы можно сгенерировать и не только при создании таблицы. Для того чтобы сгенерировать индексы для существующей таблицы, нужно вызвать метод AddIndex класса TTable, набор параметров которого полностью повторяет набор параметров для метода Add класса TIndexDefs:

  • procedure AddIndex(const Name, Fields: string;


  • Options: TIndexOptions);

    При этом для метода AddIndex справедливы все замечания по поводу записи полей и опций индексов, сделанные выше.





  • Специальные свойства TQuery

    Есть несколько свойств, принадлежащих TQuery, которые еще не упоминались:
    property UniDirectional: Boolean;
    property Handle: HDBICur;
    property StmtHandle: HDBIStmt;
    property DBHandle: HDBIDB;
    Свойство UniDirectional используется для того, чтобы оптимизировать доступ к таблице. Если Вы установите UniDirectional в True, то Вы можете перемещаться по таблице более быстро, но Вы сможете двигаться только вперед.
    Свойство StmtHandle связано со свойством Handle TDataSet. То есть, оно включено исключительно для того, что Вы могли делать вызовы Borland Database Engine напрямую. При нормальных обстоятельствах, нет никакой необходимости использовать это свойство, так как компоненты Delphi могут удовлетворить потребностями большинства программистов. Однако, если Вы знакомы с Borland Database Engine, и если Вы знаете что существуют некоторые возможности не поддерживаемые в VCL, то Вы можете использовать TQuery.StmtHandle, или TQuery. Handle, чтобы сделать вызов напрямую в engine.
    Следующий фрагмент кода показывает два запроса к BDE:
    var
    Name: array[0..100] of Char;
    Records: Integer;
    begin
    dbiGetNetUserName(Name);
    dbiGetRecordCount(Query1.Handle, Records);
    end;

    SQL-выражения для управления транзакциями

  • Для управления транзакциями имеется три выражения:

  • SET TRANSACTION - Начинает транзакцию и определяет ее поведение.
    COMMIT- Сохраняет изменения, внесенные транзакцией, в базе данных и завершает транзакцию.
    ROLLBACK - Отменяет изменения, внесенные транзакцией, и завершает транзакцию.

    Стандартные компоненты

    Для дальнейшего знакомства со средой программирования Delphi потребуется рассказать о составе первой страницы Палитры Компонент.
    На первой странице Палитры Компонент размещены 14 объектов
    (рис.8) определенно важных для использования. Мало кто обойдется длительное время без кнопок, списков, окон ввода и т.д. Все эти объекты такая же часть Windows, как мышь или окно.
    Набор и порядок компонент на каждой странице являются конфигурируемыми. Так, Вы можете добавить к имеющимся компонентам новые, изменить их количество и порядок.
    Стандартные компоненты

    Рис.8: Компоненты, расположенные на первой странице Палитры.
    Стандартные компоненты Delphi перечислены ниже с некоторыми комментариями по их применению. При изучении данных компонент было бы полезно иметь под рукой компьютер с тем, чтобы посмотреть, как они работают и как ими манипулировать.
  • · TMainMenu позволяет Вам поместить главное меню в программу. При помещении TMainMenu на форму это выглядит, как просто иконка. Иконки данного типа называют "невидимыми компонентом", поскольку они невидимы во время выполнения программы. Создание меню включает три шага: (1) помещение TMainMenu на форму, (2) вызов Дизайнера Меню через свойство Items в Инспекторе Объектов, (3) определение пунктов меню в Дизайнере Меню.

  • · TPopupMenu позволяет создавать всплывающие меню. Этот тип меню появляется по щелчку правой кнопки мыши.
    · TLabel служит для отображения текста на экране. Вы можете изменить шрифт и цвет метки, если дважды щелкнете на свойство Font в Инспекторе Объектов. Вы увидите, что это легко сделать и во время выполнения программы, написав всего одну строчку кода.
    · TEdit - стандартный управляющий элемент Windows для ввода. Он может быть использован для отображения короткого фрагмента текста и позволяет пользователю вводить текст во время выполнения программы.
    · TMemo - иная форма TEdit. Подразумевает работу с большими текстами. TMemo может переносить слова, сохранять в Clipboard фрагменты текста и восстанавливать их, и другие основные функции редактора. TMemo имеет ограничения на объем текста в 32Кб, это составляет 10-20 страниц. (Есть VBX и “родные” компоненты Delphi, где этот предел снят).

    · TButton позволяет выполнить какие- либо действия при нажатии кнопки во время выполнения программы. В Delphi все делается очень просто. Поместив TButton на форму, Вы по двойному щелчку можете создать заготовку обработчика события нажатия кнопки. Далее нужно заполнить заготовку кодом (подчеркнуто то, что нужно написать вручную):

    procedure TForm1.Button1Click(Sender: TObject);

    begin

    MessageDlg('Are you there?',mtConfirmation,mbYesNoCancel,0);

    end;

  • · TCheckBox отображает строку текста с маленьким окошком рядом. В окошке можно поставить отметку, которая означает, что что-то выбрано. Например, если посмотреть окно диалога настроек компилятора (пункт меню Options | Project, страница Compiler), то можно увидеть, что оно состоит преимущественно из CheckBox’ов.


  • · TRadioButton позволяет выбрать только одну опцию из нескольких. Если Вы опять откроете диалог Options | Project и выберете страницу Linker Options, то Вы можете видеть, что секции Map file и Link buffer file состоят из наборов RadioButton.

    · TListBox нужен для показа прокручиваемого списка. Классический пример ListBox’а в среде Windows - выбор файла из списка в пункте меню File | Open многих приложений. Названия файлов или директорий и находятся в ListBox’е.

    · TComboBox во многом напоминает ListBox, за исключением того, что позволяет водить информацию в маленьком поле ввода сверху ListBox. Есть несколько типов ComboBox, но наиболее популярен выпадающий вниз (drop-down combo box), который можно видеть внизу окна диалога выбора файла.

    · TScrollbar -

    полоса прокрутки, появляется автоматически в объектах редактирования, ListBox’ах при необходимости прокрутки текста для просмотра.

    · TGroupBox используется для визуальных целей и для указания Windows, каков порядок перемещения по компонентам на форме (при нажатии клавиши TAB).

    · TPanel - управляющий элемент, похожий на TGroupBox, используется в декоративных целях. Чтобы использовать TPanel, просто поместите его на форму и затем положите другие компоненты на него. Теперь при перемещении TPanel будут передвигаться и эти компоненты. TPanel используется также для создания линейки инструментов и окна статуса.





  • TScrollBox представляет место на форме, которое можно скроллировать в вертикальном и горизонтальном направлениях. Пока Вы в явном виде не отключите эту возможность, форма сама по себе действует так же. Однако, могут быть случаи, когда понадобится прокручивать только часть формы. В таких случаях используется TScrollBox.


  • Это полный список объектов на первой странице Палитры Компонент. Если Вам нужна дополнительная информация, то выберите на Палитре объект и нажмите клавишу F1 - появится Справочник с полным описанием данного объекта.







  • Стандартные компоненты


    TRadioGroup используется аналогично TGroupBox, для группировки объектов TRadioButton.

    Стандартные компоненты


    TPanel - управляющий элемент, похожий на TGroupBox, используется в декоративных целях. Чтобы использовать TPanel, просто поместите его на форму и затем положите другие компоненты на него. Теперь при перемещении TPanel будут передвигаться и эти компоненты. TPanel используется также для создания линейки инструментов и окна статуса.

    Это полный список объектов на первой странице Палитры Компонент. Если Вам нужна дополнительная информация, то выберите на Палитре объект и нажмите клавишу F1 - появится Справочник с полным описанием данного объекта.

    Стандартные Редакторы Свойств

  • Прежде, чем приступить к созданию своего Редактора Свойств, давайте исследуем уже имеющиеся в среде Delphi редакторы свойств. Вы уже видели редактор для свойства Color. Даже простейшие свойства, вроде Left или Caption, имеют свои редакторы. Причем, компоненты сами по себе даже не знают, что за редакторы используются для их свойств. Это означает, что Вы можете свой Редактор Свойств связать с уже существующими свойствами. Например, можно было бы написать Редактор Свойств, который ограничивает свойство, имеющее целый тип (Integer), некоторым максимальным значением и затем связать этот редактор со свойством Width для всех существующих компонент.

  • Взглянем на иерархию классов Редакторов Свойств. Базовым является класс TPropertyEditor:
    TPropertyEditor
    TOrdinalProperty
    TIntegerProperty
    TColorProperty
    TModalResultProperty
    TTabOrderProperty
    TCharProperty
    TEnumProperty
    TSetProperty
    TShortCutProperty
    TFloatProperty
    TStringProperty
    TComponentNameProperty
    TFontNameProperty
    TCaptionProperty
    TSetElementProperty
    TClassProperty
    TFontProperty
    TMethodProperty
    TComponentProperty
    Названия классов в большинстве своем очевидны. Класс TFloatProperty связан со свойствами, которые имеют тип Float, класс TSetProperty связан со свойствами, которые имеют тип Set. Некоторые редакторы имеют специальное назначение. Так, например, TTabOrderProperty нужен для того, чтобы предотвратить изменение свойства TabOrder (тип Integer) при выборе на форме нескольких компонент одновременно.

    Страница Additional

  • На странице Standard представлены управляющие элементы, появившиеся в Windows 3.0. На странице Additional размещены объекты, позволяющие создать более красивый пользовательский интерфейс программы.

  • Страница Additional

    Список компонент:
    Страница Additional

    TBitBtn - кнопка вроде TButton, однако на ней можно разместить картинку (glyph). TBitBtn имеет несколько предопределенных типов (bkClose, bkOK и др), при выборе которых кнопка принимает соответствующий вид. Кроме того, нажатие кнопки на модальном окне (Form2.ShowModal) приводит к закрытию окна с соответствующим модальным результатом (Form2.ModalResult).
    Страница Additional

    TSpeedButton - кнопка для создания панели быстрого доступа к командам (SpeedBar). Пример - SpeedBar слева от Палитры Компонент в среде Delphi. Обычно на данную кнопку помещается только картинка (glyph).
    Страница Additional

    TTabSet - горизонтальные закладки. Обычно используется вместе с TNoteBook для создания многостраничных окон. Название страниц можно задать в свойстве Tabs. Но проще это сделать в программе при создании формы (OnCreate) :
    TabSet1.Tabs := Notebook1.Pages;
    А для того, чтобы при выборе закладки страницы перелистывались нужно в обработчике события OnClick для TTabSet написать:
    Notebook1.PageIndex := TabSet1.TabIndex;
    Страница Additional

    TNoteBook - используется для создания многостраничного диалога, на каждой странице располагается свой набор объектов. Используется совместно с TTabSet.
    Страница Additional

    TTabbedNotebook - многостраничный диалог со встроенными закладками, в данном случае - закладки сверху.
    Страница Additional

    TMaskEdit - аналог TEdit, но с возможностью форматированного ввода. Формат определяется в свойстве EditMask. В редакторе свойств для EditMask есть заготовки некоторых форматов: даты, валюты и т.п. Спец. символы для маски можно посмотреть в Справочнике.
    Страница Additional

    TOutline - используется для представления иерархических отношений связанных данных. Например - дерево директорий.
    Страница Additional

    TStringGrid - служит для представления текстовых данных в виде таблицы. Доступ к каждому элементу таблицы происходит через свойство Cell.
    Страница Additional

    TDrawGrid - служит для представления данных любого типа в виде таблицы. Доступ к каждому элементу таблицы происходит через свойство CellRect.

    Страница Additional


    TImage - отображает графическое изображение на форме. Воспринимает форматы BMP, ICO, WMF. Если картинку подключить во время дизайна программы, то она прикомпилируется к EXE файлу.

    Страница Additional


    TShape - служит для отображения простейших графических объектов на форме: окружность, квадрат и т.п.

    Страница Additional


    TBevel - элемент для рельефного оформления интерфейса.

    Страница Additional


    THeader - элемент оформления для создания заголовков с изменяемыми размерами для таблиц.

    Страница Additional


    TScrollBox - позволяет создать на форме прокручиваемую область с размерами большими, нежели экран. На этой области можно разместить свои объекты.

    Страница System

    Страница System

    Страница представляет набор компонент для доступа к некоторым системным сервисам типа таймер, DDE, OLE и т.п.
    Страница System

    TTimer - таймер, событие OnTimer периодически вызывается через промежуток времени, указанный в свойстве Interval. Период времени может составлять от 1 до 65535 мс.
    Страница System

    TPaintBox - место для рисования. В обработчики событий, связанных с мышкой передаются относительные координаты мышки в TPaintBox, а не абсолютные в форме.
    Страница System

    TFileListBox - специализированный ListBox, в котором отображаются файлы из указанной директории (св-во Directory). На названия файлов можно наложить маску, для этого служит св-во Mask. Кроме того, в св-ве FileEdit можно указать объект TEdit для редактирования маски.
    Страница System

    TDirectoryListBox - специализированный ListBox, в котором отображается структура директорий текущего диска. В св-ве FileList можно указать TFileListBox, который будет автоматически отслеживать переход в другую директорию.
    Страница System

    TDriveComboBox - специализированный ComboBox для выбора текущего диска. Имеет свойство DirList, в котором можно указать TDirectoryListBox, который будет отслеживать переход на другой диск.
    Страница System

    TFilterComboBox - специализированный ComboBox для выбора маски имени файлов. Список масок определяется в свойстве Filter. В свойстве FileList указывается TFileListBox, на который устанавливается маска.
    !!!! С помощью последних четырех компонент (TFileListBox, TDirectoryListBox, TDriveComboBox, TFilterComboBox) можно построить свой собственный диалог выбора файла, причем для этого не потребуется написать ни одной строчки кода.
    Страница System

    TMediaPlayer - служит для управления мултимедйными устройствами (типа CD-ROM, MIDI и т.п.). Выполнен в виде панели управления с кнопками Play, Stop, Record и др. Для воспроизведения может понадобиться как соответствующее оборудование, так и программное обеспечение. Подключение устройств и установка ПО производится в среде Windows. Например, для воспроизведения видео, записанного в формате AVI, в потребуется установить ПО MicroSoft Video (в Windows 3.0, 3.1, WFW 3.11).

    Страница System
    Страница System


    TOLEContainer - контейнер, содержащий OLE объекты. Поддерживается OLE 2.02 Подробнее об этом - в последующих уроках.

    TDDEClientConv,TDDEClientItem, TDDEServerConv, TDDEServerItem - 4 объекта для организации DDE. С помощью этих объектов можно построить приложение как DDE-сервер, так и DDE-клиент. Подробнее - в следующих уроках.

    Страница VBX

    Страница System






    Поскольку формат объектов из MicroSoft Visual Basic (VBX) является своего рода стандартом и существует большое количество библиотек таких объектов, то в Delphi была предусмотрена совместимость с этим форматом. VBX версии 1.0 можно включить в Палитру Компонент Delphi и использовать их как “родные” компоненты (в том числе, выбирать их в качестве предков и наследовать свойства и методы).

    TBiSwitch - двухпозиционный переключатель.

    Страница System


    Страница System


    TBiGauge - прогресс-индикатор.

    Страница System


    TBiPict - аналог TImage.

    Страница System


    TChartFX - деловая графика.



    Структура среды программирования

  • Внешний вид среды программирования Delphi отличается от многих других из тех, что можно увидеть в Windows. К примеру, Borland Pascal for Windows 7.0, Borland C++ 4.0, Word for Windows, Program Manager - это все MDI приложения и выглядят по-другому, чем Delphi. MDI (Multiple Document Interface) - определяет особый способ управления нескольких дочерних окон внутри одного большого окна.

  • Среда Delphi же следует другой спецификации, называемой Single Document Interface (SDI), и состоит из нескольких отдельно расположенных окон. Это было сделано из-за того, что SDI близок к той модели приложений, что используется в Windows 95.
    Если Вы используете SDI приложение типа Delphi, то уже знаете, что перед началом работы лучше минимизировать другие приложения, чтобы их окна не загромождали рабочее пространство. Если нужно переключиться на другое приложение, то просто щелкните мышкой на системную кнопку минимизации Delphi. Вместе с главным окном свернутся все остальные окна среды программирования, освободив место для работы других программ.

    Структурная обработка исключительных ситуаций

  • Структурная обработка исключительных ситуаций - это система, позволяющая программисту при возникновении ошибки (исключительной ситуации) связаться с кодом программы, подготовленным для обработки такой ошибки. Это выполняется с помощью языковых конструкций, которые как бы “охраняют” фрагмент кода программы и определяют обработчики ошибок, которые будут вызываться, если что-то пойдет не так в “охраняемом” участке кода. В данном случае понятие исключительной ситуации относится к языку и не нужно его путать с системными исключительными ситуациями (hardware exceptions), такими как General Protection Fault. Эти исключительные ситуации обычно используют прерывания и особые состояния “железа” для обработки критичной системной ошибки; исключительные ситуации в Delphi же независимы от “железа”, не используют прерываний и используются для обработки ошибочных состояний, с которыми подпрограмма не готова иметь дело. Системные исключительные ситуации, конечно, могут быть перехвачены и преобразованы в языковые исключительные ситуации, но это только одно из применений языковых исключительных ситуаций.

  • При традиционной обработке ошибок, ошибки, обнаруженные в процедуре обычно передаются наружу (в вызывавшую процедуру) в виде возвращаемого значения функции, параметров или глобальных переменных (флажков). Каждая вызывающая процедура должна проверять результат вызова на наличие ошибки и выполнять соответствующие действия. Часто, это просто выход еще выше, в более верхнюю вызывающую процедуру и т.д. : функция A вызывает B, B вызывает C, C обнаруживает ошибку и возвращает код ошибки в B, B проверяет возвращаемый код, видит, что возникла ошибка и возвращает код ошибки в A, A проверяет возвращаемый код и выдает сообщение об ошибке либо решает сделать что-нибудь еще, раз первая попытка не удалась.
    Такая “пожарная бригада” для обработки ошибок трудоемка, требует написания большого количества кода в котором можно легко ошибиться и который трудно отлаживать.
    Одна ошибка в коде программы или переприсвоение в цепочке возвращаемых значений может привести к тому, что нельзя будет связать ошибочное состояние с положением дел во внешнем мире. Результатом будет ненормальное поведение программы, потеря данных или ресурсов, или крах системы.

    Структурная обработка исключительной ситуации замещает ручную обработку ошибок автоматической, сгенерированной компилятором системой уведомления. В приведенном выше примере, процедура A установила бы “охрану” со связанным обработчиком ошибки на фрагмент кода, в котором вызывается B. B просто вызывает C. Когда C обнаруживает ошибку, то создает (raise) исключительную ситуацию. Специальный код, сгенерированный компилятором и встроенный в Run-Time Library (RTL) начинает поиск обработчика данной исключительной ситуации. При поиске “защищенного” участка кода используется информация, сохраненная в стеке. В процедурах C и B нет такого участка, а в A - есть. Если один из обработчиков ошибок, которые используются в A, подходит по типу для возникшей в C исключительной ситуации, то программа переходит на его выполнение. При этом, область стека, используемая в B и C, очищается; выполнение этих процедур прекращается.

    Если в A нет подходящего обработчика, то поиск продолжается в более верхнем уровне, и так может идти, пока поиск не достигнет подходящего обработчика ошибок среди используемых по умолчанию обработчиков в RTL. Обработчики ошибок из RTL только показывают сообщение об ошибке и форсированно прекращают выполнение программы. Любая исключительная ситуация, которая осталась необработанной, приведет к прекращению выполнения приложения.

    Без проверки возвращаемого кода после каждого вызова подпрограммы, код программы должен быть более простым, а скомпилированный код - более быстрым. При наличии исключительных ситуаций подпрограмма B не должна содержать дополнительный код для проверки возвращаемого результата и передачи его в A. B ничего не должна делать для передачи исключительной ситуации, возникшей в C, в процедуру A - встроенная система обработки исключительных ситуаций делает всю работу.

    Данная система называется структурной,

    поскольку обработка ошибок определяется областью “защищенного” кода; такие области могут быть вложенными. Выполнение программы не может перейти на произвольный участок кода; выполнение программы может перейти только на обработчик исключительной ситуации активной программы.

    Свойства TReport

  • У TReport есть следующие свойства:

  • AutoUnload
    определяет, выгружается ли ReportSmith Runtime из памяти после завершения печати отчета. Если AutoUnload True, то ReportSmith Runtime выгружается, как только закончена пересылка отчета на печать. Если AutoUnload False, то ReportSmith Runtime остается в памяти. Например, можно создать приложение, которое включает пункт меню, запускающий отчет. После того, как отчет выполнился, можно пожелать, чтобы ReportSmith Runtime остался в памяти, и повторно отчет напечатается быстрее. Чтобы выгрузить ReportSmith Runtime из памяти при AutoUnload=False, нужно вызывать метод CloseApplication.
    EndPage
    указывает последнюю страницу отчета, которая будет напечатана. По-умолчанию это 9999 (чтобы напечатать весь отчет).
    InitialValues - строка переменных отчета, которые используются отчетом при запуске (если таковые имеются). Например, в отчет можно передавать из программы начальную и конечную даты для выборки данных. Указывая значения этих переменных, не требуется использовать диалоги для ввода во время выполнения отчета.
    MaxRecords - количество записей БД, которые вы хотите использовать для создания отчета. Например, если вы хотите только просмотреть примерный отчет, а ваша БД содержит 50,000 записей, вы можете определить в MaxRecords величину, которая ограничивает число записей в отчете значительно меньшей величиной, например 100. Это тот же самое, что и использование ReportSmith в draft режиме.
    PrintCopies
    определяет, сколько копий отчета будут напечатаны.
    ReportDir - каталог, где хранятся файлы отчетов. Определяя каталог отчета, не нужно включать туда имя файла отчета.
    ReportName
    содержит имя отчета, который нужно выполнить. Здесь можно указать полное имя отчета (каталог + имя файла), если вы не определили каталог в свойстве ReportDir или хотите выполнить отчет, которое сохранен в другом месте. Если св-во ReportDir определено, то имя каталога опускается и просто указывается имя отчета.
    StartPage - номер страницы, с которой вы хотите начать печатать отчет. По-умолчанию равен 1.

    Свойство объектов Canvas

  • У ряда объектов из библиотеки визуальных компонент есть свойство Canvas (канва), которое предоставляет простой путь для рисования на них. Эти объекты - TBitmap, TComboBox, TDBComboBox, TDBGrid, TDBListBox, TDirectoryListBox, TDrawGrid, TFileListBox, TForm, TImage, TListBox, TOutline, TPaintBox, TPrinter, TStringGrid. Canvas является в свою очередь объектом, объединяющим в себе поле для рисования, карандаш (Pen), кисть (Brush) и шрифт (Font). Canvas обладает также рядом графических методов : Draw, TextOut, Arc, Rectangle и др. Используя Canvas, Вы можете воспроизводить на форме любые графические объекты - картинки, многоугольники, текст и т.п. без использования компонент TImage,TShape и TLabel (т.е. без использования дополнительных ресурсов), однако при этом Вы должны обрабатывать событие OnPaint того объекта, на канве которого Вы рисуете. Рассмотрим подробнее свойства и методы объекта Canvas.

  • Свойства Canvas :
    Brush -кисть, является объектом со своим набором свойств:
    Bitmap - картинка размером строго 8x8, используется для заполнения (заливки) области на экране.
    Color - цвет заливки.
    Style - предопределенный стиль заливки; это свойство конкурирует со свойством Bitmap - какое свойство Вы определили последним, то и будет определять вид заливки.
    Handle - данное свойство дает возможность использовать кисть в прямых вызовах процедур Windows API .
    ClipRect - (только чтение) прямоугольник, на котором происходит графический вывод.
    CopyMode - свойство определяет, каким образом будет происходить копирование (метод CopyRect) на данную канву изображения из другого места: один к одному, с инверсией изображения и др.
    Font - шрифт, которым выводится текст (метод TextOut).
    Handle - данное свойство используется для прямых вызовов Windows API.
    Pen - карандаш, определяет вид линий; как и кисть (Brush) является объектом с набором свойств:
    Color - цвет линии
    Handle - для прямых вызовов Windows API
    Mode - режим вывода: простая линия, с инвертированием, с
    выполнением исключающего или и др.

    Style - стиль вывода: линия, пунктир и др.

    Width - ширина линии в точках

    PenPos - текущая позиция карандаша, карандаш рекомендуется перемещать с помощью метода MoveTo, а не прямой установкой данного свойства.

    Pixels - двухмерный массив элементов изображения (pixel), с его помощью Вы получаете доступ к каждой отдельной точке изображения (см. пример к данному уроку).

    Методы Canvas:

    Методы для рисования простейшей графики - Arc, Chord, LineTo, Pie, Polygon, PolyLine, Rectangle, RoundRect. При прорисовке линий в этих методах используются карандаш (Pen) канвы, а для заполнения внутренних областей - кисть (Brush).

    Методы для вывода картинок на канву - Draw и StretchDraw, В качестве параметров указываются прямоугольник и графический объект для вывода (это может быть TBitmap, TIcon или TMetafile). StretchDraw отличается тем, что растягивает или сжимает картинку так, чтобы она заполнила весь указанный прямоугольник (см. пример к данному уроку).

    Методы для вывода текста - TextOut и TextRect. При выводе текста используется шрифт (Font) канвы. При использовании TextRect текст выводится только внутри указанного прямоугольника. Длину и высоту текста можно узнать с помощью функций TextWidth и TextHeight.

    Свойство SQL

  • Свойство SQL - вероятно, самая важная часть TQuery. Доступ к этому свойству происходит либо через Инспектор Объектов во время конструирования проекта (design time), или программно во время выполнения программы (run time).

  • Интересней, конечно, получить доступ к свойству SQL во время выполнения, чтобы динамически изменять запрос. Например, если требуется выполнить три SQL запроса, то не надо размещать три компонента TQuery на форме. Вместо этого можно разместить один и просто изменять свойство SQL три раза. Наиболее эффективный, простой и мощный способ - сделать это через параметризованные запросы, которые будут объяснены в следующей части. Однако, сначала исследуем основные особенности свойства SQL, а потом рассмотрим более сложные темы, типа запросов с параметрами.
    Свойство SQL имеет тип TStrings, который означает что это ряд строк, сохраняемых в списке. Список действует также, как и массив, но, фактически, это специальный класс с собственными уникальными возможностями. В следующих нескольких абзацах будут рассмотрены наиболее часто используемые свойства.
    При программном использовании TQuery, рекомендуется сначала закрыть текущий запрос и очистить список строк в свойстве SQL:
    Query1.Close;
    Query1.SQL.Clear;
    Обратите внимание, что всегда можно “безопасно” вызвать Close. Даже в том случае, если запрос уже закрыт, исключительная ситуация генерироваться не будет.
    Следующий шаг - добавление новых строк в запрос:
    Query1.SQL.Add(‘Select * from Country’);
    Query1.SQL.Add(‘where Name = ’’Argentina’’’);
    Метод Add используется для добавления одной или нескольких строк к запросу SQL. Общий объем ограничен только количеством памяти на вашей машине.
    Чтобы Delphi отработал запрос и возвратил курсор, содержащий результат в виде таблицы, можно вызвать метод:
    Query1.Open;
    Демонстрационная программа THREESQL показывает этот процесс (см Рис.1)
    Свойство SQL

    Рис.1:
    Программа THREESQL показывает, как сделать несколько запросов с помощью единственного объекта TQuery.
    Программа THREESQL использует особенность локального SQL, который позволяет использовать шаблоны поиска без учета регистра (case insensitive). Например, следующий SQL запрос:

    Select * form Country where Name like ’C%’

    возвращает DataSet, содержащий все записи, где поле Name начинается с буквы ‘C’. Следующий запрос позволит увидеть все страны, в названии которых встречается буква ‘C’:

    Select * from Country where Name like ‘%C%’;

    Вот запрос, которое находит все страны, название которых заканчивается на ‘ia’:

    Select * from Country where Name like ‘%ia’;

    Одна из полезных особенностей свойства SQL - это способность читать файлы, содержащие текст запроса непосредственно с диска. Эта особенность показана в программе THREESQL.

    Вот как это работает. В директории с примерами к данному уроку есть файл с расширением SQL. Он содержат текст SQL запроса. Программа THREESQL имеет кнопку с названием Load, которая позволяет Вам выбрать один из этих файлов и выполнять SQL запрос, сохраненный в этом файле.

    Кнопка Load имеет следующий метод для события OnClick:

    procedure TForm1.LoadClick(Sender: TObject);

    begin

    if OpenDialog1.Execute then

    with Query1 do begin

    Close;

    SQL.LoadFromFile(OpenDialog1.FileName);

    Open;

    end;

    end;

    Метод LoadClick сначала загружает компоненту OpenDialog и позволяет пользователю выбрать файл с расширением SQL. Если файл выбран, текущий запрос закрывается, выбраный файл загружается с диска в св-во SQL, запрос выполняется и результат показывается пользователю.

    TButton, исходный текст, заголовки и

    Z-упорядочивание
  • Еще несколько возможностей Инспектора Объектов и Дизайнера Форм.

  • Создайте новый проект. Поместите на форму объект TMemo, а затем TEdit так, чтобы он наполовину перекрывал TMemo, как показано на рис.13. Теперь выберите пункт меню Edit | Send to Back, что приведет к перемещению TEdit вглубь формы, за объект TMemo. Это называется изменением Z-порядка компонент. Буква Z
    используется потому, что обычно математики обозначают третье измерение буквой Z. Так, X и Y
    используются для обозначения ширины и высоты, и Z
    используется для обозначения глубины.
    TButton, исходный текст, заголовки и

    Рис.13: Объект TEdit перекрывается наполовину объектом TMemo.
    Если Вы “потеряли” на форме какой-то объект, то найти его можно в списке Combobox’а, который находится в верхней части Инспектора Объектов.
    Поместите кнопку TButton в нижнюю часть формы. Теперь растяните Инспектор Объектов так, чтобы свойства Name и Caption были видны одновременно на экране. Теперь измените имя кнопки на Terminate. Заметьте, что заголовок (Caption) поменялся в тот же момент. Такое двойное изменение наблюдается только если ранее не изменялось свойство Caption.
    Текст, который Вы видите на поверхности кнопки - это содержимое свойства Caption, свойство Name служит для внутренних ссылок, Вы будете использовать его при написании кода программы. Если Вы откроете сейчас окно Редактора, то увидите следующий фрагмент кода:
    TForm1 = class(TForm)
    Edit1: TEdit;
    Memo1: TMemo;
    Terminate: TButton;
    private
    { Private declarations }
    public
    { Public declarations }
    end;
    В этом фрагменте кнопка TButton называется Terminate из-за того, что Вы присвоили это название свойству Name. Заметьте, что TMemo имеет имя, которое присваивается по умолчанию.
    Перейдите на форму и дважды щелкните мышкой на объект TButton. Вы сразу попадете в окно Редактора, в котором увидите фрагмент кода вроде этого:
    procedure TForm1.TerminateClick(Sender: TObject);
    begin
    end;
    Данный код был создан автоматически и будет выполняться всякий раз, когда во время работы программы пользователь нажмет кнопку Terminate. Вдобавок, Вы можете видеть, что определение класса в начале файла теперь включает ссылку на метод TerminateClick:

    TForm1 = class(TForm)

    Edit1: TEdit;

    Memo1: TMemo;

    Terminate: TButton;

    procedure TerminateClick(Sender: TObject);

    private

    { Private declarations }

    public

    { Public declarations }

    end;

    Потратьте немного времени на усвоение последовательности действий, описанных выше. Изначально Вы смотрите на кнопку на форме. Вы делаете двойной щелчок на эту кнопку, и соответствующий фрагмент кода автоматически заносится в Редактор.

    Теперь самое время написать строчку кода. Это очень простой код, состоящий из одного слова Close:

    procedure TForm1.TerminateClick(Sender: TObject);

    begin

    Close;

    end;

    Когда этот код исполняется, то главная форма (значит и все приложение) закрывается. Для проверки кода запустите программу и нажмите кнопку Terminate. Если все сделано правильно, программа закроется и Вы вернетесь в режим дизайна.

    Прежде, чем перейти к следующему разделу, перейдите в Инспектор Объектов и измените значение свойства Name для кнопки на любое другое, например OK. Нажмите Enter для внесения изменений. Посмотрите в Редактор, Вы увидите, что код, написанный Вами изменился:

    procedure TForm1.OkClick(Sender: TObject);

    begin

    Close;

    end;

    Заметьте, что аналогичные изменения произошли и в определении класса:

    TForm1 = class(TForm)

    Edit1: TEdit;

    Memo1: TMemo;

    Ok: TButton;

    procedure OkClick(Sender: TObject);

    private

    { Private declarations }

    public

    { Public declarations }

    end;

    Тьюторы (интерактивные обучающие программы)

    Delphi предоставляет тьютор, содержащий несколько тем и который можно запустить из пункта меню Help | Interactive Tutors. Тьютор запускается только если среда Delphi имеет все установки по умолчанию. Если конфигурация была изменена, то проще всего сохранить файл DELPHI.INI под другим именем и скопировать файл DELPHI.CBT в DELPHI.INI.
    В первых двух темах дается краткий обзор Delphi и происходит обучение построению простейшего приложения.
    Остальные темы посвящены построению пользовательского интерфейса: размещению объектов на форме, настройке их свойств и написанию обработчиков событий. А также созданию приложений, работающих с базами данных.

    TQuery и Параметры

    Delphi позволяет составить “гибкую” форму запроса, называемую параметризованным запросом. Такие запросы позволяют подставить значение переменной вместо отдельных слов в выражениях “where” или “insert”. Эта переменная может быть изменена практически в любое время. (Если используется локальный SQL, то можно сделать замену почти любого слова в утверждении SQL, но при этом та же самая возможность не поддерживается большинством серверов.)
    Перед тем, как начать использовать параметризованные запросы, рассмотрим снова одно из простых вышеупомянутых предложений SQL:
    Select * from Country where Name like ’C%’
    Можно превратить это утверждение в параметризованный запрос заменив правую часть переменной NameStr:
    select * from County where Name like :NameStr
    В этом предложении SQL, NameStr не является предопределенной константой и может изменяться либо во время дизайна, либо во время выполнения. SQL parser (программа, которая разбирает текст запроса) понимает, что он имеет дело с параметром, а не константой потому, что параметру предшествует двоеточие ":NameStr". Это двоеточие сообщает Delphi о необходимости заменить переменную NameStr некоторой величиной, которая будет известна позже.
    Обратите внимание, слово NameStr было выбрано абсолютно случайно. Использовать можно любое допустимое имя переменной, точно также, как выбирается идентификатор переменной в программе.
    Есть два пути присвоить значение переменной в параметризованном запросе SQL. Один способ состоит в том, чтобы использовать свойство Params объекта TQuery. Второй - использовать свойство DataSource для получения информации из другого DataSet. Вот ключевые свойства для достижения этих целей:
    property Params[Index: Word];
    function ParamByName(const Value: string);
    property DataSource;
    Если подставлять значение параметра в параметризованный запрос через свойство Params, то обычно нужно сделать четыре шага:

  • Закрыть TQuery

  • Подготовить объект TQuery, вызвав метод Prepare

  • Присвоить необходимые значения свойству Params


  • Открыть TQuery


  • Второй шаг выполняется в том случае, если данный текст запроса выполняется впервые, в дальнейшем его можно опустить.

    Вот фрагмент кода, показывающий как это может быть выполнено практически:

    Query1.Close;

    Query1.Prepare;

    Query1.Params[0].AsString := ‘Argentina’;

    Query1.Open;

    Этот код может показаться немного таинственным. Чтобы понять его, требуется внимательный построчный анализ. Проще всего начать с третьей строки, так как свойство Params является “сердцем” этого процесса.

    Params - это индексированное свойство, которое имеет синтаксис как у свойства Fields для TDataSet. Например, можно получить доступ к первой переменной в SQL запросе, адресуя нулевой элемент в массиве Params:

    Params[0].AsString := ‘”Argentina”’;

    Если параметризованный SQL запрос выглядит так:

    select * from Country where Name = :NameStr

    то конечный результат (т.е. то, что выполнится на самом деле) - это следующее предложение SQL:

    select * from Country where Name = “Argentina”

    Все, что произошло, это переменной :NameStr было присвоено значение "Аргентина" через свойство Params. Таким образом, Вы закончили построение простого утверждения SQL.

    Если в запросе содержится более одного параметра, то доступаться к ним можно изменяя индекс у свойства Params

    Params[1].AsString := ‘SomeValue’;

    либо используя доступ по имени параметра

    ParamByName(‘NameStr’).AsString:=’”Argentina”’;

    Итак, параметризованные SQL запросы используют переменные, которые всегда начинаются с двоеточия, определяя места, куда будут переданы значения параметров.

    Прежде, чем использовать переменную Params, сначала можно вызвать Prepare. Этот вызов заставляет Delphi разобрать ваш SQL запрос и подготовить свойство Params так, чтобы оно "было готово принять” соответствующее количество переменных. Можно присвоить значение переменной Params без предварительного вызова Prepare, но это будет работать несколько медленнее.

    После того, как Вы вызывали Prepare, и после того, как присвоили необходимые значения переменной Params, Вы должны вызвать Open, чтобы закончить привязку переменных и получить желаемый DataSet. В нашем случае, DataSet должен включать записи где в поле “Name” стоит “Argentina”.


    Рассмотрим работу с параметрами на примере (программа PARAMS.DPR). Для создания программы, разместите на форме компоненты TQuery, TDataSource, TDBGrid и TTabSet. Соедините компоненты и установите в свойстве TQuery.DatabaseName псевдоним DBDEMOS. См. рис.2

    TQuery и Параметры


    Рис.2 : Программа PARAMS во время дизайна.

    В обработчике события для формы OnCreate напишем код, заполняющий закладки для TTabSet, кроме того, здесь подготавливается запрос:

    procedure TForm1.FormCreate(Sender: TObject);

    var

    i : Byte;

    begin

    Query1.Prepare;

    for i:=0 to 25 do

    TabSet1.Tabs.Add(Chr(Byte('A')+i));

    end;

    Текст SQL запроса в компоненте Query1:

    select * from employee where LastName like :LastNameStr

    Запрос выбирает записи из таблицы EMPLOYEE, в которых поле LastName похоже (like) на значение параметра :LastNameStr. Параметр будет передаваться в момент переключения закладок:

    procedure TForm1.TabSet1Change(Sender: TObject;



















  • NewTab: Integer;




  • var AllowChange: Boolean);


  • begin

    with Query1 do begin

    Close;

    Params[0].AsString:=

    '"'+TabSet1.Tabs.Strings[NewTab]+'%"';

    Open;

    end;

    end;

    TQuery и Параметры


    Рис.3: Программа PARAMS во время выполнения.





  • Указание сетевого протокола при соединении с БД

    В случае с InterBase можно в явном виде указать, какой сетевой протокол используется при соединении с базой данных. Эта установка выполняется либо в утилите конфигурации BDE, либо в программе - нужно изменить параметр “SERVER NAME”, который содержит полный путь к файлу с базой данных.
    Итак:
    Протокол Параметр SERVER NAME
    TCP/IP IB_SERVER:PATH\DATABASE.GDB ( nt:c:\ib\base.gdb ) ( unix:/ib/base.gdb )
    IPX/SPX IB_SERVER:PATH\DATABASE.GDB ( nw@sys:ib\base.gdb )
    NetBEUI \\IB_SERVER\PATH\DATABASE.GDB ( \\nt\c:\ib\base.gdb )

    Управление ReportSmith по DDE

    В прилагаемом примере run-time версия ReportSmith выполняет команду, переданную по DDE. Имена DDE сервиса для ReportSmith и некоторых других приложений можно узнать в Справочнике в среде ReportSmith.
    Перед запуском примера нужно правильно установить в свойстве ServiceApplication путь к run-time версии ReportSmith и в тексте программы в строке
    Cmd:='LoadReport "c:\d\r\video\summary.rpt","@Repvar1=<40>,@Repvar2=<'#39'Smith'#39'>"'#0;
    правильно указать путь к отчету.

    Управление транзакциями в Delphi

    Прежде всего, транзакции в Delphi бывают явные и неявные.
    Явная транзакция - это транзакция, начатая и завершенная с помощью методов объекта DataBase: StartTransaction, Commit, RollBack. После начала явной транзакции, все изменения, вносимые в данные относятся к этой транзакции.
    Другого способа начать явную транзакцию, нежели с использованием DataBase, нет. (Точнее говоря, такая возможность есть, но это потребует обращения к функциям API InterBase. Однако, это уже достаточно низкоуровневое программирование.) Следовательно, в рамках одного соединения нельзя начать две транзакции.
    Неявная транзакция стартует при модификации данных, если в данный момент нет явной транзакции. Неявная транзакция возникает, например, при выполнении метода Post
    для объектов Table и Query. То есть, если Вы отредактировали запись, в DBGrid и переходите на другую запись, то это влечет за собой выполнение Post, что, в свою очередь, приводит к началу неявной транзакции, обновлению данных внутри транзакции и ее завершению. Важно отметить, что неявная транзакция, начатая с помощью методов Post, Delete, Insert, Append и т.д. заканчивается автоматически.
    Для модификации данных может использоваться и PassThrough SQL - SQL-выражение, выполняемое с помощью метода ExecSQL класса TQuery. Выполнение модификации через PassThrough SQL также приводит к старту неявной транзакции. Дальнейшее поведение транзакции, начатой таким путем, определяется значением параметра SQLPASSTHRU MODE для псевдонима базы данных (или тот-же параметр в св-ве Params объекта DataBase). Этот параметр может принимать три значения:



  • SHARED AUTOCOMMIT - слово SHARED указывает на то, что оба вида транзакций(через Passthrough SQL и через методы TTable и TQuery) разделяют одно и то же соединение к базе данных. Слово AUTOCOMMIT указывает на то, что неявная транзакция, начатая через Passthrough SQL, завершается после выполнения действия по модификации данных (автоматически выполняется COMMIT).


  • SHARED NOAUTOCOMMIT - отличается от предыдущего тем, что неявная транзакция, начатая через Passthrough SQL, не завершается после выполнения, ее нужно явно завершить, выполнив SQL-выражение “COMMIT”.




  • NOT SHARED - транзакции разных типов работают через разные соединения с базой. Данное значение параметра подразумевает также NOAUTOCOMMIT. То есть все неявные PassthroughSQL-транзакции нужно завершать явно - выполняя SQL-выражение “COMMIT” для Passtrough SQL.


  • Рассмотрим возможные сценарии поведения транзакций при разных значениях параметра.

    В первом случае, если нет в данный момент начатой транзакции, то попытка модификация данных методами TTable или TQuery, как и выполнение через Passtrough SQL какой-либо операции приведет к старту неявной транзакции. После выполнения, такая транзакция будет автоматически завершена (если не возникло ошибки по ходу транзакции). Если уже имеется начатая явно (метод StartTransaction объекта DataBase) транзакция, то изменения будут проходить в ее контексте. Все транзакции используют одно и то-же соединение.

    Во втором случае все происходит, как в первом. Отличие в том, что неявная PassthroughSQL-транзакция не завершается, пока не будет выполнена команда “COMMIT”.

    В третьем случае, при выполнении команды Passthrough SQL, будет установлено еще одно соединение, начата неявная транзакция и выполнены действия по модификации данных. Транзакция не будет завершена, пока не будет выполнена команда “COMMIT”. Наличие транзакции, начатой явно с помощью DataBase никак не отразится на ходе выполнения PassthroughSQL-транзакции. Пока PassthroughSQL-транзакция не завершится, изменения, внесенные ей, не будут видны в объектах Table и Query, работающих через другое соединение. PassthroughSQL-транзакции можно рассматривать в некотором смысле, как транзакции из другого приложения.

    Взаимодействие транзакций данной программы с транзакциями из других приложений определяется свойством TransIsolation объекта DataBase. Для InterBase имеет смысл два значения: tiReadCommitted и tiRepeatableRead. Выполнение метода StartTransaction в этих двух случаях равносильно выполнению SQL-выражений, соответственно:

    SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL READ COMMITTED

    и

    SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

    Установка Редактора свойств

    После того, как модуль с новым редактором свойств подготовлен, его нужно подключить к среде Delphi. Установка Редактора Свойств абсолютно аналогична установке новых объектов в палитру компонент и происходит следующим образом:


  • выберите пункт меню "Options|Install Components...."


  • нажмите кнопку “Add”


  • укажите имя подключаемого модуля (или воспользуйтесь кнопкой “Browse”)


  • нажмите “OK” и еще раз “OK”

  • После успешной перекомпиляции библиотеки проверьте, как действует новый редактор свойств. Для этого создайте новый проект, положите на форму какой-либо видимый объект, например TButton, установите ShowHint для него в True, вызовите редактор подсказки (кнопка ‘…’ в свойстве Hint), редактор выглядит примерно так:
    Установка Редактора свойств

    В диалоге нажмите “OK” и запустите программу.
    Полный текст модуля с Редактором Свойств см. в примерах к данному уроку.




  • Выбор предка

    Прежде, чем приступить к написанию кода, нужно определиться, хотя бы приблизительно, что за компонент вы собираетесь делать. Далее, исходя из его предполагаемых свойств, определите класс-предок. В VCL имеется несколько базовых классов, рекомендуемых для наследования:


  • TObject - Можно использовать в качестве предка, если с этим компонентом не нужно работать во время дизайна. Это может быть, например, класс, содержащий значения переменных среды (environment) или класс для работы с INI файлами.


  • TComponent - Отправная точка для многих невидимых компонент. Данный класс обладает встроенной возможностью сохранять/считывать себя в потоке во время дизайна.


  • TGraphicControl - Используйте этот класс для создания видимых компонент, которым не нужен handle. Такие компоненты рисуют прямо на своей поверхности и требуют мало ресурсов Windows.


  • TWinControl - Базовый класс для компонент, которые имеют окно. Данное окно имеет свой handle, его используют при доступе к возможностям Windows через API.


  • TCustomControl - Потомок TWinControl, вводит понятие канвы (Canvas) и метод Paint() для лучшего контроля за прорисовкой компонента. Именно этот класс используется в качестве базового для построения большинства видимых компонент, имеющих оконный handle.


  • TXxxxx - Класс вроде TEdit или TButton. Используются с целью доопределения их свойств и методов или переопределения значения свойств, принимаемых по умолчанию.





  • Выполнение соединения нескольких таблиц.

    Вы видели что таблицы CUSTOMERS и ORDERS связаны в отношении один-ко-многим, основанному на поле CustNo. Таблицы ORDERS и ITEMS также связаны отношении один-ко-многим, только через поле OrderNo.
    Более конкретно, каждый заказ который существует в таблице ORDERS будет иметь несколько записей в таблице ITEMS, связанных с этим заказом. Записи из таблицы ITEMS определяют тип и количество изделий, связанных с этим заказом.
    Пример.
    Некто Иванов Ф.П. 1 мая 1995г. заказал следующее:

  • Гайка 4х-угольная - 50 штук

  • Вентиль - 1 штука

  • А некто Сидорчук Ю.Г. 8 декабря 1994г. заказал:

  • М/схема КР580 ИК80 - 10 штук

  • Транзистор КТ315 - 15 штук

  • Моток провода - 1 штука

  • В ситуации подобной этой, иногда проще всего "соединить" данные из таблиц ORDERS и ITEMS так, чтобы результирующий DataSet содержал информацию из обеих таблиц:
    Иванов Ф.П. 1 мая 1995г Гайка 4х-угольная 50 штук
    Иванов Ф.П. 1 мая 1995г Вентиль 1 штука
    Сидорчук Ю.Г. 8 декабря 1994г М/схема КР580 ИК80 10 штук
    Сидорчук Ю.Г. 8 декабря 1994г Транзистор КТ315 15 штук
    Сидорчук Ю.Г. 8 декабря 1994г Моток провода 1 штука
    Слияние этих двух таблиц называется "соединение" и это одно из фундаментальных действий, которые Вы можете выполнить на наборе двух или больше таблиц.
    Взяв таблицы ORDERS и ITEMS из подкаталога DEMOS\DATA, их можно соединить их таким путем, что поля CustNo, OrderNo и SaleDate из таблицы ORDERS будут “слиты” с полями PartNo и Qty из таблицы ITEMS и сформируют новый DataSet, содержащий все пять полей. Grid содержащий результирующий DataSet показан на рис.5
    Выполнение соединения нескольких таблиц.

    Рис.5: Соединение таблиц ORDERS и ITEMS может быть сделано так, что формируется новый DataSet содержащий поля из каждой таблицы.
    Имеется существенное различие между связанными курсорами и соединенными таблицами. Однако они имеют две общие черты:

  • И те, и другие используют две или более таблиц

  • Каждый таблица связана с другой по одному или более одинаковых полей.

  • Соединение таблиц ORDERS и ITEMS может быть выполнено единственным SQL запросом, который выглядит так:

    select

    O.CustNo, O.OrderNo, O.SaleDate, I.PartNo, I.Qty

    from Orders O, Items I

    where O.OrderNo = I.OrderNo

    Этот запрос состоит из четырех различных частей:




  • Выражение Select определяет, что Вы хотите получить - курсор, содержащий некоторую форму DataSet.




  • Затем идет список полей которые Вы хотите включить в dataset. Этот список включает поля CustNo, OrderNo, SaleDate, PartNo и Qty. Первые три поля из таблицы ORDERS, а два других - из таблицы ITEMS.




  • Выражение from объявляет, что Вы работаете с двумя таблицами, одна называется ORDERS, а другая ITEMS. Для краткости, в запросе используется особенность SQL, которая позволяет Вам ссылаться на таблицу ORDERS буквой O, а на таблицу ITEMS буквой I.




  • Выражение where жизненно важно потому, что оно определяет поля связи для двух таблиц. Некоторые серверы могут вернуть DataSet, даже если Вы не включите выражение where в запрос, но почти всегда результирующий набор записей будет не тем, что Вы хотели видеть. Чтобы получить нужный результат, убедитесь что Вы включили выражение where.






  • Вывод содержимого формы на печать

  • Иногда в программе требуется просто получить твердую копию экранной формы. В Delphi это делается более, чем просто - у объекта TForm есть метод Print, который и нужно вызвать в нужный момент.


  • Вызов исключительной ситуации

  • В процедуре C из примера мы уже могли видеть, как должна поступать программа при обнаружении состояния ошибки - она вызывает исключительную ситуацию:

  • raise ESampleError.Create(‘Error!’);
    После ключевого слова raise
    следует код, аналогичный тому, что используется для создания нового экземпляра класса. Действительно, в момент вызова исключительной ситуации создается экземпляр указанного класса; данный экземпляр существует до момента окончания обработки исключительной ситуации и затем автоматически уничтожается. Вся информация, которую нужно сообщить в обработчик ошибки передается в объект через его конструктор в момент создания.
    Почти все существующие классы исключительных ситуаций являются наследниками базового класса Exception и не содержат новых свойств или методов. Класс Exception имеет несколько конструкторов, какой из них конкретно использовать - зависит от задачи. Описание класса Exception можно найти в on-line Help.

    Заготовка для нового компонента

  • В среде Delphi есть специальный эксперт, создающий заготовку для нового компонента. Вызвать его можно в пункте меню File|New Component… (см рис.2)

  • Заготовка для нового компонента

    Рис.2: Эксперт для создания нового компонента
    В диалоге нужно указать имя нового класса (например, TMyButton), предка класса (TButton) и страницу палитры, куда поместить новый компонент (Samples). Если нажать “OK”, то эксперт создаст модуль - заготовку для нового компонента:
    unit Unit1;
    interface
    uses
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
    Forms, Dialogs, StdCtrls;
    type
    TMyButton = class(TButton)
    private
    { Private declarations }
    protected
    { Protected declarations }
    public
    { Public declarations }
    published
    { Published declarations }
    end;
    procedure Register;
    implementation
    procedure Register;
    begin
    RegisterComponents('Samples', [TMyButton]);
    end;
    end.
    Модуль содержит декларацию нового класса и процедуру его регистрации в Палитре Компонент. В процедуре RegisterComponents первый параметр - имя страницы (можно указать свое имя - появится новая страница); второй параметр - множество объектов для регистрации.
    Теперь модуль нужно сохранить под новым именем (например, NEW_BTN.PAS) и приступить к дописыванию новых свойств и методов. После того, как эта работа закончена и новый компонент отлажен, можно добавить его в Палитру (см. предыдущую главу). Но перед этим желательно создать файл ресурсов, в котором будет лежать пиктограмма для представления данного объекта в Палитре Компонент. Файл ресурсов можно создать с помощью программы Resource Workshop, называться он должен точно так же, как модуль регистрации компонента и иметь расширение .DCR (т.е., если объект регистрируется в модуле NEW_BTN.PAS, то тогда имя файла ресурсов будет NEW_BTN.DCR). В файле ресурсов должен находиться ресурс типа BITMAP - картинка размером 28x28 точки (можно меньше), название картинки должно совпадать с именем класса (в нашем случае TMYBUTTON).

    Закладки (Bookmarks)

  • Часто бывает полезно отметить текущее местоположение в таблице так, чтобы можно было быстро возвратиться к этому месту в дальнейшем. Delphi обеспечивает эту функциональную возможность посредством трех методов, которые используют понятие закладки.

  • function GetBookmark: TBookmark;
    (устанавливает закладку в таблице)
    procedure GotoBookmark(Bookmark: TBookmark);
    (переходит на закладку)
    procedure FreeBookmark(Bookmark: TBookmark);
    (освобождает память)
    Как Вы можете видеть, вызов GetBookmark
    возвращает переменную типа TBookmark. TBookmark содержит достаточное количество информации, чтобы Delphi мог найти местоположение к которому относится этот TBookmark. Поэтому Вы можете просто передавать этот TBookmark функции GotoBookmark, и будете немедленно возвращены к местоположению, связанному с этой закладкой.
    Обратите внимание, что вызов GetBookmark
    распределяет память для TBookmark, так что Вы должны вызывать FreeBookmark до окончания вашей программы, и перед каждой попыткой повторного использования Tbookmark (в GetBookMark).


  • Данный урок должен был дать

    Данный урок должен был дать вам достаточно информации для того, чтобы начать исследование того, как Вы можете использовать систему обработки исключительных ситуаций в вашей программе. Вы, конечно, можете обрабатывать ошибки и без привлечения этой системы; но с ней Вы получите лучшие результаты с меньшими усилиями.


    Итак, мы познакомились с еще одним способом создания таблиц - способом, использующим метод CreateTable класса TTable. Использование данного способа придаст Вашему приложению максимальную гибкость, и Вы сможете строить локальные таблицы “на лету”. Сопутствующим методом является метод AddIndex класса TTable, позволяющий создавать индексы для уже существующей таблицы. Подчеркнем еще раз, что данный способ применим только для локальных таблиц. Более общий способ состоит в использовании SQL-запросов, который мы рассматривали на уроке 12.

    Запуск транзакции

    Выполнять транзакции можно, например, из Windows Interactive SQL, из программы, из сохраненной процедуры или триггера. В общем виде, синтаксис команды SQL для запуска транзакции:
    SET TRANSACTION [Access mode] [Lock Resolution]
    [Isolation Level] [Table Reservation]
    Значения, принимаемые по-умолчанию:
    выражение
    SET TRANSACTION
    равносильно выражению
    SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT
    Access Mode - определяет тип доступа к данным. Может принимать два значения:



  • READ ONLY - указывает, что транзакция может только читать данные и не может модифицировать их.


  • READ WRITE - указывает, что транзакция может читать и модифицировать данные. Это значение принимается по умолчанию.

  • Пример:
    SET TRANSACTION READ WRITE
    Isolation Level - определяет порядок взаимодействия данной транзакции с другими в данной базе. Может принимать значения:



  • SNAPSHOT - значение по умолчанию. Внутри транзакции будут доступны данные в том состоянии, в котором они находились на момент начала транзакции. Если по ходу дела в базе данных появились изменения, внесенные другими завершенными транзакциями, то данная транзакция их не увидит. При попытке модифицировать такие записи возникнет сообщение о конфликте.


  • SNAPSHOT TABLE STABILITY - предоставляет транзакции исключительный доступ к таблицам, которые она использует. Другие транзакции смогут только читать данные из них.


  • READ COMMITTED - позволяет транзакции видеть текущее состояние базы.

  • Конфликты, связанные с блокировкой записей происходят в двух случаях:


  • Транзакция пытается модифицировать запись, которая была изменена или удалена уже после ее старта. Транзакция типа READ COMMITTED может вносить изменения в записи, модифицированные другими транзакциями после их завершения.


  • Транзакция пытается модифицировать таблицу, которая заблокирована другой транзакцией типа SNAPSHOT TABLE STABILITY.

  • Lock Resolution - определяет ход событий при обнаружении конфликта блокировки. Может принимать два значения:






  • WAIT - значение по умолчанию. Ожидает разблокировки требуемой записи. После этого пытается продолжить работу.




  • NO WAIT - немедленно возвращает ошибку блокировки записи.


  • Table Reservation - позволяет транзакции получить гарантированный доступ необходимого уровня к указанным таблицам. Существует четыре уровня доступа:







  • PROTECTED READ - запрещает обновление таблицы другими транзакциями, но позволяет им выбирать данные из таблицы.




  • PROTECTED WRITE - запрещает обновление таблицы другими транзакциями, читать данные из таблицы могут только транзакции типа SNAPSHOT или READ COMMITTED.




  • SHARED READ - самый либеральный уровень. Читать могут все, модифицировать - транзакции READ WRITE.




  • SHARED WRITE - транзакции SNAPSHOT или READ COMMITTED READ WRITE могут модифицировать таблицу, остальные - только выбирать данные.






  • Завершение транзакции

    Когда все действия, составляющие транзакцию успешно выполнены или возникла ошибка, транзакция должна быть завершена, для того, чтобы база данных находилась в непротиворечивом состоянии. Для этого есть два SQL-выражения:



  • COMMIT - сохраняет внесенные транзакцией изменения в базу данных. Это означает, что транзакция завершена успешно.


  • ROLLBACK - откат транзакции. Транзакция завершается и никаких изменений в базу данных не вносится. Данная операция выполняется при возникновении ошибки при выполнении операции (например, при невозможности обновить запись).





  • 

        Программирование: Языки - Технологии - Разработка




    Класс компоненты Имя свойства Для каких свойств
    Nil ‘’ совпадает тип свойства
    Nil ‘Name’ Тип свойства + Имя свойства
    TClass ‘’ Тип свойства + класс компоненты
    TClass ‘Name’ Тип свойства + Имя свойства+ класс компоненты