2015-02-18 8 views
4

Я пытаюсь создать узел с TButton. Я создаю узел и кнопки, связанные с узлами. В случае TVirtualStringTree.AfterCellPaint, я инициализирую BoundsRect на кнопке. Но кнопка всегда отображается в первом узле.Кнопка внедрения VirtualTreeView в ячейках

У вас есть идея проблемы?

type 
    TNodeData = record 
    TextValue: string; 
    Button: TButton; 
    end; 
    PNodeData = ^TNodeData; 

procedure TForm1.FormCreate(Sender: TObject); 

    procedure AddButton(__Node: PVirtualNode); 
    var 
    NodeData: PNodeData; 
    begin 
    NodeData := VirtualStringTree1.GetNodeData(__Node); 
    NodeData.Button := TButton.Create(nil); 
    with NodeData.Button do 
    begin 
     Parent := VirtualStringTree1; 
     Height := VirtualStringTree1.DefaultNodeHeight; 
     Caption := '+'; 
     Visible := false; 
    end; 
    end; 

    procedure InitializeNodeData(__Node: PVirtualNode; __Text: string); 
    var 
    NodeData: PNodeData; 
    begin 
    NodeData := VirtualStringTree1.GetNodeData(__Node); 
    NodeData.TextValue := __Text; 
    end; 

var 
    Node: PVirtualNode; 
begin 
    VirtualStringTree1.NodeDataSize := SizeOf(TNodeData); 

    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, 'a');  
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, 'a.1'); 

    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, 'b'); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, 'Here the button'); 
    AddButton(Node); 
end; 

procedure TForm1.VirtualStringTree1AfterCellPaint(Sender: TBaseVirtualTree; 
    TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect); 
var 
NodeData: PNodeData; 
begin 
    if (Column = 0) then 
    Exit; 

    NodeData := VirtualStringTree1.GetNodeData(Node); 
    if (Assigned(NodeData)) and (Assigned(NodeData.Button)) then 
    begin 
    with NodeData.Button Do 
    begin 
     Visible := (vsVisible in Node.States) 
       and ((Node.Parent = VirtualStringTree1.RootNode) or (vsExpanded in Node.Parent.States)); 
     BoundsRect := CellRect; 
    end; 
    end; 
end; 

ответ

1

Координаты параметра CellRect в обработчике событий OnAfterCellPaint относительно отрисованного узла. То, что вам нужно, - это абсентская позиция узла внутри дерева. Вы можете получить это, вызвав GetDisplayRect дерева. Так изменить свой код так:

procedure TForm1.VirtualStringTree1AfterCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect); 
 
var 
 
    NodeData: PNodeData; 
 
    R: TRect; 
 
begin 
 
    if (Column = 0) then 
 
    Exit; 
 
    NodeData := VirtualStringTree1.GetNodeData(Node); 
 
    if (Assigned(NodeData)) and (Assigned(NodeData.Button)) then 
 
    begin 
 
    with NodeData.Button Do 
 
    begin 
 
     Visible := (vsVisible in Node.States) 
 
       and ((Node.Parent = VirtualStringTree1.RootNode) or (vsExpanded in Node.Parent.States)); 
 
     R := Sender.GetDisplayRect(Node, Column, False); 
 
     BoundsRect := R; 
 
    end; 
 
    end; 
 
end;

+0

Спасибо, это работа. – r038tmp5

+0

Это работа, но у меня все еще есть проблема. Если я буду расширять своего отца, у меня есть кнопка: ОК. Если я разрушу его отца: кнопка останется видимой – r038tmp5

2

Таким образом, проблема с ответом iamjoosy является - даже если он работает - что, как только вы прокрутить Дерево с нарисованными кнопками/изображения/все, те, которые должны покинуть Дерево снова, все еще существуют, окрашиваются в самое низкое/самое высокое место, где вы их оставили. В зависимости от суммы, которую вы просто прокручиваете, она оставляет в этой колонке меньше или больше путаницы. AfterCellPaint больше не перемещает их, так как ячейки этого теперь невидимого узла ниже дна/над вершиной больше не раскрашены.

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

procedure TMyTree.MyTreeAfterCellPaint(Sender: TBaseVirtualTree; 
    TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; 
    CellRect: TRect); 
var 
    InitialIndex: Integer; 
// onInitNode I AddOrSetValue a "DataIndexList" TDictionary<PVirtualNode, LongInt> 
// to preserve an original index "InitialIndex" (violating the virtual paradigm), 
// because I need it for something else anyways 
    Data: PMyData; 
    ANode: PVirtualNode; 
begin 
    if Node <> nil then 
    begin 
    if Column = 2 then 
    begin 
     ANode := MyTree.GetFirst; 
     while Assigned(ANode) do 
     begin 
     DataIndexList.TryGetValue(ANode, InitialIndex); 
     if not (CheckVisibility(Sender.GetDisplayRect(ANode, Column, False))) then 
     begin 
      MyBtnArray[InitialIndex].Visible := False; 
      MyPanelArray[InitialIndex].Visible := False; 
     end 
     else 
     begin 
      MyBtnArray[InitialIndex].Visible := True; 
      MyPanelArray[InitialIndex].Visible := True; 
     end; 
     ANode := MyTree.GetNext(ANode); 
     end; 
     DataIndexList.TryGetValue(Node, InitialIndex); 
     Data := MyTree.GetNodeData(Node); 
     MyPanelArray[InitialIndex].BoundsRect := Sender.GetDisplayRect(Node, Column, False); 
    end; 
    end; 
end; 

function TMyTree.CheckVisibility(R: TRect): Boolean; 
begin 
// in my case these checks are the way to go, because 
// MyTree is touching the top border of the TForm. You will have 
// to adjust accordingly if your placement is different 
    if (R.Bottom < MyTree.Top) or (R.Bottom > MyTree.Top + MyTree.Height) then 
    Result := False 
    else 
    Result := True; 
end; 

Излишне говорить, что вы можете сделать, проникающего с visibilityCheck внутри многих других OnEvents успешно. Это не должно быть в AfterCellPaint; возможно, еще одно событие может быть намного лучше.

Чтобы создать копии RunTime вашей оригинальной панели + Button, чтобы разместить внутри вашего ButtonArray или любой другой структуры, которую вы используете, вам также придется скопировать RTTI. Эта процедура берется из http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.zip (дополнительной информации RTTI на http://www.blong.com/Conferences/BorConUK98/DelphiRTTI/CB140.htm) и «использует TypInfo»:

procedure CopyObject(ObjFrom, ObjTo: TObject); 
var 
    PropInfos: PPropList; 
    PropInfo: PPropInfo; 
    Count, Loop: Integer; 
    OrdVal: Longint; 
    StrVal: String; 
    FloatVal: Extended; 
    MethodVal: TMethod; 
begin 
    { Iterate thru all published fields and properties of source } 
    { copying them to target } 

    { Find out how many properties we'll be considering } 
    Count := GetPropList(ObjFrom.ClassInfo, tkAny, nil); 
    { Allocate memory to hold their RTTI data } 
    GetMem(PropInfos, Count * SizeOf(PPropInfo)); 
    try 
    { Get hold of the property list in our new buffer } 
    GetPropList(ObjFrom.ClassInfo, tkAny, PropInfos); 
    { Loop through all the selected properties } 
    for Loop := 0 to Count - 1 do 
    begin 
     PropInfo := GetPropInfo(ObjTo.ClassInfo, PropInfos^[Loop]^.Name); 
     { Check the general type of the property } 
     { and read/write it in an appropriate way } 
     case PropInfos^[Loop]^.PropType^.Kind of 
     tkInteger, tkChar, tkEnumeration, 
     tkSet, tkClass{$ifdef Win32}, tkWChar{$endif}: 
     begin 
      OrdVal := GetOrdProp(ObjFrom, PropInfos^[Loop]); 
      if Assigned(PropInfo) then 
      SetOrdProp(ObjTo, PropInfo, OrdVal); 
     end; 
     tkFloat: 
     begin 
      FloatVal := GetFloatProp(ObjFrom, PropInfos^[Loop]); 
      if Assigned(PropInfo) then 
      SetFloatProp(ObjTo, PropInfo, FloatVal); 
     end; 
     {$ifndef DelphiLessThan3} 
     tkWString, 
     {$endif} 
     {$ifdef Win32} 
     tkLString, 
     {$endif} 
     tkString: 
     begin 
      { Avoid copying 'Name' - components must have unique names } 
      if UpperCase(PropInfos^[Loop]^.Name) = 'NAME' then 
      Continue; 
      StrVal := GetStrProp(ObjFrom, PropInfos^[Loop]); 
      if Assigned(PropInfo) then 
      SetStrProp(ObjTo, PropInfo, StrVal); 
     end; 
     tkMethod: 
     begin 
      MethodVal := GetMethodProp(ObjFrom, PropInfos^[Loop]); 
      if Assigned(PropInfo) then 
      SetMethodProp(ObjTo, PropInfo, MethodVal); 
     end 
     end 
    end 
    finally 
    FreeMem(PropInfos, Count * SizeOf(PPropInfo)); 
    end; 
end; 

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

function TFoo.IsNodeVisibleInClientRect(Node: PVirtualNode; Column: TColumnIndex = NoColumn): Boolean; 
begin 
    Result := VST.IsVisible[Node] and 
    VST.GetDisplayRect(Node, Column, False).IntersectsWith(VST.ClientRect); 
end; 
2

Я написал небольшую программу для создания любого элемента управления для узла. Я узнал, что лучшее место для установки узлов контролирует видимость в событии OnAfterPaint. Прокрутка работает по назначению, и почти нулевое мерцание.

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, VirtualTrees, StdCtrls, Buttons, ExtCtrls; 

type 
    TForm1 = class(TForm) 
    VirtualStringTree1: TVirtualStringTree; 
    procedure FormCreate(Sender: TObject);    
    procedure VirtualStringTree1GetText(Sender: TBaseVirtualTree; 
     Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; 
     var CellText: WideString); 
    procedure VirtualStringTree1AfterPaint(Sender: TBaseVirtualTree; 
     TargetCanvas: TCanvas); 
    procedure VirtualStringTree1MeasureItem(Sender: TBaseVirtualTree; 
     TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); 
    private 
    procedure SetNodesControlVisibleProc(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean); 
    procedure SetNodeControlVisible(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex = NoColumn); 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

type 
    TNodeData = record 
    Text: WideString; 
    Control: TControl; 
    end; 
    PNodeData = ^TNodeData; 

{ Utility } 
function IsNodeVisibleInClientRect(Tree: TBaseVirtualTree; Node: PVirtualNode; 
    Column: TColumnIndex = NoColumn): Boolean; 
var 
    OutRect: TRect; 
begin 
    Result := Tree.IsVisible[Node] and 
    Windows.IntersectRect(OutRect, Tree.GetDisplayRect(Node, Column, False), Tree.ClientRect); 
end; 

type 
    TControlClass = class of TControl; 

    TMyPanel = class(TPanel) 
    public 
    CheckBox: TCheckBox; 
    end; 

{ TForm1 } 
procedure TForm1.FormCreate(Sender: TObject); 

    function CreateNodeControl(Tree: TVirtualStringTree; Node: PVirtualNode; ControlClass: TControlClass): TControl; 
    var 
    NodeData: PNodeData; 
    begin 
    NodeData := Tree.GetNodeData(Node); 
    NodeData.Control := ControlClass.Create(nil); 
    with NodeData.Control do 
    begin 
     Parent := Tree; // Parent will destroy the control 
     Height := Tree.DefaultNodeHeight; 
     Visible := False; 
    end; 
    Tree.IsDisabled[Node] := True; 
    Result := NodeData.Control; 
    end; 

    procedure InitializeNodeData(Node: PVirtualNode; const Text: WideString); 
    var 
    NodeData: PNodeData; 
    begin 
    NodeData := VirtualStringTree1.GetNodeData(Node); 
    Initialize(NodeData^); 
    NodeData.Text := Text; 
    end; 

var 
    Node: PVirtualNode; 
    MyPanel: TMyPanel; 
    I: integer; 
begin 
    VirtualStringTree1.NodeDataSize := SizeOf(TNodeData); 
    // trigger MeasureItem 
    VirtualStringTree1.TreeOptions.MiscOptions := VirtualStringTree1.TreeOptions.MiscOptions + [toVariableNodeHeight]; 

    // Populate some nodes  
    for I := 1 to 5 do begin 
    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, Format('%d', [I])); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, Format('%d.1', [I])); 
    end; 

    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, '[TSpeedButton Parent]'); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, 'TSpeedButton'); 
    TSpeedButton(CreateNodeControl(VirtualStringTree1, Node, TSpeedButton)).Caption := '+'; 

    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, '[TEdit Parent]'); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, 'TEdit'); 
    TEdit(CreateNodeControl(VirtualStringTree1, Node, TEdit)).Text := 'Hello'; 

    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, '[TMyPanel Parent]'); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, 'TMyPanel'); 
    MyPanel := TMyPanel(CreateNodeControl(VirtualStringTree1, Node, TMyPanel)); 
    with MyPanel do 
    begin 
    Caption := 'TMyPanel'; 
    ParentBackground := False; 
    CheckBox := TCheckBox.Create(nil); 
    CheckBox.Caption := 'CheckBox'; 
    CheckBox.Left := 10; 
    CheckBox.Top := 10; 
    CheckBox.Parent := MyPanel; 
    end; 

    for I := 6 to 10 do begin 
    Node := VirtualStringTree1.AddChild(nil); 
    InitializeNodeData(Node, Format('%d', [I])); 
    Node := VirtualStringTree1.AddChild(Node); 
    InitializeNodeData(Node, Format('%d.1', [I])); 
    end; 
end; 

procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree; 
    Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; 
    var CellText: WideString); 
var 
    NodeData: PNodeData; 
begin 
    NodeData := Sender.GetNodeData(Node); 
    if Assigned(NodeData) then 
    CellText := NodeData.Text; 
end; 

procedure TForm1.SetNodeControlVisible(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex = NoColumn); 
var 
    NodeData: PNodeData; 
    R: TRect; 
begin 
    NodeData := Tree.GetNodeData(Node); 
    if Assigned(NodeData) and Assigned(NodeData.Control) then 
    begin 
    with NodeData.Control do 
    begin 
     Visible := IsNodeVisibleInClientRect(Tree, Node, Column) 
       and ((Node.Parent = Tree.RootNode) or (vsExpanded in Node.Parent.States)); 
     R := Tree.GetDisplayRect(Node, Column, False); 
     BoundsRect := R; 
    end; 
    end; 
end; 

procedure TForm1.SetNodesControlVisibleProc(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean); 
begin 
    SetNodeControlVisible(Sender, Node); 
end; 

procedure TForm1.VirtualStringTree1AfterPaint(Sender: TBaseVirtualTree; 
    TargetCanvas: TCanvas); 
begin 
    // Iterate all Tree nodes and set visibility 
    Sender.IterateSubtree(nil, SetNodesControlVisibleProc, nil); 
end; 

procedure TForm1.VirtualStringTree1MeasureItem(Sender: TBaseVirtualTree; 
    TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); 
var 
    NodeData: PNodeData; 
begin 
    NodeData := Sender.GetNodeData(Node); 
    if Assigned(NodeData) and Assigned(NodeData.Control) then 
    // set node special height if control is TMyPanel 
    if NodeData.Control is TMyPanel then 
     NodeHeight := 50; 
end; 

end. 

ДФМ:

object Form1: TForm1 
    Left = 192 
    Top = 124 
    Width = 782 
    Height = 365 
    Caption = 'Form1' 
    Color = clBtnFace 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Style = [] 
    OldCreateOrder = False 
    OnCreate = FormCreate 
    DesignSize = (
    766 
    327) 
    PixelsPerInch = 96 
    TextHeight = 13 
    object VirtualStringTree1: TVirtualStringTree 
    Left = 8 
    Top = 8 
    Width = 450 
    Height = 277 
    Anchors = [akLeft, akTop, akRight, akBottom] 
    Header.AutoSizeIndex = 0 
    Header.Font.Charset = DEFAULT_CHARSET 
    Header.Font.Color = clWindowText 
    Header.Font.Height = -11 
    Header.Font.Name = 'MS Sans Serif' 
    Header.Font.Style = [] 
    Header.MainColumn = -1 
    TabOrder = 0 
    OnAfterPaint = VirtualStringTree1AfterPaint 
    OnGetText = VirtualStringTree1GetText 
    OnMeasureItem = VirtualStringTree1MeasureItem 
    Columns = <> 
    end 
end 

Выход:

Output

Испытано с Delphi 7, ВТ версия 5.3.0, Окно s 7