Click here to Skip to main content
15,891,943 members
Articles / Programming Languages / C#

Generic Tree <T> in C#

Rate me:
Please Sign up or sign in to vote.
4.70/5 (27 votes)
28 Jan 2006CPOL10 min read 353.6K   5.9K   107  
A generic 1:(0..N) tree container with change events and automatic updating of a TreeView.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;

using PH.DataTree;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

namespace XTree
{

   public partial class Form1 : Form
   {
      TreeViewController<string>  m_tvc;
      DTreeNode<string>           m_data;

      LabelGenerator m_lGen = new LabelGenerator();

      public Form1()
      {
         // System.Threading.Thread.Sleep(1000);
         // PerfTest.TestPerf();

         InitializeComponent();

         m_data = new DTreeNode<string>(""); // note: root node is not mapped
         m_tvc = new TreeViewController<string>(treeView1, m_data);

         UpdateUI();
         NextSuggestion();
      }

      private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
      {
         UpdateUI();
      }

      void NextSuggestion()
      {
         textBox1.Text = m_lGen.Get();
         textBox1.SelectAll();
         textBox1.Focus();
      }

      private void UpdateUI()
      {
         DTreeNode<string> curNode = m_tvc.SelectedNode;

         bool haveNode = curNode != null;

         button2.Enabled = haveNode;
         button3.Enabled = haveNode;
         button5.Enabled = haveNode;

         if (haveNode)
         {
            string s = curNode.GetNodePathAsString(true, '\\');
            label2.Text = s;
         }


         label1.Text = m_lGen.Index.ToString();
      }

      private void button1_Click(object sender, EventArgs e)
      {
         // Add new node
         DTreeNode<string> curNode = m_tvc.SelectedNode;

         DTreeNode<string> newNode;

         if (curNode == null)
            newNode = m_data.Nodes.Add(textBox1.Text);
         else
            newNode = curNode.Siblings.InsertAfter(curNode, textBox1.Text);

         m_tvc.SelectedNode = newNode;
         NextSuggestion();
      }

      private void button2_Click(object sender, EventArgs e)
      {
         // Add new child node
         DTreeNode<string> curNode = m_tvc.SelectedNode;
         m_tvc.SelectedNode =
            curNode.Nodes.Add(textBox1.Text);
         NextSuggestion();
      }

      private void button5_Click(object sender, EventArgs e)
      {
         // change current value
         DTreeNode<string> currentNode = m_tvc.SelectedNode;
         if (currentNode != null)
            currentNode.Value = textBox1.Text;

         // Note: if the node type is a reference type, 
         // and is changed indirectly (not through an assignment), you have to call:
         //   m_tvc.Update(currentNode)
         // This is because changing attributes of a reference cannot fire an OnValueChanged event

         // Note II: UpdateUI() only updates the buttons of this form. 
         // It is not required to update the tree 
      }

      private void button3_Click(object sender, EventArgs e)
      {
         // remove current node
         DTreeNode<string> currentNode = m_tvc.SelectedNode;
         if (currentNode != null)
            currentNode.Remove();
      }



      private class ComparebyName : IComparer<string>
      {
         public int Compare(string x, string y)
         {
            return x.CompareTo(y);
         }
      }

      private class ComparebyNameDesc : IComparer<string>
      {
         public int Compare(string x, string y)
         {
            return y.CompareTo(x);
         }
      }

      private class CompareByLength : IComparer<string>
      {
         public int Compare(string x, string y)
         {
            return x.Length.CompareTo(y.Length);
         }
      }



      private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
      {
         int curSel = comboBox1.SelectedIndex;

         switch (curSel)
         {
            case 0 : // none
               m_tvc.AutoSortCompare = null;
               break;

            case 1:  // Name Ascending
               m_tvc.AutoSortCompare = new ComparebyName();
               break;

            case 2:
               m_tvc.AutoSortCompare = new ComparebyNameDesc();
               break;

            case 3:
               m_tvc.AutoSortCompare = new CompareByLength();
               break;
         }
      }

      private void openToolStripMenuItem_Click(object sender, EventArgs e)
      {
         DialogResult result = openFileDialog1.ShowDialog(this);
         if (result != DialogResult.OK)
            return;

         string ext = Path.GetExtension(openFileDialog1.FileName).ToLower();

         bool asXml = ext=="xml" || (ext != "bin" && openFileDialog1.FilterIndex == 1);

         if (asXml)
         {
            XmlSerializer ser = new XmlSerializer(typeof(DTreeNode<string>));
            FileStream stream = new FileStream(openFileDialog1.FileName, FileMode.Open);
            m_data = (DTreeNode<string>)(ser.Deserialize(stream));
            stream.Close();
         }
         else
         {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            m_data = (DTreeNode<string>)formatter.Deserialize(stream);
            stream.Close();
         }

         // update tvc
         m_tvc = new TreeViewController<string>(treeView1, m_data);
      }

      private void saveToolStripMenuItem_Click(object sender, EventArgs e)
      {
         DialogResult result = saveFileDialog1.ShowDialog(this);
         if (result != DialogResult.OK)
            return;

         bool asXML = saveFileDialog1.FilterIndex == 1;

         if (asXML)
         {
            XmlSerializer ser = new XmlSerializer(typeof(DTreeNode<string>));
            StreamWriter writer = new StreamWriter(saveFileDialog1.FileName);
            ser.Serialize(writer, m_data);
            writer.Close();
         }
         else
         {
            m_tvc = null;
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(saveFileDialog1.FileName, FileMode.Create, FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, m_data);
            stream.Close();
            m_tvc = new TreeViewController<string>(treeView1, m_data);
         }
      }


   }

   class LabelGenerator // Helper class to generate some nice words
   {
      string[] words;
      int index = -1;

      #region CTOR
      public LabelGenerator()
      {
         string text = 
            @"Ich bin so wild nach deinem Erdbeermund,
               ich schrie mir schon die Lungen wund
               nach deinem weissen Leib, du Weib.
               Im Klee, da hat der Mai ein Bett gemacht,
               da bl�ht ein sch�ner Zeitvertreib
               mit deinem Leib die lange Nacht.
               Da will ich sein im tiefen Tal
               dein Nachtgebet und auch dein Sterngemahl.

               Die graue Welt macht keine Freude mehr,
               ich gab den sch�nsten Sommer her,
               und dir hats auch kein Gl�ck gebracht;
               hast nur den roten Mund noch aufgespart
               f�r mich so tief im Haar verwahrt ...
               Ich such ihn schon die lange Nacht
               im Wintertal im Aschengrund ...
               Ich bin so wild nach deinem Erdbeermund.

               Im Wintertal, im schwarzen Erdbeerkraut,
               da hat der Schnee ein Nest gebaut
               und fragt nicht, wo die Liebe sei.
               Und habe doch das rote Tier so tief
               erfahren, als ich bei dir schlief.
               W�r nur der Winter erst vorbei
               und wieder gr�n der Wiesengrund!
               ... ich bin so wild nach deinem Erdbeermund!";

         words = text.Split(new Char[] { ' ', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
      }
      #endregion

      public string Get()
      {
         ++index;
         if (index >= words.Length)
            index = 0;
         return words[index];
      }

      public int Index { get { return index; } }
   }




}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Klippel
Germany Germany
Peter is tired of being called "Mr. Chen", even so certain individuals insist on it. No, he's not chinese.

Peter has seen lots of boxes you youngsters wouldn't even accept as calculators. He is proud of having visited the insides of a 16 Bit Machine.

In his spare time he ponders new ways of turning groceries into biohazards, or tries to coax South American officials to add some stamps to his passport.

Beyond these trivialities Peter works for Klippel[^], a small german company that wants to make mankind happier by selling them novel loudspeaker measurement equipment.


Where are you from?[^]



Please, if you are using one of my articles for anything, just leave me a comment. Seeing that this stuff is actually useful to someone is what keeps me posting and updating them.
Should you happen to not like it, tell me, too

Comments and Discussions