2

私のツリーには2つのレベルのノードがあります-それは連絡先リストスタイルのツリーです。

私の問題は、すべての「連絡先カテゴリ」ですべての連絡先をチェックしてもらいたいということです。これが現在の連絡先リストのスクリーンショットです(はい、投稿する権限があります)

連絡先リスト

ご覧のとおり、Todd Hirschはカテゴリテストカテゴリでチェックされていますが、すべての連絡先ではチェックされていません。私が達成しようとしているのは、連絡先にすべてのカテゴリで同じチェックステータスを持たせることです。

例:テストカテゴリでToddHirschをチェックします-ToddHirschはすべての連絡先(および他のすべてのカテゴリ)で自動的にチェックされます。すべての連絡先でToddHirschをチェックすると、テストカテゴリでもチェックされます。すべての連絡先でToddHirschのチェックを外すと、テストカテゴリでもチェックが外されます。

VirtualStringtreeのOnCheckingイベントを介して、ツリー内の各ノードのツリー全体をループして試してみましたが、連絡先リストが大きい場合(2000以上)は非常に遅く、5000以上の場合はさらに遅くなる可能性があります。プログラムをクラッシュさせます(アプリケーションが動作を停止しました)

何を指示してるんですか?

これは、連絡先が1回だけチェックされるようにするために使用するコードです。(それは私が今望んでいるものではありませんが、私が今使っているものです。)

////////////////////////////////////////////////////////////////////////////////
/// HasDuplicateChecked
////////////////////////////////////////////////////////////////////////////////
Function HasDuplicateChecked(Node: PVirtualNode): PVirtualNode;
Var
  ParentNode, ChildNode: PVirtualNode;
  I, J: Integer;
Begin

  // IHCW
  Result := Nil;

  // Get the first node of the tree..
  ParentNode := VT.GetFirst;

  // Loop thru the parent nodes.
  for I := 0 to VT.RootNodeCount - 1 do
  begin
    // Get the first child node.
    ChildNode := ParentNode.FirstChild;
    // Loop thru the children..
    for J := 0 to ParentNode.ChildCount - 1 do
    begin
      // If the ChildNode is checked...
      if NodeIsChecked(ChildNode) then
        // And it is NOT the passed node..
        if ChildNode <> Node then
          // but the data matches..
          if GetData(ChildNode).SkypeID = GetData(Node).SkypeID then
          begin
            // Then pass the Childnode as a result, and EXIT!
            Result := ChildNode;
            Exit;
          end;
      // Next child..
      ChildNode := ChildNode.NextSibling;
    end;
    // Next parent...
    ParentNode := ParentNode.NextSibling;
  end;

End;


////////////////////////////////////////////////////////////////////////////////
/// vtSkypeChecking
////////////////////////////////////////////////////////////////////////////////
procedure TSkypeListEventHandler.vtSkypeChecking(Sender: TBaseVirtualTree;
  Node: PVirtualNode; var NewState: TCheckState; var Allowed: Boolean);
Var
  Level: Integer;
  I: Integer;
  Child: PVirtualNode;
begin
  // Allow the checking..
  Allowed := True;
  // Get the Level..
  Level := Sender.GetNodeLevel(Node);

  // If the level is 0 (Category Level)
  if Level = 0 then
  begin
    // And if the Node's Childcount is more than 0
    if Node.ChildCount > 0 then
    Begin
      // Get the first child..
      Child := Node.FirstChild;
      // Loop thru the children..
      for I := 0 to Node.ChildCount - 1 do
      begin
        // Set the checkstate, and go next..
        Child.CheckState := NewState;
        Child := Child.NextSibling;
      end;
    End;
  end;


  // If the level is 1 (User Level)
  if Level = 1 then
  begin
    // and if the Node's parent is not Nil..
    if Node.Parent <> nil then
    begin
      // aaand, if the new state is Unchecked...
      if (NewState = csUncheckedNormal) or (NewState = csUncheckedPressed) then
      begin
        // .. and if the node checkstate is checked..
        if NodeIsChecked(Node) then
        Begin
          // Set the PARENT node's checkstate to Unchecked!
          Node.Parent.CheckState := csUncheckedNormal;
        End;

      end;
      // BUT, if there is a DUPLICATE of the node, screw the above, and
      // forbid the checking!
      if HasDuplicateChecked(Node) <> nil then
        Allowed := False;

    end;
  end;

  // Uncheck all the duplicates.
  UncheckDuplicates;

  // Refresh the Tree
  Sender.Refresh;

end;
4

3 に答える 3

5

まず、OnChecking処理するのが間違ったイベントです。あなたが欲しいOnCheckedOnChecking「このノードのチェック状態は変更できますか?」と尋ねるだけです。立ち去って他のノードをチェックすることを意図したものではありません。そのために使用OnCheckedします。

次に、カテゴリノードのチェック状態を処理する必要はありません。オプションをオンにtoAutoTristateTrackingすると、コントロールは関連するすべての子ノードと親ノードの状態を自動的に調整します。(親を変更すると、すべての子が変更されます。子を変更すると、親が「不確定」に変更されます。)

ただし、それ以外の点では、コードは正しい方向に進んでいるようです。子ノードが変更された場合、ツリーの残りの部分でそのノードの他のすべてのコピーを検索し、変更されたばかりのノードの新しい状態と一致するようにチェック状態を変更する必要があります。その操作の実行にかかる時間は、ツリー内のノード数に比例する必要があります。ノード数の2倍であり、すべての重複を見つけるのに約2倍の時間がかかるはずです。しかし、数千のノードがあっても、瞬く間に終了するはずです。時間がかかる場合は、ここに示していない他の時間のかかる操作があります。プロファイラーを使用してボトルネックを見つけてみてください。

以下のコードは、ツリー内のすべてのノードを1回トラバースします。イベントハンドラーを一時的に無効にします。OnCheckedそうしないと、重複の1つの状態が変更されるたびに、イベントが再度実行されるためです。新しいチェック状態が現在のチェック状態と同じである場合、イベントは実行されないため、無限再帰の危険はありませんが、イベントを無効にすると、ツリーを介して多くの冗長なトラバーサルを実行できなくなります。

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  TargetID: string;
  Parent: PVirtualNode;
  FoundOne: Boolean;
begin
  Data := Tree.GetNodeData(Node);
  TargetID := Data.SkypeID;

  Parent := Tree.GetFirst;
  while Assigned(Parent) do begin
    // Assume no user appears twice in the same category
    if Parent <> Tree.NodeParent[Node] then begin
      FoundOne := False;
      Child := Tree.GetFirstChild(Parent);
      while Assigned(Child) and not FoundOne do begin
        Data := Tree.GetNodeData(Child);
        if Data.SkypeID = TargetID then begin
          // Found a duplicate. Sync it with Node.
          Tree.CheckState[Child] := Tree.CheckState[Node];
          FoundOne := True;
        end;
        Child := Tree.GetNextSibling(Child);
      end;
    end;
    Parent := Tree.GetNextSibling(Parent);
  end;
end;

procedure TSkypeListEventHandler.vtSkypeChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  CheckedEvent: TVTChangeEvent;
begin
  if Sender.GetNodeLevel(Node) = 0 then
    exit; // The tree cascades changes automatically

  Assert(Sender.GetNodeLevel(Node) = 1, 'Unexpected node level');
  // We'll be accessing members that are protected in TBaseVirtualTree, but
  // they're public in TVirtualStringTree, so make sure we're still operating
  // on the same tree.
  Assert(Sender = vtSkype);

  CheckedEvent := vtSkype.OnChecked;
  vtSkype.OnChecked := nil;
  try
    PropagateCheckState(vtSkype, Node);
  finally
    vtSkype.OnChecked := CheckedEvent;
  end;
end;

データ構造に特定のユーザーIDに関連付けられたすべてのノードのリストがある場合、それははるかに簡単になります。

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  i: Integer;
begin
  Data := Tree.GetNodeData(Node);

  for i := 0 to Pred(Data.User.Nodes.Count) do
    Tree.CheckState[Data.User.Nodes[i]] := Tree.CheckState[Node];
end;

すべてのデータをツリーコントロール自体に保存し続ける場合でも(何度もアドバイスされていますが、これは悪い考えです)、セカンダリデータ構造を使用して、ユーザーをキーオフしたツリーノードのインデックスとして機能させることができます。 ID。十分に新しいDelphiバージョンがある場合は、を使用できますTDictionary<string, TList<PVirtualNode>>。次にPropagateCheckState、次のようになります。

uses Generics.Collections;

var
  UserNodes: TDictionary<string, TList<PVirtualNode>>;

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  Nodes: TList<PVirtualNode>;
  i: Integer;
begin
  Data := Tree.GetNodeData(Node);

  if not UserNodes.TryGetValue(Data.SkypeID, Nodes) then
    exit; // Weird. The node's ID isn't in the index at all.

  for i := 0 to Pred(Nodes.Count) do
    Tree.CheckState[Nodes[i]] := Tree.CheckState[Node];
end;

UserNodesカテゴリ内のユーザーを追加または削除するときは、必ずインデックスを更新してください。

于 2011-04-08T18:05:56.847 に答える
0

簡単にするために、TreeViewがエントリを作成するために使用するクラスに「Todd」が含まれていると仮定します。TreeViewがそのクラスからの情報を要求している限り、クラス自体にブールチェックを追加してツリービューを無効にすることで回避できます。
ツリーがそれ自体を再描画するとき、それに応じてチェックボックスを設定するためにクラスを使用します。

VirtualTreeViewは、一瞬で数千のエントリに対してこれを実行するのに十分な速度であることを私は知っています。

于 2011-04-08T14:38:48.497 に答える
0

すべてのノードをトラバースするには、(C ++コードの言い訳)のような関数GetNext(PVirtualNode、bool childs)が役立つ場合があります。

TVirtualNode* parent=NULL;
TVirtualNode* node=VST->GetFirst();
 while (node)
    {
    data=static_cast<TreeItemData*>(VST->GetNodeData(node));
    //doo something with node, data
    node=VST->GetNext(node, true);
    } 
于 2013-07-04T09:19:39.797 に答える