วิธีคัดลอกส่วนหนึ่งของ Treeview ไปยังเมนู

ฉันกำลังพยายามคัดลอกส่วนหนึ่งของ Treeview ไปยังเมนูป๊อปอัป และไม่มีโชคเลย ดูเหมือนว่าฉันไม่สามารถรับการเรียกซ้ำเพื่อทำงานได้ และฉันรู้ว่าฉันอาจทำผิดทั้งหมด

ใช้ภาพตัวอย่างนี้ (ซึ่งเป็นภาพหน้าจอรันไทม์จากโค้ดด้านล่าง):

ป้อนคำอธิบายรูปภาพที่นี่

ฉันต้องการให้สร้างเมนูที่มีความสัมพันธ์เดียวกันกับ Treeview แต่ฉันไม่ต้องการให้เพิ่มรายการ Root นี่คือสิ่งที่ฉันต้องการให้มันมีลักษณะเช่นนี้:

ป้อนคำอธิบายรูปภาพที่นี่

โปรดทราบว่ารายการแรกไม่ใช่ไอคอนการตั้งค่า (รูท) และอยู่ในระดับเดียวกับ Treeview

นี่คือรหัสที่ฉันมี:

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  Menus, StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ImageList1: TImageList;
    MenuItem1: TMenuItem;
    PopupMenu1: TPopupMenu;
    TreeView1: TTreeView;
    procedure MyMenuItemClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    procedure TreeViewToMenu(TreeView: TTreeView; BaseNode: TTreeNode; OutMenu: TMenu);
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.MyMenuItemClick(Sender: TObject);
begin
  ShowMessage('You selected ' + TMenuItem(Sender).Name + ' - Tag: ' +
    IntToStr(TMenuItem(Sender).Tag));
end;

procedure TForm1.TreeViewToMenu(TreeView: TTreeView; BaseNode: TTreeNode; OutMenu: TMenu);
var
  I: Integer;
  MenuItem: TMenuItem;
begin
  MenuItem := TMenuItem.Create(nil);
  with MenuItem do
  begin
    Caption := BaseNode.Text;
    ImageIndex := BaseNode.ImageIndex;
    OnClick := @MyMenuItemClick;
  end;

  for I := 0 to BaseNode.Count - 1 do
  begin
    MenuItem.Tag := I;
    TreeViewToMenu(TreeView, BaseNode[I], OutMenu);
  end;

  OutMenu.Items.Add(MenuItem);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Pt: TPoint;
  I: Integer;
  Node: TTreeNode;
begin
  Pt.X := Button1.Left + 1;
  Pt.Y := Button1.Top + Button1.Height + 1;
  Pt := ClientToScreen(Pt);

  PopupMenu1.Items.Clear;
  TreeViewToMenu(TreeView1, TreeView1.Items[0], PopupMenu1);

  PopupMenu1.Popup(Pt.X, Pt.Y);
end;

end.

ฉันกำลังพยายามเพิ่มคุณสมบัติ MenuItem Tag เพื่อให้ฉันสามารถระบุแต่ละรายการเมนูด้วยแท็ก

ฉันคิดว่าโดยทั่วไปแล้วการเรียกซ้ำหมายถึงการเรียกโพรซีเดอร์อีกครั้งจากภายในโพรซีเดอร์ ดังนั้นมันจึงเกิดซ้ำเอง ไม่ว่าจะด้วยวิธีใดฉันก็สามารถช่วยได้จริงๆ

ขอบคุณ.


person Community    schedule 14.09.2013    source แหล่งที่มา


คำตอบ (2)


ไม่มีปัญหากับความเข้าใจของคุณเกี่ยวกับการเรียกซ้ำ แต่คุณไม่ต้องการผนวกรายการสำหรับโหนดรูท ดังนั้นคุณควรเพิ่มรายการและเรียกซ้ำสำหรับแต่ละลูกของโหนดใดๆ ที่ถูกส่งผ่านไปยังขั้นตอน ต่อไปนี้เป็นตัวอย่างการใช้งาน:

type
  TForm1 = class(TForm)
    ..
  private
    procedure TreeViewToMenu(BaseNode: TTreeNode; OutMenu: TComponent);
    ..

procedure TForm1.TreeViewToMenu(BaseNode: TTreeNode; OutMenu: TComponent);
var
  i: Integer;
  Node: TTreeNode;
  MenuItem: TMenuItem;
begin
  for i := 0 to BaseNode.Count - 1 do begin
    Node := BaseNode.Item[i];

    MenuItem := TMenuItem.Create(nil);
    MenuItem.Caption := Node.Text;
    MenuItem.ImageIndex := Node.ImageIndex;
    MenuItem.Tag := i;
    if Node.Count = 0 then
      MenuItem.OnClick := MyMenuItemClick;

    if OutMenu is TPopupMenu then
      TMenu(OutMenu).Items.Add(MenuItem)
    else if
      OutMenu is TMenuItem then
        TMenuItem(OutMenu).Add(MenuItem)
      else
        raise Exception.Create('Invalid class type');

    TreeViewToMenu(Node, MenuItem);

  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ..
begin
  ..
  TreeViewToMenu(TreeView1.Items[0], PopupMenu1);
  ..

โปรดทราบว่าฉันเปลี่ยนการประกาศ TreeViewToMenu สำหรับ (1) ไม่ได้ใช้ TreeView และ (2) เรากำลังต่อท้ายรายการเข้ากับ TPopupMenu หรือ TMenuItem ดังนั้นฉันจึงประกาศ 'OutMenu' เป็น TComponent ซึ่งจะยอมรับทั้งสองอย่าง

person Sertac Akyuz    schedule 14.09.2013
comment
เมื่อทดลองสร้าง TMenuItems ตอนรันไทม์ ฉันพบปัญหาร้ายแรงเมื่อเมนูมีขนาดใหญ่ขึ้น การเรียก Add แต่ละครั้งทำให้เกิดการสร้างเมนูใหม่ทั้งหมด วิธีแก้ปัญหาด้วยตัวช่วยคลาสเป็นไปได้โดยตั้งค่า ComponentState = [csLoading] หลีกเลี่ยงการสร้างรายการเมนูใหม่หลายร้อยรายการ ต้นไม้ที่มีไอเท็ม 100 รายการจะช้ากว่าต้นไม้ที่มีไอเท็ม 100 เท่าในการสร้าง และอื่นๆ O(N^2) - person Warren P; 14.09.2013
comment
@Warren - ขอบคุณสำหรับสิ่งนั้น ฉันเคยมีปัญหาที่คล้ายกัน - ถ้าฉันจำได้ว่าถูกต้องซึ่งเกิดจาก RethinkHotkeys - ซึ่งฉันไม่สามารถหาทางแก้ไขได้และเปลี่ยนการออกแบบ - person Sertac Akyuz; 14.09.2013
comment
+1 มันตรงกับสถานการณ์ของ OP ทุกประการ แต่จะใช้ได้ก็ต่อเมื่อมีรายการรูทหลักนั้นเท่านั้น - person NGLN; 14.09.2013
comment
@NGLN - แน่นอนว่ามันใช้งานได้กับโหนดใด ๆ แต่จะไม่เพิ่มโหนดนั้น แต่เป็นลูก ๆ ของมันเป็นต้น ขอขอบคุณและ +1 สำหรับคำตอบของคุณอย่างน้อยก็เตือนฉันว่า TPopupMenu.Items จริงๆ แล้วเป็น TMenuItem จริงๆ แล้วฉันไม่จำเป็นต้องเปลี่ยนการประกาศ ฉันจะไม่แก้ไขคำตอบ เนื่องจากคุณแสดงให้เห็นว่าควรทำอย่างไร - person Sertac Akyuz; 15.09.2013
comment
@ Sertac +1 ในเรื่อง TMenuItem แต่ขออภัยที่ pernickety: เมื่อไม่มีรายการรูท Items[0].Count = 0 (Items[0].HasChildren = False) และลูปของคุณจะไม่ทำงาน ฉันรู้ว่าฉันลองแล้ว เพราะฉันลืมรายการรูทนั้นในโค้ดทดสอบของตัวเอง - person NGLN; 15.09.2013
comment
ขอขอบคุณที่อธิบายการเปลี่ยนแปลง ซึ่งใช้งานได้ดีมาก และ +1 สำหรับทางเลือกอื่นจาก @NGLN - person ; 15.09.2013
comment
@NGLN - นั่นคือสิ่งที่ฉันพูด ;) - .. แต่มันไม่ได้เพิ่มโหนดนั้น แต่ลูกของมันและอื่น ๆ หากไม่มีลูกก็จะไม่เพิ่มอะไรเลย ฉันไม่ได้ขัดแย้งกับคุณ ฉันพยายามบอกว่าคุณสามารถผ่านโหนดใดก็ได้เพื่อให้ลูก ๆ ของมันบรรจุอยู่ในเมนู - person Sertac Akyuz; 15.09.2013

เช่นเดียวกับที่ Sertac กล่าว คุณกำลังเพิ่มรายการเมนูทั้งหมดไปที่รากของ PopupMenu คุณควรเพิ่มรายการเมนูย่อยลงในรายการเมนูสุดท้ายที่สร้างขึ้น

ขอเป็นแนวทางอื่นโดยใช้ TTreeNode.GetFirstChild และ .GetNextSibling:

procedure TForm1.TreeViewToMenu(Node: TTreeNode; Menu: TMenuItem);
var
  MenuItem: TMenuItem;
begin
  while Node <> nil do
  begin
    MenuItem := TMenuItem.Create(nil);
    MenuItem.Caption := Node.Text;
    MenuItem.ImageIndex := Node.ImageIndex;
    Menu.Add(MenuItem);
    if Node.HasChildren then
      TreeViewToMenu(Node.GetFirstChild, MenuItem)
    else
      MenuItem.OnClick := MyMenuItemClick;
    Node := Node.GetNextSibling;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  PopupMenu1.Items.Clear;
  TreeViewToMenu(TreeView1.Items[1], PopupMenu1.Items);
end;

โปรดทราบว่ารูทีนเริ่มต้นที่นี่ด้วยดัชนีรายการ 1 ซึ่งเป็นรายการย่อยแรกของรายการรูทของคุณ เมื่อไม่มีรายการรูท ให้เริ่มต้นด้วยดัชนี 0

person NGLN    schedule 14.09.2013