質問

私は、CaseNotesの親子関係を表示するWinForm TreeViewコントロールを持っています(ほとんどの人にとってそれは何の意味もありませんが、答えを視覚化するのに役立ちます)。

表示する必要があるCaseNotesのDataTableがあります。親/子は次のように定義されます:行にParentNoteIDがある場合、それはそのノートのchildNodeであり、そうでない場合はrootNodeです。別の行がParentNoteIDであるようにIDを持っている場合、親ノート(rootNodeではありません)である可能性もあります。

物事を複雑にする(おそらく単純化する)ために、ノードを交互に色付けする以下の(大部分の)動作コードがあります。ツリービューの静的コレクションを手動で作成し、かなり正確に色付けしています。ここで、DataTableからノードを動的に設定する必要があります。

ツリービューをノードごとに既に実行しているので、このプロセスに何らかの形でデータを追加することはできませんか?最初にノードを構築し、次に別のルーチンとして色を付ける必要があるかもしれませんが、再帰方法はまだ適用されますか?

各ノードのCaseNoteIDを表示したいとします。これはDataTableで返され、一意です。

foreach (TreeNode rootNode in tvwCaseNotes.Nodes)
        {
            ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);

        }
protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
    {
        root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;

        foreach (TreeNode childNode in root.Nodes)
        {
            Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;

            if (childNode.Nodes.Count > 0)
            {
                // alternate colors for the next node
                if (nextColor == firstColor)
                    ColorNodes(childNode, secondColor, firstColor);
                else
                    ColorNodes(childNode, firstColor, secondColor);
            }
        }
    }

編集

これまでの私の考え/試み:

        public void BuildSummaryView()
    {
        tvwCaseNotes.Nodes.Clear();

        DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);
        foreach (var cNote in cNotesForTree.Rows)
        {

            tvwCaseNotes.Nodes.Add(new TreeNode("ContactDate"));
        }
        FormPaint();
    }

明らかにこれには欠陥があります。 1つは、ContactDateを何度も表示するだけです。正しい回数が表示されていることは確かですが、ContactDate(Valueはデータベースの列であり、DataTableで返されます。2番目にChildNodeロジックを追加する必要があります。 if(ノード。 parentNode = node.CaseNoteID)blah ...

編集2

だから私はこのリンクを見つけました、こちら、DataTableをArrayListに入れる必要があるように思われます。正しいですか?

編集3

さて、Cerebusのおかげでこれはほとんど機能しています。もう1つ質問があります。これをどうやって取るのですか?>

DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);

これで返されたDataTableを使用しますか?これを置き換えるだけですか?>

    dt = new DataTable("CaseNotes");
dt.Columns.Add("NoteID", typeof(string));
dt.Columns.Add("NoteName", typeof(string));
DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
dc.AllowDBNull = true;
dt.Columns.Add(dc);

// Add sample data.
dt.Rows.Add(new string[] { "1", "One", null });
dt.Rows.Add(new string[] { "2", "Two", "1" });
dt.Rows.Add(new string[] { "3", "Three", "2" });
dt.Rows.Add(new string[] { "4", "Four", null });
dt.Rows.Add(new string[] { "5", "Five", "4" });
dt.Rows.Add(new string[] { "6", "Six", null });
dt.Rows.Add(new string[] { "7", "Seven", null });
dt.Rows.Add(new string[] { "8", "Eight", "7" });
dt.Rows.Add(new string[] { "9", "Nine", "8" });

混乱は、Column.AddとRow.Addsを行う必要があるのでしょうか?また、DataColumnは実際のデータ構造にどのように変換されますか?非常に無知な質問でごめんなさい、良いニュースは二度と尋ねる必要がないことです。

編集4

以下は実行時エラーを提供しています。

if (nodeList.Find(FindNode) == null)
  {
    DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
    if (childRows.Length > 0)
    {
      // Recursively call this function for all childRowsl
      TreeNode[] childNodes = RecurseRows(childRows);

      // Add all childnodes to this node.
      node.Nodes.AddRange(childNodes);
    }

    // Mark this noteID as dirty (already added).
    //doneNotes.Add(noteID);
    nodeList.Add(node);
  }

エラーは次のとおりです-> 列[ea8428e4]が見つかりません これは正しいNoteIDの最初の8桁です(Guidを使用する必要があります)。その名前の列を探しているべきですか??私はGUIDを使用しているため、他に何かする必要がありますか?私のすべての参照とあなたのコードをGuidに変更しました...

役に立ちましたか?

解決

この問題を解決するために、サンプルのWindowsフォームを作成し、次のコードを作成しました。データテーブルの設計は次のように思い描いていました。

 NoteID  NoteName  ParentNoteID
   "1"    "One"        null
   "2"    "Two"        "1"
   "3"    "Three"      "2"
   "4"    "Four"       null
...

これにより、ツリーが作成されます(申し訳ありませんが、ASCIIアートはあまり得意ではありません!):

One
 |
 ——Two
 |
 ————Three
 |
Four

擬似コードは次のようになります。

  1. データテーブル内のすべての行を反復処理します。
  2. 各行に対して、TreeNodeを作成し、そのプロパティを設定します。この行のIDと一致するParentNodeIDを持つすべての行に対して、このプロセスを再帰的に繰り返します。
  3. 各完全な反復は、無限のネストを持つすべての一致する子ノードを含むノードを返します。
  4. 完成したノードリストをTreeViewに追加します。

シナリオの問題は、「外部キー」という事実から発生します。同じテーブルの列を参照します。これは、行を反復処理するときに、どの行が既に解析されたかを追跡する必要があることを意味します。たとえば、上の表では、2番目と3番目の行に一致するノードは、最初の完全な反復で既に追加されています。したがって、それらを再度追加しないでください。これを追跡する方法は2つあります:

  1. 行われたIDのリストを維持します( doneNotes )。新しいノードを追加する前に、そのリストにnoteIDが存在するかどうかを確認してください。これはより高速な方法であり、通常は推奨されます。 (このメソッドは以下のコードでコメント化されています
  2. 各反復で、述語汎用デリゲート( FindNode )を使用して、追加されたノードのリスト(ネストされたノードを含む)を検索し、追加されるノードがそのリストに存在するかどうかを確認します。これは遅いソリューションですが、私は複雑なコードが好きです! :P

さて、試してテストしたコードは次のとおりです(C#2.0):


public partial class TreeViewColor : Form
{
  private DataTable dt;
  // Alternate way of maintaining a list of nodes that have already been added.
  //private List<int> doneNotes;
  private static int noteID;

  public TreeViewColor()
  {
    InitializeComponent();
  }

  private void TreeViewColor_Load(object sender, EventArgs e)
  {
    CreateData();
    CreateNodes();

    foreach (TreeNode rootNode in treeView1.Nodes)
    {
      ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);
    }
  }

  private void CreateData()
  {
    dt = new DataTable("CaseNotes");
    dt.Columns.Add("NoteID", typeof(string));
    dt.Columns.Add("NoteName", typeof(string));
    DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
    dc.AllowDBNull = true;
    dt.Columns.Add(dc);

    // Add sample data.
    dt.Rows.Add(new string[] { "1", "One", null });
    dt.Rows.Add(new string[] { "2", "Two", "1" });
    dt.Rows.Add(new string[] { "3", "Three", "2" });
    dt.Rows.Add(new string[] { "4", "Four", null });
    dt.Rows.Add(new string[] { "5", "Five", "4" });
    dt.Rows.Add(new string[] { "6", "Six", null });
    dt.Rows.Add(new string[] { "7", "Seven", null });
    dt.Rows.Add(new string[] { "8", "Eight", "7" });
    dt.Rows.Add(new string[] { "9", "Nine", "8" });
  }

  private void CreateNodes()
  {
    DataRow[] rows = new DataRow[dt.Rows.Count];
    dt.Rows.CopyTo(rows, 0);
    //doneNotes = new List<int>(9);

    // Get the TreeView ready for node creation.
    // This isn't really needed since we're using AddRange (but it's good practice).
    treeView1.BeginUpdate();
    treeView1.Nodes.Clear();

    TreeNode[] nodes = RecurseRows(rows);
    treeView1.Nodes.AddRange(nodes);

    // Notify the TreeView to resume painting.
    treeView1.EndUpdate();
  }

  private TreeNode[] RecurseRows(DataRow[] rows)
  {
    List<TreeNode> nodeList = new List<TreeNode>();
    TreeNode node = null;

    foreach (DataRow dr in rows)
    {
      node = new TreeNode(dr["NoteName"].ToString());
      noteID = Convert.ToInt32(dr["NoteID"]);

      node.Name = noteID.ToString();
      node.ToolTipText = noteID.ToString();

      // This method searches the "dirty node list" for already completed nodes.
      //if (!doneNotes.Contains(doneNoteID))

      // This alternate method using the Find method uses a Predicate generic delegate.
      if (nodeList.Find(FindNode) == null)
      {
        DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
        if (childRows.Length > 0)
        {
          // Recursively call this function for all childRowsl
          TreeNode[] childNodes = RecurseRows(childRows);

          // Add all childnodes to this node.
          node.Nodes.AddRange(childNodes);
        }

        // Mark this noteID as dirty (already added).
        //doneNotes.Add(noteID);
        nodeList.Add(node);
      }
    }

    // Convert this List<TreeNode> to an array so it can be added to the parent node/TreeView.
    TreeNode[] nodeArr = nodeList.ToArray();
    return nodeArr;
  }

  private static bool FindNode(TreeNode n)
  {
    if (n.Nodes.Count == 0)
      return n.Name == noteID.ToString();
    else
    {
      while (n.Nodes.Count > 0)
      {
        foreach (TreeNode tn in n.Nodes)
        {
          if (tn.Name == noteID.ToString())
            return true;
          else
            n = tn;
        }
      }
      return false;
    }
  }

  protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
  {
    root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;

    foreach (TreeNode childNode in root.Nodes)
    {
      Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;

      if (childNode.Nodes.Count > 0)
      {
        // alternate colors for the next node
        if (nextColor == firstColor)
          ColorNodes(childNode, secondColor, firstColor);
        else
          ColorNodes(childNode, firstColor, secondColor);
      }
    }
  }
}

他のヒント

TreeViewに2つの便利なプロパティを追加する新しい単純な拡張クラスの使用を含む、よりシンプルなTreeViewの拡張メソッドを作成しました。

    internal class IdNode : TreeNode
    {
        public object Id { get; set; }
        public object ParentId { get; set; }
    }

    public static void PopulateNodes(this TreeView treeView1, DataTable dataTable, string name, string id, string parentId)
    {
        treeView1.BeginUpdate();
        foreach (DataRow row in dataTable.Rows)
        {
            treeView1.Nodes.Add(new IdNode() { Name = row[name].ToString(), Text = row[name].ToString(), Id = row[id], ParentId = row[parentId], Tag = row });
        }
        foreach (IdNode idnode in GetAllNodes(treeView1).OfType<IdNode>())
        {
            foreach (IdNode newparent in GetAllNodes(treeView1).OfType<IdNode>())
            {
                if (newparent.Id.Equals(idnode.ParentId))
                {
                    treeView1.Nodes.Remove(idnode);
                    newparent.Nodes.Add(idnode);
                    break;
                }
            }
        }
        treeView1.EndUpdate();
    }

    public static List<TreeNode> GetAllNodes(this TreeView tv)
    {
        List<TreeNode> result = new List<TreeNode>();
        foreach (TreeNode child in tv.Nodes)
        {
            result.AddRange(GetAllNodes(child));
        }
        return result;
    }
    public static List<TreeNode> GetAllNodes(this TreeNode tn)
    {
        List<TreeNode> result = new List<TreeNode>();
        result.Add(tn);
        foreach (TreeNode child in tn.Nodes)
        {
            result.AddRange(GetAllNodes(child));
        }
        return result;
    }

modiX すべての(ネストされた)ノードを取得する方法

これをチェック:

Public Sub BuildTree(ByVal dt As DataTable, ByVal trv As TreeView, ByVal expandAll As [Boolean])
    ' Clear the TreeView if there are another datas in this TreeView
    trv.Nodes.Clear()
    Dim node As TreeNode
    Dim subNode As TreeNode
    For Each row As DataRow In dt.Rows
        'search in the treeview if any country is already present
        node = Searchnode(row.Item(0).ToString(), trv)
        If node IsNot Nothing Then
           'Country is already present
            subNode = New TreeNode(row.Item(1).ToString())
            'Add cities to country
            node.Nodes.Add(subNode)
        Else
            node = New TreeNode(row.Item(0).ToString())
            subNode = New TreeNode(row.Item(1).ToString())
            'Add cities to country
            node.Nodes.Add(subNode)
            trv.Nodes.Add(node)
        End If
    Next
    If expandAll Then
        ' Expand the TreeView
        trv.ExpandAll()
    End If
End Sub

その他の完全なソースコードの場合:ツリービューを作成する方法vb.netのデータテーブルから

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top