Recent

Author Topic: [Solved]Is it possible to store non visible data to a node in TTreeView  (Read 15475 times)

Knipfty

  • Full Member
  • ***
  • Posts: 232
Hi All,

I would like to store DB keys in the last child nodes of a treeview so that they are not visible.  For example

Investors
-Joe Smith
-Mary Jones
-George Bush

It doesn't look like you can.  The only thing that I can do is add a child node to each investor that contains the db key.  For example

Investors
-Joe Smith
--{EA1B0235-E54C-4913-80AC-6D4AE064254E}
-Mary Jones
--{7A934ADB-55B2-43A8-87B3-436053EC3057}
-George Bush
--{C1C6CEFC-8667-4282-ACD9-5A6F0164922F}

While its not a big deal to have this data visible, it would look nicer if the info was simply stored with the investor and not the extra child.

Thanks

Knipfty
« Last Edit: August 16, 2012, 05:27:21 pm by Knipfty »
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

IPguy

  • Sr. Member
  • ****
  • Posts: 385
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #1 on: August 15, 2012, 02:09:23 am »
I've thought about this issue with VST. 
One option would be to have an entry in the tree that I strip out (delete) when I load the tree and add back in when I save the tree.

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #2 on: August 15, 2012, 02:30:44 am »
The thing is that I need the data, I just don't want to display it.

I found a property called Data with is a Pointer.  And I'll admit, pointers are a weak spot.  What I can't figure out is what kind of data structure I need to create to have this data item point to?  There is also an AddObject method to insert the data.
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #3 on: August 15, 2012, 10:21:38 am »
The thing is that I need the data, I just don't want to display it.

I found a property called Data with is a Pointer.  And I'll admit, pointers are a weak spot.  What I can't figure out is what kind of data structure I need to create to have this data item point to?  There is also an AddObject method to insert the data.

As a pointer type variable data can hold anything you like it to hold from objects to record to simple variables like string or integers there is no limit on what it can hold.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #4 on: August 15, 2012, 11:53:44 am »
The easiest way to add extra properties is to use the OnCreateNodeClass event and make your own Node Class.

eg.
First create your own node class with the extra properties you want.
Code: [Select]
TMyNode = class(TTreeNode)
  public
    Guid:string;
  end; 

And then on the OnCreateNodeClass do something like ->
Code: [Select]
NodeClass := TMyNode;

And whenever you want to access your extra properties do something like ->
Code: [Select]
with TMyNode(Node) do
    begin
    showmessage(guid);
    end;

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #5 on: August 15, 2012, 03:05:53 pm »
Hi KpjComp,

This is helpful.  Just one more question.  I think my disconnect is between steps 1 and 2 as you have outlined it.  I see that you've create a rcord type of TMyNode.  Where is the record instantiated?

Is NodeClass part of my tree already?

This is what I have so far

Code: [Select]
type
TTreeListForm = class(TForm)
  HMLTreeView: TTreeView;
{...}
private
public
    procedure BuildTreeList;
end;

var
  TreeListForm: TTreeListForm;
  MtgNode: TTreeNode;

{...}
procedure TTreeListForm.BuildTreeList;
var
  N: TTreeNode;
begin
  HMLTreeView.BeginUpdate;
  HMLTreeView.Items.Clear;
{...}
  MtgNode := HMLTreeView.Items.AddChild(nil, 'Mortgages');
  AdsQueryMtg.First;
  while not AdsQueryMtg.EOF do
  begin
    N := HMLTreeView.Items.AddChild(MtgNode,
      LeftStr(AdsQueryMtg.FieldByName('BusinessName').AsString,10) + '/'
      + LeftStr(AdsQueryMtg.FieldByName('ShortName').AsString,10) + '/'
      + AdsQueryMtg.FieldByName('Principal').AsString);
    HMLTreeView.Items.AddChild(N, 'KID:'+AdsQueryMtg.FieldByName('KID').AsString);
    AdsQueryMtg.Next;
  end;

I believe that the last two lines in the loop is what I need to change:
Code: [Select]
    N := HMLTreeView.Items.AddChild(MtgNode,
      LeftStr(AdsQueryMtg.FieldByName('BusinessName').AsString,10) + '/'
      + LeftStr(AdsQueryMtg.FieldByName('ShortName').AsString,10) + '/'
      + AdsQueryMtg.FieldByName('Principal').AsString);
    HMLTreeView.Items.AddChild(N, 'KID:'+AdsQueryMtg.FieldByName('KID').AsString);

To somehting like:
Code: [Select]
N := HMLTreeView.Items.AddChildObject(MtgNode,
      LeftStr(AdsQueryMtg.FieldByName('BusinessName').AsString,10) + '/'
      + LeftStr(AdsQueryMtg.FieldByName('ShortName').AsString,10) + '/'
      + AdsQueryMtg.FieldByName('Principal').AsString,
     'KID:'+AdsQueryMtg.FieldByName('KID').AsString);

This very last line which contains the string "'KID:'+AdsQueryMtg.FieldByName('KID').AsString" is the data that need to be stored in some data structure.  This can be a TStrings I guess.  DO I do something like MyTStrings.lines.Add('my data') and get the pointer address (how?) and replace that poiniter in the last line above?  And then how do I dereference that pointer later on?

Thanks

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #6 on: August 15, 2012, 03:32:34 pm »
Quote
Where is the record instantiated?
Is NodeClass part of my tree already?

NodeClass is the parameter to the OnCreateNodeClass event.
What this does is all the TTreeNode's get made into whatever you pass into NodeClass, in this case all your Nodes will become TMyNode instead of TTreeNode, so the class get instantiated automatically for you, you don't need to worry about allocation/deallocations :).

So in your example where your creating your child object, that you have called N.
You can now do ->
Code: [Select]
  N := HMLTreeView.Items.AddChild(MtgNode,.......
  TMyNode(N).guid := AdsQueryMtg.FieldByName('KID').AsString;

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #7 on: August 15, 2012, 03:44:55 pm »
Thanks.  I'll play with it over the next couple of nights. And let you know how it works.

I really appreciate it.

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

sfeinst

  • Full Member
  • ***
  • Posts: 245
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #8 on: August 15, 2012, 07:49:58 pm »
Hi All,

I would like to store DB keys in the last child nodes of a treeview so that they are not visible.  For example

Investors
-Joe Smith
-Mary Jones
-George Bush

It doesn't look like you can.  The only thing that I can do is add a child node to each investor that contains the db key.  For example

Investors
-Joe Smith
--{EA1B0235-E54C-4913-80AC-6D4AE064254E}
-Mary Jones
--{7A934ADB-55B2-43A8-87B3-436053EC3057}
-George Bush
--{C1C6CEFC-8667-4282-ACD9-5A6F0164922F}


My data is in an object so I defined a type as a pointer to that object.  I create an object assigning it to the pointer.  Then I assign the pointer to the treenode.Data property.  Since it looks like your DB ID is a string you might be able to grab the address of the string and store it in treenode.data.  I haven't tried this as I tend to keep all my data about each node together in an object.

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #9 on: August 15, 2012, 08:00:12 pm »
Hi sfeinst,

What I was thinking of doing before  KpjComp's post was something like this:

Code: [Select]
  I := TGUIDStrings.Add('{F7F13ABA-E1A2-4CD4-BFCB-FD7917E11FDC}');
  N := TreeView1.Items.AddChildObject(N, 'Hello World', pointer(TGUIDStrings.Strings[I]));
  showmessage(PChar(n.Data));

The only ther thing I would need to do is create and free the TGUIDStrings in the OnCreate and OnDestroy form event handler.

I think this meshes up with your suggestion.

So my plan is to play with this approach and KpjComp's suggestion.  Assuming I get either to work, this will simplify some of my code.

Thanks for the suggestion.  I really appreciate it.

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

sfeinst

  • Full Member
  • ***
  • Posts: 245
Re: Is it possible to store non visible data to a node in TTreeView
« Reply #10 on: August 16, 2012, 04:15:36 pm »
What you are attempting sounds similar to how I did it.  The OnCreateNodeEvent is interesting as well.  I did not notice that when I wrote my code.  I prefer using objects to pointers just out of habit, so I may follow that route in the future as well.

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: [Solved]Is it possible to store non visible data to a node in TTreeView
« Reply #11 on: August 16, 2012, 05:35:16 pm »
Hi sfeinst/KpjComp,

I got it working last night.  I went with a TStringList to store the GUIDs.

Code: [Select]
procedure TTreeListForm.LoadFromQuery(var TV: TTreeView; const DS: TDataSet; const TN: TTreeNode;
                                      const F: array of string; const L: array of byte);
// The arrarys above are zero offset
const
  CB: array[0..2] of byte = (%100,%10,%1);  //Binary 100, 10, 1
var
  I: integer;  //Index of recently added TGUIDStrings
  C: byte;     //Case value
begin
  // It should be noted that loop invariants checks for F1 thru F3 now reside
  // outside the while loop.  Doing this should make the while loop more efficient.
  // I'm concerned that if the data sets grow too large, this could slow down the
  // tree building.  The case statement gets rid of all the if/then/else logic
  // and makes it a two step process.  Jump to correct 'C' value and add string
  // to tree.
  C := 0;
  for I := 0 to 2 do
    if F[I] <> '' then C := C + CB[I];  //Figure out which fields are needed to build string
  DS.Active := false;
  DS.Active := true;   //Refresh DataSet
  DS.First;            //Retrieve 1st record
  while not DS.EOF do  //Loop until we hit End Of File
  begin
    I := GUIDStrings.Add(DS.FieldByName('KID').AsString);
    case C of  //Build String and Add string as child of node, TN
      %100: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[0]).AsString,L[0]),
                                    pointer(GUIDStrings.Strings[I]));
      %010: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[1]).AsString,L[1]),
                                    pointer(GUIDStrings.Strings[I]));
      %001: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[2]).AsString,L[2]),
                                    pointer(GUIDStrings.Strings[I]));
      %110: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[0]).AsString,L[0])+'/'
                                      +LeftStr(DS.FieldByName(F[1]).AsString,L[1]),
                                    pointer(GUIDStrings.Strings[I]));
      %101: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[0]).AsString,L[0])+'/'
                                      +LeftStr(DS.FieldByName(F[2]).AsString,L[2]),
                                    pointer(GUIDStrings.Strings[I]));
      %011: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[1]).AsString,L[1])+'/'
                                      +LeftStr(DS.FieldByName(F[2]).AsString,L[2]),
                                pointer(GUIDStrings.Strings[I]));
      %111: TV.Items.AddChildObject(TN,LeftStr(DS.FieldByName(F[0]).AsString,L[0])+'/'
                                      +LeftStr(DS.FieldByName(F[1]).AsString,L[1])+'/'
                                  +LeftStr(DS.FieldByName(F[2]).AsString,L[2]),
                                pointer(GUIDStrings.Strings[I]));
      otherwise TV.Items.AddChildObject(TN,'',pointer(GUIDStrings.Strings[I]))
    end;
    DS.Next  //Retrieve next record in DataSet
  end
end;

It greatly simplified my code through the unit.  As an example, before I was storing the GUID in a child node that had to betrieved with extra steps:
Code: [Select]
procedure TTreeListForm.MenuItemModInvestorClick(Sender: TObject);
var
  N: TTreeNode;
begin
//Level KIDLevel is where the KID is stored.  If its missing, then we have real issues
//When entering here, we are either on Level 1 or 2, not zero.
  N := HMLTV.Selected;
  while N.HasChildren and (N.Level < KIDLevel) do
    N := N.GetFirstChild;
  if N.Level = KIDLevel then
  begin
    with RefInvestorsEditEntryForm do
    begin
      EditEntryMode := eeModify;
      InvestorID := RightStr(N.Text, TGUIDStrLen);
      ShowModal
    end;
    BuildTreeList
  end
end;

Now it looks like this:
Code: [Select]
procedure TTreeListForm.MenuItemModInvestorClick(Sender: TObject);
begin
    with RefInvestorsEditEntryForm do
    begin
      EditEntryMode := eeModify;
      InvestorID := PChar(HMLTV.Selected.Data);
      ShowModal
    end;
    BuildTreeList
end;

Thanks for you help.

KpjComp, I will look at your proposed solution when I free up some time.  It looks interesting.  I just more comfortable of a TStringList.

Thanks

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: [Solved]Is it possible to store non visible data to a node in TTreeView
« Reply #12 on: August 17, 2012, 12:01:27 am »
Quote
KpjComp, I will look at your proposed solution when I free up some time.  It looks interesting.  I just more comfortable of a TStringList.

Always good idea to go with what you feel comfortable with.

If you do get chance to play with creating you own NodeType, it will give you a few extra benefits,.. Eg if you later wanted to add some more properties, just add it, and no extra typecasting required.  Also if you delete/add new nodes, you don't have the worry about having to maintain two lists.

One thing I would mention, this sort of casting
Code: [Select]
pointer(GUIDStrings.Strings[I]) makes me cringe!! Your casting a managed string type to a pointer, and then casting this pointer to a pchar, scary stuff!!

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: [Solved]Is it possible to store non visible data to a node in TTreeView
« Reply #13 on: August 18, 2012, 12:58:22 am »
Big Shout Out to KpjComp!

Not only was his suggestion great way of extending the TTreeNode, but it also solved my SIGSEGV error I was getting on another thread.

In case anyone is interested here is what was done.
1.  Create a new TTreeNode class with addition properties
Code: [Select]
type
  TMyTreeNode = class(TTreeNode)
  public
    Guid: TGUIDStr;
    PopUpMenu: tvNodeType;
  end; 

2. Add an event handler, CreateNodeClass.  This gets called each time a new TTreeNode is created.
Code: [Select]
procedure TTreeListForm.HMLTVCreateNodeClass(Sender: TCustomTreeView;
  var NodeClass: TTreeNodeClass);
begin
  NodeClass := TMyTreeNode
end;

3. Any time you add a new TTreeNode, use the standard method AddChild followed by assignments to the new properties.  Note the type casting in the with statement.
Code: [Select]
procedure AddMyChild(S: string);  //Does the grunt work of creating and adding data to TreeView
  var
    N: TTreeNode;
  begin
    N := TV.Items.AddChild(TN, S);  // Add TreeNode using stardard call
    with TMyTreeNode(N) do
    begin
      GUID := DS.FieldByName('KID').AsString; // Add GUID that is was added to the structure
      PopUpMenu := PopUp  //Assign popup menu value
    end
  end;

4. Finally, when you need to extract the data from a TTreeNode.  You once again need to type cast.  See InvestorID assignment below.
Code: [Select]
procedure TTreeListForm.MenuItemModInvestorClick(Sender: TObject);
begin
  with RefInvestorsEditEntryForm do
  begin
    EditEntryMode := eeModify;
    InvestorID := TMyTreeNode(HMLTV.Selected).GUID;
    ShowModal
  end;
  BuildTreeList
end;

That's basically it.

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: [Solved]Is it possible to store non visible data to a node in TTreeView
« Reply #14 on: August 18, 2012, 08:23:46 am »
Great stuff, Knipfty.

Your explanation of what you did is very handy too.  Other users I'm sure will find it useful.
Also for a little bit of extra type safety, what you can do is wrap the type cast into a function.

Something like this ->
Code: [Select]
function TTreeListForm.MyTreeNode(Node:TTreeNode):TMyTreeNode;
begin
  result := TMyTreeNode(Node); //you could even use AS here for extra safety
end;

//then ->
with TMyTreeNode(N) do
//becomes
with MyTreeNode(N) do

The advantage here is that it forces you to pass a TTreeNode type,.eg you couldn't say accidentally do MyTreeNode(self); //if say self was the Form, etc. but you could do TMyTreeNode(self); //bad.   Basically it reduces the number of cast's, and this in the long term makes for much safer code.

 

TinyPortal © 2005-2018