Невозможно выбрать корневой узел в представлении виртуального дерева - PullRequest
0 голосов
/ 23 сентября 2019

Я использую Delphi XE3 с Virtual Tree View.

Мои коды указаны ниже:

type
  TMyData = record
    Caption: string;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  RootNode: PVirtualNode;
  PData: ^TMyData;
begin
  RootNode := tvItems.AddChild(nil);
  PData := tvItems.GetNodeData(RootNode);
  PData^.Caption := 'This is a test node';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  tvItems.NodeDataSize := SizeOf(TMyData);
end;

procedure TForm1.tvItemsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  PData: ^TMyData;
begin
  if Assigned(Node) then
  begin
    PData := tvItems.GetNodeData(Node);

    if Assigned(PData) then
      Celltext := PData^.Caption;
  end;
end;

Когда я нажимаю «Button1», будет создан корневой узел.Однако, когда моя мышь щелкает текст узла, он не будет выделен.

Некоторые из моих выводов:

  1. Необходимо щелкнуть в начале текста узла, чтобывыберите узел.Если щелкнуть в середине или в конце текста узла, то узел не будет выбран.

  2. Если я изменю tvItemsGetText на ниже, проблема исчезнет:

    procedure TForm1.tvItemsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    var
      PData: ^TMyData;
    begin
      CellText := 'This is a test node';
    end;

Я установил точку останова в tvItemsGetText и обнаружил, что она будет вызываться несколько раз.В первые несколько раз PData будет нулевым, что делает CellText пустым.При последнем вызове PData станет действительным, и для CellText будет установлено значение «Это тестовый узел».

Кажется, что диапазон, который позволяет щелкать мышью и выбирать узел, определяется исходными текстами.узла.Если исходный текст является пустой строкой, то для его выбора необходимо щелкнуть в самом начале узла.

Это ошибка Virtual Tree View?

1 Ответ

1 голос
/ 24 сентября 2019

Существует несколько способов инициировать новый узел по пользовательским данным.

1.Использование OnInitNode события:

procedure TForm5.Button1Click(Sender: TObject);
begin
  vt1.InsertNode(nil, amAddChildLast); // internal calls vt1InitNode
end;

procedure TForm5.vt1InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  var InitialStates: TVirtualNodeInitStates);
var
  PData: ^TMyData;
begin
  PData := Sender.GetNodeData(Node);
  PData^.Caption := 'This is a test node';
end;

2 Использование UserData param

Вариант 1. Динамические данные

Не забудьте удалить событие InitNode и не устанавливатьNodeDataSize свойство

type
  TMyData = record
    Caption: string;
  end;
  PMyData = ^TMyData;

procedure TForm5.Button1Click(Sender: TObject);
var
  p: PMyData;
begin
  New(p);
  p.Caption:='This is a test node'; 
  vt1.InsertNode(nil, amAddChildLast, p); // create node with initialized user data
  // by default VirtualTree use NodeDataSize = SizeOf(pointer), 
  // so there is no reason to use GetNodeDataSize event 
end;


procedure TForm5.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
  Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var
  PData: PMyData;
begin
  if Assigned(Node) then
  begin
    PData := PMyData(Sender.GetNodeData(Node)^); // little modification
    // for correct access to dynamic node data

    if Assigned(PData) then
      CellText := PData.Caption;
  end;
end;

procedure TForm5.vt1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  p: PMyData;
begin
  p:=PMyData(Sender.GetNodeData(Node)^);
  Dispose(p); // as you allocate memory for user data - you should free it to avoid memory leaks
end;

Вариант 2. Объекты

Добавление новой закрытой функции к вашей форме:

  private
    { Private declarations }
    function GetObjectByNode<T: class>(Node: PVirtualNode): T; 
    // do not forget to include System.Generics.Collections to `uses`

реализация:

function TForm5.GetObjectByNode<T>(Node: PVirtualNode): T;
var
  NodeData: Pointer;
  tmpObject: TObject;
begin
  Result := nil;
  if not Assigned(Node) then
    exit;
  NodeData := vt1.GetNodeData(Node);
  if Assigned(NodeData) then
    tmpObject := TObject(NodeData^);

  if tmpObject is T then
    Result := T(tmpObject)
  else
    Result := nil;
end;

И основной код (практически идентичный варианту 1):

procedure TForm5.Button1Click(Sender: TObject);
var
  d: TMyData;
begin
  d := TMyData.Create;
  d.Caption := 'This is a test node';
  vt1.InsertNode(nil, amAddChildLast, d);
end;

procedure TForm5.vt1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  d: TMyData;
begin
  d := GetObjectByNode<TMyData>(Node);
  d.Free;
end;

procedure TForm5.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
  Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var
  d: TMyData;
begin
  d := GetObjectByNode<TMyData>(Node);
  if Assigned(d) then
    CellText := d.Caption;
end;
...