Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

TreeView to Menu 1

Status
Not open for further replies.

Destruktion

Programmer
Mar 18, 2011
28
GB
Hi, Im trying to copy the structure of a TTreeview to a popup menu, I can do this getting the caption and image of the tree nodes and adding it to the popup menu, but I cant get the parent and child relationship working.

55292755.png


Please see my code and help me make the changes.

Code:
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  tvwMain.FullExpand;
end;

procedure TfrmMain.cmdRoot1AsMenuClick(Sender: TObject);
var
  P: TTreeNode;
  i: Integer;

  MenuItem, SubItem: TMenuItem;

  pt: TPoint;
begin
  mnuPopup.Items.Clear;

  for i:= 0 to tvwMain.Items.Count -1 do // iterate through the tree
  begin
    if tvwMain.Items[i].Level > 0 then // ignore highest root
    begin
      P:= tvwMain.Items[i];

      while Assigned(P.Parent) do
      begin
        P:= P.Parent;
      end;

      if P.Text = 'Root1' then // scan Root1 of treeview only and
      begin                    // add the nodes to the popup menu
        MenuItem:= TMenuItem.Create(mnuPopup);
        MenuItem.Caption:= tvwMain.Items[i].Text;
        MenuItem.ImageIndex:= tvwMain.Items[i].ImageIndex;

        if tvwMain.Items[i].HasChildren then
        begin
          //
        end;

       mnuPopup.Items.Add(MenuItem);
      end;
    end;
  end;

  // position and show the popup menu
  pt.X:= TSpeedButton(Sender).Left + 1;
  pt.Y:= TSpeedButton(Sender).Top + TSpeedButton(Sender).Height + 1;
  pt:= ClientToScreen(pt);

  mnuPopup.Popup(pt.X, pt.Y);
end;

procedure TfrmMain.cmdRoot2AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdRoot3AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdFullTreeviewAsMenuClick(Sender: TObject);
begin
  //
end;

Thanks
 
Hopefully this can help.

Code:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Menus, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    PopupMenu1: TPopupMenu;
    procedure Button1Click(Sender: TObject);
    procedure ClickTest(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    MenuItem: TMenuItem;
    SubItems: Array[0..3] of TMenuItem;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ClickTest(Sender: TObject);
begin
  ShowMessage(TMenuItem(Sender).Caption);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  cp: TPoint;
  i: integer;
begin
  GetCursorPos(cp);
  MenuItem := TMenuItem.Create(Self);
  MenuItem.Caption := 'Main Menu';
  MenuItem.OnClick := nil;
  for i := 0 to 3 do
    begin
      SubItems[i] := TMenuItem.Create(self);
      SubItems[i].Caption := 'Sub Item ' + IntToStr(i);
      SubItems[i].OnClick := ClickTest;
      MenuItem.Add(SubItems[i]);
    end;
  PopupMenu1.Items.Add(MenuItem);
  PopupMenu1.Popup(cp.x, cp.y);

end;

end.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Thanks for the reply Glen, I should of perhaps been more clear in my initial post. The Treeview is going to be dynamic in that I will never know its actual structure, the nodes represented by a folder are to be parent menu items, nodes that are child to the folder are to be sub menus. I just cant for the life of me figure out how to populate the popup menu with the same hierarchy structure layout as the treeview, especially since the treeview could be x many levels deep.

Please see my revised code, it obviously is not working correctly. Also see attachment as I have added the ZIP of the source.

Earlier I added new code to create a sample main menu at runtime to get an idea of how it works, there are no click events but I know that and can easily implement that. I just want to populate a menu based on a treeview...

Code:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, Buttons, Menus, ImgList;

type
  TfrmMain = class(TForm)
    tvwMain: TTreeView;
    cmdRoot1AsMenu: TSpeedButton;
    mnuPopup: TPopupMenu;
    imlMain: TImageList;
    cmdRoot2AsMenu: TSpeedButton;
    cmdRoot3AsMenu: TSpeedButton;
    cmdFullTreeviewAsMenu: TSpeedButton;
    mnuMain: TMainMenu;
    procedure FormCreate(Sender: TObject);
    procedure cmdRoot1AsMenuClick(Sender: TObject);
    procedure cmdRoot2AsMenuClick(Sender: TObject);
    procedure cmdRoot3AsMenuClick(Sender: TObject);
    procedure cmdFullTreeviewAsMenuClick(Sender: TObject);
  private
    procedure CreateDynamicMainMenu;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

// this is just to show how to create menu and sub menu items
// to the main menu
procedure TfrmMain.CreateDynamicMainMenu;
var
  MenuItem, SubMenuItem: TMenuItem;
begin
  // file menu
  MenuItem:= TMenuItem.Create(mnuMain);
  MenuItem.Caption:= '&File';
  mnuMain.Items.Add(MenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Treeview';
  mnuMain.Items[0].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Expand';
  MenuItem.Items[0].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Collapse';
  MenuItem.Items[0].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= '-';
  mnuMain.Items[0].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'E&xit';
  mnuMain.Items[0].Add(SubMenuItem);

  // edit menu
  MenuItem:= TMenuItem.Create(mnuMain);
  MenuItem.Caption:= '&Edit';
  mnuMain.Items.Add(MenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Root1 Nodes as Menu';
  mnuMain.Items[1].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Root2 Nodes as Menu';
  mnuMain.Items[1].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Root3 Nodes as Menu';
  mnuMain.Items[1].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= '-';
  mnuMain.Items[1].Add(SubMenuItem);

  SubMenuItem:= TMenuItem.Create(mnuMain);
  SubMenuItem.Caption:= 'Full Treeview as Menu';
  mnuMain.Items[1].Add(SubMenuItem);
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  CreateDynamicMainMenu;

  tvwMain.FullExpand;
end;

procedure TfrmMain.cmdRoot1AsMenuClick(Sender: TObject);
var
  P: TTreeNode;
  i: Integer;

  FCaption: string;
  FIcon: Integer;
  FName: string;

  MenuItem, SubMenuItem: TMenuItem;

  pt: TPoint;
begin
  mnuPopup.Items.Clear;

  for i:= 0 to tvwMain.Items.Count -1 do // iterate through the tree
  begin
    if tvwMain.Items[i].Level > 0 then // ignore highest root
    begin
      P:= tvwMain.Items[i];

      while Assigned(P.Parent) do // get the highest root
      begin
        P:= P.Parent;
      end;

      if P.Text = 'Root1' then // scan Root1 of treeview only
      begin
        FCaption:= tvwMain.Items[i].Text;
        FIcon:= tvwMain.Items[i].ImageIndex;
        FName:= 'Menu' + IntToStr(i);

        MenuItem:= TMenuItem.Create(mnuPopup);
        MenuItem.Caption:= FCaption;
        MenuItem.ImageIndex:= FIcon;
        MenuItem.Name:= FName;

        if tvwMain.Items[i].HasChildren then // found parents such as Sub Root 1
        begin
          SubMenuItem:= TMenuItem.Create(mnuMain);
          SubMenuItem.Caption:= FCaption;
          SubMenuItem.ImageIndex:= FIcon;
          SubMenuItem.Name:= FName;

          //showmessage(tvwMain.Items[i].Text);

          MenuItem.Add(SubMenuItem);
        end;

       mnuPopup.Items.Add(MenuItem);
      end;
    end;
  end;

  // position and show the popup menu
  pt.X:= TSpeedButton(Sender).Left + 1;
  pt.Y:= TSpeedButton(Sender).Top + TSpeedButton(Sender).Height + 1;
  pt:= ClientToScreen(pt);

  mnuPopup.Popup(pt.X, pt.Y);
end;

procedure TfrmMain.cmdRoot2AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdRoot3AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdFullTreeviewAsMenuClick(Sender: TObject);
begin
  //
end;

end.

Thanks.

PS, incase link does not show in attachments it is here to download zip source:
 
The answer to this one is recursion. As it's a computer science topic that usually has a certain amount of time spent on it, I can just leave you to research/ask questions if you want to know more.

Here's my boilerplate TTreeView example I did once upon a time for another question, but I modified it to generate a popup menu on a selected item. There are two examples of recursion within the source. Keep in mind that running it against a drive might generate a large amount of data and therefore take a long time and maybe bust the stack. I made it to disable the buttons while it is processing, but to that end if you do something that takes a while you will need to make it threaded.

Code:
unit ffunit;

  { demonstrates recursive processing on a TTreeView using files and directories, added button that makes popup menu of the selected element.}
interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, FileCtrl, ComCtrls, Menus;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    DriveComboBox1: TDriveComboBox;
    Button2: TButton;
    PopupMenu1: TPopupMenu;
    procedure Button1Click(Sender: TObject);
    procedure rec_list_dir(inpath: string; WinAddr1: TTreeNode);
    procedure TestClick(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure PopupMenuPopulate(BaseNode: TTreeNode; MenuItem: TMenuItem);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.rec_list_dir(inpath: string; WinAddr1: TTreeNode);
  var
    sr: TSearchRec;
    res: integer;
    TNode: TTreeNode;
  begin
    res := FindFirst(inpath + '\*.*', faAnyFile, sr);
    while res = 0 do
      begin
        if (Sr.Name <> '.') and (Sr.Name <> '..') then
          begin
            TNode := TreeView1.Items.AddChild(WinAddr1, Sr.Name);
            if (sr.Attr and faDirectory) = faDirectory then
              rec_list_dir(inpath + '\' + sr.name, TNode);
          end;
        res := FindNext(sr);
      end;
    FindClose(sr);
  end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Button1.Enabled := false;
  TreeView1.Items.BeginUpdate;
  TreeView1.Items.Clear;
  rec_list_dir(DriveComboBox1.Drive + ':', nil);
  TreeView1.Items.EndUpdate;
  Button1.Enabled := true;
end;

procedure TForm1.TestClick(Sender: TObject);
begin
  ShowMessage(TMenuItem(Sender).Caption);
end;

procedure TForm1.PopupMenuPopulate(BaseNode: TTreeNode; MenuItem: TMenuItem);
var
  i: integer;
  SubMenuItem: TMenuItem;
begin
  if BaseNode.HasChildren then
    for i := 0 to (BaseNode.Count - 1) do
      begin
        SubMenuItem := TMenuItem.Create(self);
        SubMenuItem.Caption := BaseNode.Item[i].Text;
        if BaseNode.Item[i].HasChildren then
          PopupMenuPopulate(BaseNode.Item[i], SubMenuItem)
        else
          SubMenuItem.OnClick := TestClick;
        MenuItem.Add(SubMenuItem);
      end;
end;

procedure TForm1.Button2Click(Sender: TObject);
// make and popup menu on selected item.
var
  MenuItem: TMenuItem;
  SelNode: TTreeNode;
  cp: TPoint;
  i: integer;
begin
  Button2.Enabled := false;
  for i := 0 to (PopupMenu1.Items.Count - 1) do
    PopupMenu1.Items.Delete(i);
  SelNode := TreeView1.Selected;
  MenuItem := TMenuItem.Create(self);
  MenuItem.Caption := SelNode.Text;
  PopupMenuPopulate(SelNode, MenuItem);
  PopupMenu1.Items.Add(MenuItem);
  GetCursorPos(cp);
  PopupMenu1.Popup(cp.x, cp.y);
  Button2.Enabled := true;
end;

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Thats perfect thanks Glen, I actually implemented a Directory Listbox in addition to the Drive combo to make selecting a folder easier and essentially faster by reading a desired directory and changing:

Code:
rec_list_dir(DriveComboBox1.Drive + ':', nil);

to

Code:
rec_list_dir(DirectoryListBox1.Directory, nil);

Now onto the code, I am more interested in how the popup menu is been created dynamically. The thing that seemed to mix me up was I was adding MenuItem and SubMenuItem, when in this example you only used SubMenuItem (although just a naming term, it only has one TMenuItem reference, I kept adding two). The other thing I noticed is you check if the selected node has children, then run your loop, again this time checking if all nodes in the loop have children and if so call the procedure again to build the structure.

I have no questions at this point, I feel that from looking at the source I have an better understanding as to where I was going wrong with my code.

Many thanks, Appreciate you taking the time to help out.
 
Actually, to add to what I just said I noticed in the procedure declaration you actually do use MenuItem, as well SubMenuItem.

BaseNode is the selected node i gather.
 
Wish there was an Edit function on this forum!

Just wanted to add, in Button2Click procedure you use:

Code:
for i := 0 to (PopupMenu1.Items.Count - 1) do
    PopupMenu1.Items.Delete(i);

Is there any reason for this, surely PopupMenu1.Items.Clear is the same?

Well I adapted your code and fixed my treeview to menu from it :)

Here is updated code:

Code:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, Buttons, Menus, ImgList;

type
  TfrmMain = class(TForm)
    tvwMain: TTreeView;
    cmdRoot1AsMenu: TSpeedButton;
    mnuPopup: TPopupMenu;
    imlMain: TImageList;
    cmdRoot2AsMenu: TSpeedButton;
    cmdRoot3AsMenu: TSpeedButton;
    cmdFullTreeviewAsMenu: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure cmdRoot1AsMenuClick(Sender: TObject);
    procedure cmdRoot2AsMenuClick(Sender: TObject);
    procedure cmdRoot3AsMenuClick(Sender: TObject);
    procedure cmdFullTreeviewAsMenuClick(Sender: TObject);
  private
    procedure PopulatePopupMenu(BaseNode: TTreeNode; MenuItem: TMenuItem);
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.PopulatePopupMenu(BaseNode: TTreeNode; MenuItem: TMenuItem);
var
  i: integer;
  SubMenuItem: TMenuItem;
begin
  if BaseNode.HasChildren then
  begin
    for i:= 0 to (BaseNode.Count - 1) do
    begin
      SubMenuItem:= TMenuItem.Create(self);
      SubMenuItem.Caption:= BaseNode.Item[i].Text;
      SubMenuItem.ImageIndex:= BaseNode.Item[i].ImageIndex;

      if BaseNode.Item[i].HasChildren then
        PopulatePopupMenu(BaseNode.Item[i], SubMenuItem);
      MenuItem.Add(SubMenuItem);
    end;
  end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  tvwMain.FullExpand;
end;

procedure TfrmMain.cmdRoot1AsMenuClick(Sender: TObject);
var
  P: TTreeNode;
  i: Integer;

  MenuItem: TMenuItem;
  Node: TTreeNode;

  pt: TPoint;
begin
  mnuPopup.Items.Clear;

  for i:= 0 to tvwMain.Items.Count -1 do // iterate through the tree
  begin
    if tvwMain.Items[i].Level > 0 then // ignore highest root
    begin
      P:= tvwMain.Items[i];

      while Assigned(P.Parent) do // get the highest root
      begin
        P:= P.Parent;
      end;

      if P.Text = 'Root1' then // scan Root1 of treeview only
      begin
        Node:= tvwMain.Selected;
        MenuItem:= TMenuItem.Create(self);
        MenuItem.Caption := Node.Text;
        PopulatePopupMenu(Node, MenuItem);
        mnuPopup.Items.Add(MenuItem);

        Break;
      end;
    end;
  end;

  // position and show the popup menu
  pt.X:= TSpeedButton(Sender).Left + 1;
  pt.Y:= TSpeedButton(Sender).Top + TSpeedButton(Sender).Height + 1;
  pt:= ClientToScreen(pt);

  mnuPopup.Popup(pt.X, pt.Y);
end;

procedure TfrmMain.cmdRoot2AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdRoot3AsMenuClick(Sender: TObject);
begin
  //
end;

procedure TfrmMain.cmdFullTreeviewAsMenuClick(Sender: TObject);
begin
  //
end;

end.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top