проблема подклассов TTreeNode в Delphi - PullRequest
2 голосов
/ 03 декабря 2009

Я пишу приложение Delphi 2009, которое использует TTreeView на панели стыковки.

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

TInfoTreeNode=class(TTreeNode)
private
  // remember some stuff
public
end;

procedure TfraInfoTree.tvInfoCreateNodeClass(Sender: TCustomTreeView;
  var NodeClass: TTreeNodeClass);
begin
  NodeClass:=TInfoTreeNode;
end;

Я думаю, что я ударил стену, хотя ... каждый экземпляр "TInfoTreeNode" должен помнить о себе. поскольку дескрипторы освобождаются при автоматическом скрытии панели, содержащей TTreeView, классы уничтожаются.

это проблема, потому что тогда все, что знали классы, затем забывается.

Есть ли способ обойти это (кроме повторной загрузки каждого TInfoTreeNode из базы данных)?

спасибо!

Ответы [ 4 ]

3 голосов
/ 03 декабря 2009

IIRC, свойство Tag Data в каждом экземпляре TTreeNode сохраняется через перестройку дескриптора.

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

0 голосов
/ 03 декабря 2009

спасибо всем за ваши ответы!

Я уже 10 лет использую древовидное представление, используя свойство данных TTreeNode. я хотел быть свободным от:

  • установка свойства данных
  • создание / уничтожение объекта «данные» таким образом, чтобы не было утечек памяти

В прошлом я также использовал свойство Data для идентификационного номера.

сегодня мои узлы имеют GUID для поиска своих данных в базе данных, чтобы они больше не "вписывались" в свойство Data.

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

  • обрабатывать событие TTreeView.OnCreateNodeClass
  • обрабатывает событие TTreeView.OnDeletion для получения последних данных с узлов до их уничтожения
  • обрабатывает событие TTreeView.OnAddition для: 1) ведения простого списка узлов 2) установки свойства Data узла, чтобы мы могли использовать его для поиска места в списке, выделенном для хранения его данных.

вот код:

  TInfoTreeNodeMemory=record
    ...
  end;

  TInfoTreeNode=class(TTreeNode)
  private
    m_rInfoTreeNodeMemory:TInfoTreeNodeMemory;
  public
    property InfoTreeNodeMemory:TInfoTreeNodeMemory read m_rInfoTreeNodeMemory write m_rInfoTreeNodeMemory;
  end;

  TInfoTreeNodeMemoryItemList=class
  private
    m_List:TList<TInfoTreeNodeMemory>;
  public
    constructor Create;
    destructor Destroy; override;

    procedure HandleOnDeletion(Node: TInfoTreeNode);
    procedure HandleOnAddition(Node: TInfoTreeNode);
  end;

  TfraInfoTree = class(TFrame)
    tvInfo: TTreeView;
    procedure tvInfoCreateNodeClass(Sender: TCustomTreeView;
      var NodeClass: TTreeNodeClass);
    procedure tvInfoDeletion(Sender: TObject; Node: TTreeNode);
    procedure tvInfoAddition(Sender: TObject; Node: TTreeNode);
  private
    m_NodeMemory:TInfoTreeNodeMemoryItemList;
   ...

procedure TfraInfoTree.tvInfoCreateNodeClass(Sender: TCustomTreeView;
  var NodeClass: TTreeNodeClass);
begin
  // THIS IS VITAL!
  NodeClass:=TInfoTreeNode;
end;

procedure TfraInfoTree.tvInfoDeletion(Sender: TObject; Node: TTreeNode);
begin
  m_NodeMemory.HandleOnDeletion(TInfoTreeNode(Node));
end;

procedure TfraInfoTree.tvInfoAddition(Sender: TObject; Node: TTreeNode);
begin
  m_NodeMemory.HandleOnAddition(TInfoTreeNode(Node));
end;

g_icTreeNodeNotInList=MAXINT;

procedure TInfoTreeNodeMemoryItemList.HandleOnDeletion(Node: TInfoTreeNode);
var
  iPosition:integer;
begin
  iPosition:=integer(Node.Data);

  if iPosition=g_icTreeNodeNotInList then
    raise Exception.Create('Node memory not found!')
    else
    // we recognize this node; store his data so we can give it back to him later
    m_List[iPosition]:=Node.InfoTreeNodeMemory;
end;

procedure TInfoTreeNodeMemoryItemList.HandleOnAddition(Node: TInfoTreeNode);
var
  iPosition:integer;
begin
  // "coat check" for getting back node data later
  iPosition:=integer(Node.Data);

  if iPosition=g_icTreeNodeNotInList then
    begin
      // Node.Data = index of it's data
      // can't set Node.Data in OnDeletion so we must assign it in OnAddition instead
      Node.Data:=pointer(m_List.Count);
      // this data may very well be blank; it mostly occupies space; we harvest the real data in OnDeletion
      m_List.Add(Node.InfoTreeNodeMemory);
    end
    else
    // we recognize this node; give him his data back
    Node.InfoTreeNodeMemory:=m_List[iPosition];
end;

очень круто ... это отвечает всем моим целям!

чтобы добавить узел в дерево, все что мне нужно сделать:

// g_icTreeNodeNotInList important so the "coat check" (TInfoTreeNodeMemoryItemList)
// can recognize this as something that's not in it's list yet.
MyInfoTreeNode:=TInfoTreeNode(tvInfo.Items.AddChildObject(nParent, sText, pointer(g_icTreeNodeNotInList))));
0 голосов
/ 03 декабря 2009

Посмотрев на TCustomTreeView.DestroyWnd, как предлагает Джо Мейер, я бы предложил вам вернуться к использованию свойства TreeNode.Data и сохранить ссылку на объекты нового класса, наследуемые от TObject напрямую. Событие OnDeletion TreeView предлагает хорошее место для размещения кода уничтожения: "TMyObject (Node.Data) .Free;"

Использование очень похоже, за исключением того, что вам нужно использовать «TMyObject (Node.Data)» вместо «TMyNode (Node)». Предупреждение: опыт научил меня уделять пристальное внимание тому, чтобы не забывать часть «.Data», поскольку «TMyObject (Node)» не будет выдавать ошибку компиляции и вызывать нарушения прав доступа во время выполнения.

0 голосов
/ 03 декабря 2009

проблема вызвана неправильной реализацией вашего собственного TreeNode - он не сохраняет свою информацию, когда родительское окно TreeView воссоздается после того, как оно было закрыто. В качестве решения создайте потомок TTreeView и переопределите его метод DestroyWnd, чтобы сохранить ваши пользовательские значения. Например, посмотрите, как реализован метод TCustomTreeView.DestroyWnd.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...