Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Компоненты и классы
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 11.10.2024, 16:38
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 178
Версия Delphi: Delphi7
Репутация: 10
По умолчанию Использование методов объектов, находящихся внутри класса

Приветствую вас, форумчане !

Прошу помощи в вопросе работы с классами. Длительное время я писал код, не создавая свои классы, что в итоге выливалось в кучу лишней работы. И сейчас решил наконец попробовать, провёл первые простейшие эксперименты.
Замысел такой: определить некие "мега-объекты" - в моём случае это дерево классификации, связанная с деревом таблица БД, и куча управляющих кнопок и прочих контролов. Для этих "мега-объектов" прописать логику работы, и затем создавать экземпляры класса "мега-объект", и связывать их с соответствующими контролами на форме, созданными в дизайн-тайме. Простейший эксперимент показал, что это работает. Однако далее я столкнулся с вопросом использования методов объектов, находящихся внутри класса.

Для пробы в моём классе были определены дерево и кнопка Expand/Collapse:

Код:
type
  TMegaObject = class
  public
    uTree: TTreeView;
    aExpandCollapseButton: TSpeedButton;

    procedure aExpandCollapseButtonClick(Sender: TObject);
end;

.... (код обработчика aExpandCollapseButtonClick).
Затем создавались переменные - экземпляры этого класса и инициализировались их поля (или объекты внутри класса? – не знаю, как правильнее тут сказать):
Код:
procedure TForm1.FormCreate(Sender: TObject);
begin
  mObj1:= TMegaObject.Create;
  mObj1.uTree := tree;
  mObj1.aExpandCollapseButton := SpeedButton3;
  mObj1.aExpandCollapseButton.OnClick := aTree.aExpandCollapseButtonClick;

  mObj2:= TMegaObject.Create;
......
end;
(контролы "дерево" и "кнопка" создавались в дизайн-тайме).

Нажатия / отжатия кнопки Expand/Collapse отрабатывались правильно, для любого количества экземпляров данного пользовательского класса.

Но вот возникла необходимость обрабатывать событие OnChange дерева uTree, объявленного в классе. Причём так, чтобы получать все параметры, которые объект TreeView передаёт в тело процедуры. Как это сделать – я не понял, и ответа в Инете нигде не нашёл.
В рамках "метода тыка" я объявил в своём классе, в секции public, процедуру с теми же параметрами, что и у OnChange стандартного TreeView:
Код:
procedure uTreeChange(Sender: TObject; Node: TTreeNode);
В реализации этой процедуры прописал просто showmessage(''); - чтобы посмотреть, вызывается ли эта процедура.
Затем в конструкторе своего класса прописал:
Код:
constructor TMegaObject.Create;
begin
  inherited;
  uTree.OnChange := uTreeChange;
end;
что вызвало исключение при запуске тестовой программы
Пробовал (понимая, что бредовое присвоение) в FormCreate написать:

Код:
mObj1.uTree.OnChange := mObj1.uTreeChange;

Тут уже компилятор не пропустил такую строку. А без связи процедур uTreeChange с uTree.OnChange ничего не будет происходить - объявленная процедура висит в воздухе, ни с чем не связанная. И объявить процедуру uTree.OnChange, чтобы написать для неё обработчик, тоже никак не получилось.

Уже кучу вариантов перебрал, и Self.uTree.OnChange пытался как-то прилепить – ничего не получается. Нутром чую – тут должно быть просто, но как? Что удивительно – нигде в просмотренных мною инетовских статьях этот вопрос даже не поднимается. Понимаю, что мои потуги со стороны выглядят идиотизмом, но я действительно впервые пробую работать с собственными классами. Поэтому прошу подсказки по данному вопросу.
Ответить с цитированием
  #2  
Старый Вчера, 06:34
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,050
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

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

Вот так все работает:

Объект (упрощеный, только для демонстрации):
Код:
unit Unit2;

interface

uses
  Vcl.ComCtrls, Vcl.Dialogs;

type
  TMyObject = class
  private
    FTree : TTreeView;
    procedure SetTree(const Value: TTreeView);
  protected
    procedure OnTreeViewChange(Sender : Tobject; Node : TTreeNode);
  public
    constructor Create;

    property Tree : TTreeView read FTree write SetTree;
  end;

implementation

{ TMyObject }

constructor TMyObject.Create;
begin
  FTree := Nil;
end;

procedure TMyObject.OnTreeViewChange(Sender: Tobject; Node: TTreeNode);
begin
  ShowMessage('On tree change.');
end;

procedure TMyObject.SetTree(const Value: TTreeView);
begin
  FTree := Value;
  If Assigned(FTree) Then
    Begin
      FTree.OnChange := OnTreeViewChange;
   End;
end;

end.

Форма (на форме только TreeView1 с созданными итемами для отладки):
Код:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Unit2, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FMyObj : TMyObject;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMyObj := TMyObject.Create;
  FMyObj.Tree := TreeView1;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FMyObj.Free;
end;

end.

Компилируется, ничего не падает, сообщение появляется.
Собственно, что сделано по сути.
Как и у тебя, есть аттрибут для ссылки на дерево (по традиции его имя начинается с 'F' (от Field). Вот только он является внутренним аттрибутом объекта и снаружи не виден (про дружественные классы мы тут не говорим, они вообще концепцию ООП нарушают ). Для доступа к этому полю созданно свойство Tree. У этого свойства есть геттер (read) и сеттер (write). В качестве геттера просто указано имя поля - нам не нужно ничего делать когда мы читаем поле, хотя там тоже можно добавить логику. А вот в качесве сеттера указан специальный метод класса (кстати, обрати внимание, что этот метод по умолчанию попадает в секцию private, иногда, если в наследниках предполагается его перекрытие, то его надо перенести хотя бы в секцию protected; тут читай теорию ООП какие секции наследуются), который не только присвоит принятое значение полю, но и настроит все обработчики событий (в данном случае только OnChange).

Последний раз редактировалось lmikle, Вчера в 06:44.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Guaho (Вчера)
  #3  
Старый Вчера, 13:48
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 178
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Оооо! Огромное спасибо!!! Это то, что нужно! Понятный пример с лаконичным объяснением! Суть понял, опробовал в своём прикидочном коде, ещё добавил объектов в класс по тем же принципам - работает замечательно! И сам код очень изящен, вообще у этого "классового" подхода своя чёткая логика построения, хотя и кажущаяся с непривычки несколько замысловатой )))
Очень странно, но я действительно нигде не нашёл даже упоминания о данном вопросе. А вопрос важнейший!
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 01:24.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter