PDA

Ver la Versión Completa : TTreeNode, que estoy haciendo mal


setq
19-11-2011, 05:41:17
Hola, primero decir que como vengo del mundo del java aun se me siguen trabando algunas cosas de Delphi que hacia algunos años que no tocaba.

Tengo una clase TForm con un TTreeView y una clase digamos B, que recibe en el constructor el TTreeNodes del TTreeview, extrae y guarda el primer nodo del TTreeView en la variable Normas que es de tipo TTreeNode. Posteriormente se usara para poder recorrer los nodos del TTreeView desde dentro de B sin necesitar conocer el TTreeView. La intencion final es mantener la vista separada de la capa negocio, al menos esa es mi idea.

Cuando intento recorrer el arbol del TTreeView en la clase B gracias a la variable Normas que apunta a su primer nodo, obtengo datos extraños que no son los de los nodos del arbol, que se muestra correctamente en el Form con sus datos.
El nodo Normas parece apuntar a un unico nodo que contiene el valor '' y que no tiene ni hijos ni hermanos, cuando deberia apuntar a un arbol con muchos nodos y datos diferentes.
No es que haya ningun error de compilacion ni en tiempo de ejecucion, simplemente algo hay mal hecho que no alcanzo a ver pero que no hace romper al programa.

Es Delphi 2010.

Ahi les dejo el codigo con algunos comentarios a ver si alguien ve algo porque yo ya estoy mareado de tanto mirarlo, y seguro que una linea vale mas que mil palabras.

Y muchas gracias por el tiempo.



unit B;

type
B = class
Normas: TTreeNode;


constructor Create(headNormas: TTreeNodes);
destructor Destroy;override;
...

implementation

constructor B.Create(headNormas: TTreeNodes);
begin
Normas:=TTreeNode.Create(headNormas);
end;
end;


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


unit SDIMAIN;

type
TSDIAppForm = class(TForm)

treeNormas: TTreeView;
...
procedure CargarTree;
...
private
FRL:B;
end;

implementation

procedure TSDIAppForm.CargarTree;
...
treeNormas.LoadFromFile(name);
FRL:=B.Create(treeNormas.Items);
...

function TInterpreteFRL.getNorma(nombreNorma: String): TTreeNode;
var
nodo: TTreeNode;
begin
nodo := Normas; <<<<<<<< AQUI ES DONDE CARGA EL VALOR EXTRAÑO ''
while (nodo <> NIL) and (nodo.Text <> nombreNorma) do
nodo := nodo.GetNextSibling;
getNorma := nodo;
end;

ecfisa
19-11-2011, 11:10:47
Hola.

Realmente no alcanzo a entender bién lo que deseas hacer por lo que no voy a sugerir nada...

Remitiendome al código, probá de este modo:

...
type
TMiTreeNodes = class(TObject)
private
FNodes: TTreeNodes;
public
property Nodes: TTreeNodes read FNodes write FNodes;
constructor Create(TNd: TTreeNodes);
function GetThisNode(Value: string): TTreeNode;
destructor Destroy; override;
end;
...
implementation

constructor TMiTreeNodes.Create(TNd: TTreeNodes);
begin
FNodes := TTreeNodes.Create(nil);
FNodes := TNd
end;

function TMiTreeNodes.GetThisNode(Value: string): TTreeNode;
var
N: TTreeNode;
begin
N:= FNodes.GetFirstNode;
while Assigned(N) and(N.Text <> Value) do
N := N.getNextSibling;
Result:= N
end;

...


Ejemplo

TForm1 = class(TForm)
TreeView1: TTreeView;
...
private
FRL: TMiTreeNodes;
procedure CargarTreeNode;
public
end;
...

implementation
...

procedure TForm1.CargarTreeNode;
begin
TreeView1.LoadFromFile('C:\ARCHIVO.TXT');
FRL:= TMiTreeNodes.Create(TreeView1.Items)
end;

procedure TForm1.Button1Click(Sender: TObject);
var
tn: TTreeNode;
begin
tn:= FRL.GetThisNode('UN DATO');
if Assigned(tn) then
begin
ShowMessage(tn.Text);
...
end
end;
...


Saludos.

setq
19-11-2011, 19:41:11
Anoche estaba un poco denso de tanto rumiar el fallo, a ver si consigo resumir lo que quiero, es fácil:
Lo que necesito es cargar un árbol de nodos desde disco sin necesidad de que se muestre en pantalla. Y no conozco en Delphi una clase que me de esa funcionalidad sin tener que meter un componente en el Form. He tenido que usar el TTreeView porque es donde único dispongo del método LoadFromFile.
Mi clase no necesita mostrar el árbol en ningun componente, es una clase solo para procesar informacion de cualquier árbol que se le pase en el constructor mediante un TTreeNodes.

En cuanto al error que me daba, creo que estaba en el constructor:

Estaba haciendo esto:
N:=TTreeNode.Create(TNs);

Y he cambiado a esto:
N:=TNs.GetFirstNode;

Ahora ya me funciona. Lo ves correcto?.

En el caso A, N apunta al primer nodo del arbol TNs? o a donde? Digo 'apunta' porque pienso en punteros, al hablar de clases es correcto?. Estoy javaintoxicado.

En el caso B, N apunta al primer nodo del arbol TNs y cualquier modificacion de N afectaria a TNs?. Y en el destructor ya no debo llamar al Free creo?.


En tú código no entiendo esta parte en el constructor de TMiTreeNode:

FNodes := TTreeNodes.Create(nil); FNodes := TNd

Primero creas el TTreeNodes y luego los reasignas?.

Y porqué usas properties pudiendo acceder a las variables declaradas public para ello. Se que son como los getters y setters de java pero en este caso no veo la necesidad,.. o si?


Gracias, saludos.

ecfisa
21-11-2011, 11:38:37
Hola.

Primero creas el TTreeNodes y luego los reasignas?.

Si, en realidad podría haber enviado directamente el TreeView como argumento al constructor...

constructor TMiTreeNodes.Create(TNd: TTreeView);
begin
FNodes := TTreeNodes.Create(TNd);
end;



Y porqué usas properties pudiendo acceder a las variables declaradas public para ello. Se que son como los getters y setters de java pero en este caso no veo la necesidad,.. o si?

El uso de propiedades, si bién no es estríctamente necesario (se puede acceder a variables públicas de la clase o incluso globales), siempre es una buena práctica, ya que brinda mayor control y encapsulamiento.

Un saludo.

Edito: Me quedé pensando en lo que mencionas: "Lo que necesito es cargar un árbol de nodos desde disco sin necesidad de que se muestre en pantalla."

¿ Y no te resultaría más fácil usar un TTreeView con su propiedad Visible = False ? Lo anterior quedaría reducido a:

function GetThisNode(FNodes: TTreeNodes; Norma: string): TTreeNode;
var
N: TTreeNode;
begin
N:= FNodes.GetFirstNode;
while Assigned(N) and(N.Text <> Norma) do
N := N.getNextSibling;
Result:= N;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
t: TTreeNode;
begin
TreeView1.LoadFromFile('C:\ARCHIVO.TXT');
t:= GetThisNode(TreeView1.Items, 'UN_NODO');
if Assigned(t) then
...
end;

setq
21-11-2011, 15:00:59
Es que no quiero usar un componente visual para manejar el arbol, me gustaria poder hacerlo por codigo solamente, es decir, sin tener que coger un componente de la paleta y pincharlo en el TForm, me explico?. Y ponerle la propiedad visible=false seria un apaño. No me gusta esa solución. De todas formas ya lo tengo solucionado usando TTreeNodes y la cosa va bien.

Pero me ha quedado una espina con el constructor que no entiendo:

Estaba haciendo esto: N:=TTreeNode.Create(TNs); //N.GetFirstNode devuelve un valor basura

Y he cambiado a esto: N:=TNs.GetFirstNode;
//N.GetFirstNode devuelve un valor correcto



En el primer caso N queda apuntando al primer nodo de TNs, o que es lo que pasa ahi para que no me de
valores correctos del TNs que le he pasado?.

Gracias por tu tiempo de nuevo.

ecfisa
21-11-2011, 16:00:28
Hola setq.

No tenés nada que agradecer.
Estoy un poco confundido, quizá por que no entiendo bién la lógica que deseas aplicar. Pero a ver, según entiendo en tu código, en el caso de N:=TTreeNode.Create(TNs), estas creando el objeto pero no lo inicializas en ningún momento, por eso el valor basura. Revisá este enlace (http://www.clubdelphi.com/foros/showthread.php?t=76600) donde se trata la función del parámetro Owner en la creación.

Hay algo que todavía no me queda muy claro:
Es que no quiero usar un componente visual para manejar el arbol
Pero aquí lo estas usando:

type
TSDIAppForm = class(TForm)

treeNormas: TTreeView;
...
procedure CargarTree;
...
private
FRL:B;
end;

Es que me parece, que si deseas usar el método LoadFromFiles de TTreeView, en algún momento vas a tener que usar un TreeView y este tiene que tener un parent ...

Saludos.

setq
21-11-2011, 16:39:09
Si, es cierto que lo estoy usando, pero como dije, es la unica forma que he visto de disponer de un metodo LoadFromFile que me cargue un arbol desde disco.

Busco algo como el TTreeNode que tenga un metodo LoadFromFile y cree un arbol (en memoria) desde disco devolviendo su root, asi no tendria que colocar TTreeViews innecesarios en formularios.

La logica que intento aplicar es la de mantener separados la logica de negocio de las vistas, y un TTreeView para mi es una Vista de algo que hay debajo (el TTreeNodes y los TTreeNode). Si pudiera manejar esos TTreeNodes sin tener que crear el TTreeView seria estupendo, pero el constructor del TTreeNode me pide un owner TTreeNodes y este a su vez me pide un TTreeView, con lo cual no salgo del circulo.

Gracias siempre.