3

[これは以前に投稿された質問の更新バージョンです。前のタイトルはDelphiの仮想ツリービューのインデックスによるノードの選択でした。]

1日の大部分を終えると、Virtual Treeviewコンポーネント(強力ですが複雑)が単純な2つのテーブルのデータ対応方式で機能するようになったと思います。

ここで、最上位ノードの1,512番目(たとえば)を単純に選択しようとしています。最初のトップレベルノードを取得してから、ループでGetNextSibling 1,511を呼び出す以外に、これを行う方法はわかりません。

これは不必要に関係しているようです。もっと簡単な方法はありますか?

アップデート

ツリー内のノードを初期化するにはデータベースアクセスが必要なため、起動時にすべてのノードを初期化することはできません。ユーザーがレコードがまだ選択されていないフォームから開始する場合、それは問題ありません。ユーザーがツリーをスクロールすると、現在のウィンドウをツリーに表示するのに十分なノードが作成され、パフォーマンスは良好です。

ユーザーがデータベースレコードを選択した状態でダイアログモードでフォームを開始する場合、ユーザーがフォームを表示する前に、ツリーをそのノードに進める必要があります。レコードがツリーの終わりに向かっている場合、最初のノードからツリーを歩くのに10秒かかる可能性があるため、これは問題です。GetNextSibling()を実行できるたびに、ノードの大部分がユーザーに表示されていなくても、ノードが初期化されます。これらのノードの初期化を、ユーザーに表示されるようになるまで延期したいと思います。

レコードを選択せず​​にツリーを開き、垂直スクロールバーを使用して、1回の操作でツリーの中央に移動すると、初期化せずに正しいノードが表示されるため、より良い方法があるはずです。スキップしたノード

これは、レコードを選択してツリーを開いたときに実現したい効果です。移動したいノードのインデックスはわかっていますが、インデックスでそこに到達できない場合は、いくつかのノードを前後にジャンプできると仮定して、ツリーでバイナリ検索を実行できます(直接スクロールして木の真ん中)。

あるいは、グリッドをトラバースするときに中間ノードを初期化しないままにする、ツリービューに作成できる状態設定があるかもしれません。Begin / End Updateを試しましたが、うまくいかないようです。

4

3 に答える 3

3

ツリー コントロールは、コンピューター サイエンスのクラスで学習する従来のツリーと同じように構成されています。ツリーのルートから 1512 番目の子にたどり着く唯一の方法は、リンクを 1 つずつたどることです。自分で行う場合でも、ツリー コントロールのメソッドを使用する場合でも、その方法で行う必要があります。コントロール自体には何も提供されていないので、この関数を使用できます。

function GetNthNextSibling(Node: PVirtualNode; N: Cardinal;
  Tree: TBaseVirtualTree = nil): PVirtualNode;
begin
  if not Assigned(Tree) then
    Tree := TreeFromNode(Node);
  Result := Node;
  while Assigned(Result) and (N > 0) do begin
    Dec(N);
    Result := Tree.GetNextSibling(Result);
  end;
end;

それを頻繁に行う場合は、自分自身をインデックスにすることをお勧めします。ポインターの配列を作成し、そこにすべての最上位の値を格納するのと同じくらい簡単なPVirtualNode場合があるため、そこから 1512 番目の値を読み取るだけです。ツリー コントロールは、そのようなデータ構造自体を必要としないため、維持しません。

そのようなデータ構造が必要かどうかを再検討することもできます。そのようなインデックスでノードにアクセスする必要は本当にありますか? または、代わりにPVirtualNodeポインターを維持して、ツリー内の残りのノードに対する相対的な位置が問題にならないようにすることもできます (たとえば、必要なノードへの参照を失うことなくそれらを並べ替えることができます)。

于 2010-02-07T00:56:42.560 に答える
3

初期化せずにノードの兄弟を取得するには、NextSiblingポインタを使用します ( の宣言を参照TVirtualNode)。

于 2010-02-12T13:46:07.487 に答える
0

更新に次のように記述します。

レコードを選択せず​​にツリーを開き、垂直スクロール バーを使用して 1 回の操作でツリーの中央に移動すると、初期化せずに正しいノードが表示されるため、より良い方法があるはずです。スキップしたノード

ここには違いがあります。これは、垂直スクロールによって、クライアント位置 0 に表示される論理 Y 座標が変更されるためです。コントロールは、スクロールバーの位置とスクロール範囲からオフセットを計算し、コントロールの上部に表示されるノードを計算します。ノードは、スクロールして表示された領域をペイントする必要がある場合にのみ、再度初期化されます。

ノードの Y 座標がある場合は、呼び出してノード ポインターを取得できます。

function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean;
  var NodeTop: Integer): PVirtualNode;

ノードの Y 座標は、以前に表示されたすべてのノードの高さの合計です。折りたたまれたノードがなく (つまり、レコードのフラットなリストであるか、子ノードを持つすべてのノードが展開されている)、それらすべてがデフォルトの高さを持っていると仮定すると、これは簡単です。このコードは良い出発点になるはずです:

procedure TForm1.SelectTreeNode(AIndex: integer; ACenterNodeInTree: boolean);
var
  Y, Dummy: integer;
  Node: PVirtualNode;
begin
  Y := Round((AIndex + 0.5) * VirtualStringTree1.DefaultNodeHeight);
  Node := VirtualStringTree1.GetNodeAt(0, Y, False, Dummy);
  if Node <> nil then begin
    Assert(Node.Index = AIndex);
    VirtualStringTree1.ScrollIntoView(Node, ACenterNodeInTree);
    VirtualStringTree1.Selected[Node] := True;
    VirtualStringTree1.FocusedNode := Node;
  end;
end;
于 2010-02-11T16:32:08.210 に答える