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

В первой статье, посвященной работе с LiveBindings были рассмотрены простенькие примеры того как и где может использоваться связывание любых данных с визуальными компонентами на форме. Собственно всё, что от нас требовалось — правильно составить выражение для обеспечения связи и «виртуозно» им воспользоваться =). Думаю, что для первого знакомства с механизмом LiveBindings подобных примеров было достаточно.

Сегодня я решил сделать ещё один небольшой пробный шаг в использовании LiveBindings и сделать связь свойств своего объекта с компонентами на форме. Посмотрим как это можно реализовать, а заодно и напишем пару выражений для связи самостоятельно безо всяких редакторов.

Представленный ниже код может не работать в версиях Delphi старше XE2 в связи с изменениями в работе механизма LiveBindings

Прежде, чем приступим непосредственно к работе над тестовой программой, рассмотрим подробнее решаемую задачу. Итак, есть модуль в котором описан класс, скажем вот такой:

ype
  TBlogInfo = class
  private
    fName: string;
    fURL: string;
    fSubscribers: integer;
    procedure SetName(const Value: string);
    procedure SetSubscribers(const Value: integer);
    procedure SetURL(const Value: string);
  public
    constructor Create;
    destructor Destroy; override;
    property Name: string read FName write SetName;
    property URL: string read FURL write SetURL;
    property Subscribers: integer read FSubscribers write SetSubscribers;
end;

Как видите — это не компонент и на форму его не уложить. По ходу работы программы, например, в момент её запуска создается объект:

[...]
MyBlog:=TBlogInfo;
[...]

Каким образом можно связать свойства этого объекта с визуальными компонентами на форме? Для того, чтобы определить связи в design-time нам нужны именно наследники от TComponent, а наш класс — от TObject и связь вроде бы не создать.

Но на деле способ связывания есть. И причём воспользоваться им можно как в run-time так и в design-time. Называется этот способ — использование компонента TBindScope. На вкладке LiveBindings палитры компонентов вы можете найти два компонента — TBindscope и TBindScopeDB. Второй используется для создания связей между записями базы данных и визуальными компонентами. Нас же сегодня интересует более простая реализация компонента — TBindScope.

В числе прочих, у компонента TBindScope имеется следующее замечательное свойство:

property DataObject: TObject read FDataObject write SetDataObject;

Записав в него наш объект мы получим как раз возможность обеспечивать связи между свойствами нашего объекта и компонентами на форме. И вот теперь, определившись с вводной, начнем писать нашу программку. Открываем Delphi XE2, создаем новое VCL-приложение и укладываем на форму следующие компоненты:

  • BindingsList — в нем будем хранить наши выражения для связей
  • BindScope — этот компонент будет держать в свойстве DataObject экземпляр нашего класса
  • 3 Edit’а для ввода/вывода информации
  • 2 Button’а — для загрузки/сохранения данных
  • 3 label’а
Должно получиться примерно следующее:
Теперь напишем необходимые методы для нашего класса. Пусть наш класс будет таким:
type
  TBlogInfo = class
  private
    fName: string;
    fURL: string;
    fSubscribers: integer;
    procedure SetName(const Value: string);
    procedure SetSubscribers(const Value: integer);
    procedure SetURL(const Value: string);
  public
    constructor Create;
    destructor Destroy; override;
    property Name: string read FName write SetName;
    property URL: string read FURL write SetURL;
    property Subscribers: integer read FSubscribers write SetSubscribers;
end;
 
implementation
 
{$R *.dfm}
 
{ TBlogInfo }
 
constructor TBlogInfo.Create;
begin
  inherited Create;
  fName:='WebDelphi';
  fURL:='http://wwww.webdelphi.ru';
  fSubscribers:=355;
end;
 
destructor TBlogInfo.Destroy;
begin
  inherited;
end;
 
procedure TBlogInfo.SetName(const Value: string);
begin
  FName := Value;
  ShowMessage('Новое название: '+Value);
end;
 
procedure TBlogInfo.SetSubscribers(const Value: integer);
begin
  FSubscribers := Value;
  ShowMessage('Новое количество подписчиков: '+IntToStr(Value));
end;
 
procedure TBlogInfo.SetURL(const Value: string);
begin
  FURL := Value;
  ShowMessage('Новый URL: '+Value);
end;

Теперь переходим к главному — обеспечиваем связь между нашим классом и компонентами на форме. Как я уже сказал выше, сделать мы это можем как в design-, так и в run-time. Вначале рассмотрим второй вариант и создадим выражение связи для одного из свойств класса, например для Name. Пишем такую процедуру:

procedure TForm13.CreateExpression(AControlComponent: TComponent;
  AControlProperty, ASourceProperty: string; ABindScope: TBindScope;
  ABindingsList: TBindingsList);
var Expression: TBindExpression;
begin
  {создаем выражение}
  Expression := TBindExpression.Create(self);
  {назначаем компонент для отображения информации}
  Expression.ControlComponent := AControlComponent;
  {Указываем в какое свойство выводить данные}
  Expression.ControlExpression := AControlProperty;
  {указываем точку доступа - тут должен содержаться наш класс}
  Expression.SourceComponent := ABindScope;
  {Тут указываем свойство КЛАССА, которое будет выводится в AControlProperty}
  Expression.SourceExpression := ASourceProperty;
  {Обеспечиваем двухстороннюю связь - будем и читать и писать свойство}
  Expression.Direction := TExpressionDirection.dirBidirectional;
  {Назначаем список}
  expression.BindingsList := ABindingsList;
end;

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

  CreateExpression(edName,'Text','Name',BindScope1,BindingsList1);

Так мы создаем связь между свойством Name нашего объекта и свойством Text у Edit’а. Теперь напишем ещё одну вспомогательную процедурку, которая будет заниматься тем, что будет записывать в BindScope наш объект и создавать необходимые связи:

procedure TForm13.InitializeBinding(ABlogInfo: TBlogInfo);
begin
  {сначала создаем необходимые выражения}
  CreateExpression(edName,'Text','Name',BindScope1,BindingsList1);
  CreateExpression(edAddress,'Text','URL',BindScope1,BindingsList1);
  CreateExpression(edSubscribers,'Text','Subscribers',BindScope1,BindingsList1);
  {потом пишем свойства}
  BindScope1.DataObject:=ABlogInfo;
end;

Теперь сделаем так, чтобы при запуске программы на форму выводились дефолтные значения свойств объекта:

procedure TForm13.FormCreate(Sender: TObject);
begin
 BlogInfo:=TBlogInfo.Create;
 InitializeBinding(BlogInfo);
end;

Теперь, если вы запустите программу, то в Edit’ы выпишутся значения свойств, которые были присвоены в момент создания объекта — прямая связь обеспечена. Следующий шаг — обеспечение обратной связи, т.е. сделать так, чтобы информация из Edit’ов записывалась в свойства. Пишем следующий обработчик кнопки «Сохранить»:

procedure TForm13.Button2Click(Sender: TObject);
var F: TIniFile;
begin
  {оповещаем об изменениях}
  BindingsList1.Notify(edName,'');
  BindingsList1.Notify(edAddress,'');
  BindingsList1.Notify(edSubscribers,'');
  {дли примера скидываем новые значения в INI-файлик}
  F:=TIniFile.Create(ExtractFilePath(Application.ExeName)+'Object.ini');
  try
    F.WriteString('Object','Name',BlogInfo.Name);
    F.WriteString('Object','URL',BlogInfo.URL);
    F.WriteInteger('Object','Subscribers',BlogInfo.Subscribers);
  finally
    F.Free
  end;
end;

Как видите, здесь нет ни одного явного присваивания значений свойствам класса, типа:

BlogInfo.Name:='Any name';

Такие операции будут обеспечены механизмом LiveBindings после соответствующего оповещения. Ну и на кнопке «Загрузка» будет такой код:

procedure TForm13.Button1Click(Sender: TObject);
begin
  BindScope1.DataObject := BlogInfo;
end;

После окончания работы не забываем убраться:

procedure TForm13.FormDestroy(Sender: TObject);
begin
 BlogInfo.Free;
end;

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

Что же касается указания связей в design-time, то достаточно посмотреть на процедуру CreateExpression, чтобы, воспользовавшись редактором выражений у BindingsList написать те же самые выражения.

И, осталось разобрать ещё один момент, касающийся сегодняшней темы — что произойдет, если мы вовремя не создадим объект?. Давайте проверим. Комментируем следующую строку:

procedure TForm13.FormCreate(Sender: TObject);
begin
 //BlogInfo:=TBlogInfo.Create; - типо забыли создать объект
 InitializeBinding(BlogInfo);
end;

Убираем всё, что касается записи в INI-файл, т.к. там 100% будет AV. Запускаем программу и наблюдаем, что никаких исключений нет ни при загрузке, ни при нажатии кнопок — просто Edit’ы не получают данных из объекта, который в данный момент у нас равен nil. Вот такой интересный механизм LiveBindings. Ну, а почему не возникло исключений мы рассмотрим в следующий раз.

Скачать исходник: Исходники —> Прочие
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
10 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Andrey Durow
Andrey Durow
30/09/2011 14:31

Хорошая заметка, спасибо! А есть ли возможность сделать биндинг списка объектов? Например к обычному Grid

Artem
Artem
26/10/2011 05:04

замечательная заметка :) webdelphi за сегодня открыл мне глаза на xe2, потому что первое впечатление было совсем грустное. про liveBindings как раз думал на днях.. наткнулся на реализацию под Android и думал было «может сделать под D7…», а тут такое :) спасибо!
 

Delphist
Delphist
14/11/2011 14:40

Очень интересует биндинг списка объектов, например через Grid или TreeView

trackback

[…] в двух постах: "Delphi XE2. Знакомство с LiveBinding" и "Delphi XE2. LiveBindings для объектов", так что повторяться не будем по поводу того, что […]

Эльдар
Эльдар
13/03/2012 12:22

Немного не понял обратную связь, а точнее не вижу никакой реакции на 
BindScope1.DataObject := BlogInfo;
по нажатию кнопки «Загрузить» — что должно произойти визуально? (программного все понятно)

Fess
Fess
28/02/2014 22:08
Ответить на  Эльдар

«Немного не понял обратную связь, а точнее не вижу никакой реакции на
BindScope1.DataObject := BlogInfo;
по нажатию кнопки «Загрузить» — что должно произойти визуально? (программного все понятно)»

Данные из пропертей BlogInfo должны перелиться в проперти соответ. компонентов на форме.
(класс TBindScope — это компонент адаптер для TBlogInfo)

trackback

[…] произвольных объектов (как это делалось в Delphi XE2 см. эту статью) и эта связь отображалась/редактировалась в […]

Сергей Плахов
Сергей Плахов
01/02/2021 18:17

Просто пляска святого вита. Столько писанины c сомнительным эффектом. Не легче ли прямые связи прописывать?