How to write a Dynamic Treeview in WPF?

Here is a sample code for a dynamic treeview. First thing is that you need to define your data staructure. I am using NavigationItem class for a single item and NavigationItemCollection class for the whole collection of items in the tree. The trick to maintaine the treeview as you change the source is to use ObservableCollection.

NavigationItem Class:

public class NavigationItem

{

        public NavigationItem(string in_name, string in_absolutePath, string in_relativePath, string in_title, Boolean in_enabled)

        {

            _name = in_name;

            _absolutePath = in_absolutePath;

            _relativePath = in_relativePath;

            _title = in_title;

            _enabled = in_enabled;

        }

        private ObservableCollection<NavigationItem> _subItems;

        public ObservableCollection<NavigationItem> SubItems

        {

            get { return _subItems; }

            set

            {

                _subItems = value;

            }

        }

        private String _relativePath;

        public String RelativePath

        {

            get { return _relativePath; }

            set

            {

                _relativePath = value;

            }

        }

        private String _absolutePath;

        public String AbsolutePath

        {

            get { return _absolutePath; }

            set

            {

                _absolutePath = value;

            }

        }

        private String _name;

        public String Name

        {

            get { return _name; }

            set

            {

                _name = value;

            }

        }

        private String _title;

        public String Title

        {

            get { return _title; }

            set

            {

                _title = value;

            }

        }

        private Boolean _enabled;

        public Boolean Enabled

        {

            get { return _enabled; }

            set

            {

                _enabled = value;

            }

        }

 }

NavigationItemCollection Class:

    public class NavigationItemCollection : BaseNavigationItemCollection

    {

        public NavigationItemCollection()

        {

            ObservableCollection<NavigationItem> _subItems = new ObservableCollection<NavigationItem>();

            _subItems.Add(new NavigationItem("MyNameSpace.Page1", "Folder1/Page1.xaml", "Page1.xaml", "Page Title 1", true));

            _subItems.Add(new NavigationItem("MyNameSpace.Page2", "Folder1/Page2.xaml", "Page2.xaml", "Page Title 2", true));

            _subItems.Add(new NavigationItem("MyNameSpace.Page3", "Folder1/Page3.xaml", "Page3.xaml", "Page Title 3", true));

            NavigationItem navitem = new NavigationItem("", "", "", "Tree Title", true);

            navitem.SubItems = _subItems;

            _navitems = new ObservableCollection<NavigationItem>();

            _navitems.Add(navitem);

        }

    }

The second step would be to define your tree as you would for any treeview and bind it to the proper datasource.

The Main Page XMAL file:

<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >

          <Border DockPanel.Dock="Left">

                   <TreeView DockPanel.Dock="Left" Width="200" Name="mytree" Margin="10,10,10,10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectedItemChanged="NavigationChanged">

                             <TreeView.ItemTemplate>

                                      <HierarchicalDataTemplate ItemsSource="{Binding Path=SubItems}" >

                                                <TextBlock Text="{Binding Path=Title}"/> </HierarchicalDataTemplate>

                             </TreeView.ItemTemplate>

                   </TreeView>

          </Border>

          <Border DockPanel.Dock="Right">

                             <Frame Name="frmBody" Source="Page1.xaml"

                             HorizontalAlignment="Stretch" VerticalAlignment="Stretch" NavigationUIVisibility="Hidden" ></Frame>

          </Border>

</DockPanel>

The code behind for the XAML file:

#region Constructors

        public MainPage()

        {

            InitializeComponent();

            BindControls();

        }

#endregion

#region Methods

        public void BindControls()

        {

            NavigationItems objNav = new NavigationItems();

            App.Current.Resources.Add("NavigationItems", objNav);

            mytree.ItemsSource = (ObservableCollection<NavigationItem>) ((NavigationItems)App.Current.Resources["NavigationItems"]).EnabledNavItems;

        }

        public void NavigationChanged(object sender, RoutedPropertyChangedEventArgs<Object> e)

        {

            NavigationItem navTree = mytree.SelectedItem as NavigationItem;

            if (navTree != null)

            {

                if (!string.IsNullOrEmpty(navTree.RelativePath))

                {

                    frmBody.Source = new Uri(navTree.RelativePath, UriKind.Relative);

                }

            }

        }

#endregion

Now here is where you want to make your tree to be more flexible and change as the user walk through the pages.

The NavigationItem class and NavigationItemCollection class are pretty straight forward. But as you look more closely at NavigationItemCollection class it inherits from BaseNavigationItemCollection class that has couple more methods:

 

GetNextItem()

MoveNext()

FindCurrentItem()

RemoveItem()

These methods will work on the collection to make changes to the items.

BaseNavigationItemCollection Class:

public class BaseNavigationItemCollection : ObservableCollection<NavigationItem>

    {

        public BaseNavigationItemCollection()

        {

        }

        protected ObservableCollection<NavigationItem> _navitems;

        private int _currentIndex;

        public ObservableCollection<NavigationItem> EnabledNavItems

        {

            get

            {

                return _navitems;

            }

        }

        public void RemoveItem(NavigationItem item)

        {

           for (int index = 0; index < _navitems[0].SubItems.Count; index++)

            {

                if (_navitems[0].SubItems[index].Name == item.Name)

                {

                    _navitems[0].SubItems[index].Enabled = false;

                }

     }

        }

        public NavigationItem GetNextItem(string currentpage)

        {

            FindCurrentItem(currentpage);

            MoveNext();

            while (_currentIndex < _navitems[0].SubItems.Count && !_navitems[0].SubItems[_currentIndex].Enabled)

            {

                MoveNext();

            }

            if (_currentIndex >= _navitems[0].SubItems.Count)

            {

                return _navitems[0];

            }

            else

        {

                return _navitems[0].SubItems[_currentIndex];

            }

        }

        private void MoveNext()

        {

            _currentIndex += 1;

        }

        private void FindCurrentItem(string name)

        {

            for (int index = 0; index < _navitems[0].SubItems.Count; index++)

            {

                if (_navitems[0].SubItems[index].Name == name)

                {

                    _currentIndex = index;

                    return;

                }

            }

        }

    }

The result: As the user walks through the pages and provides input values you can determine the next step in the app based on the values entered by the user and even make change in the navigation items including moving steps back and force, removing some steps, changing the Titles, and etc. The nice thing here is that the treeview on the left Nav will reflect those changes. J pretty neat.

Comments

  • Anonymous
    February 25, 2008
    First off, thanks, this helped alot, however, i  noticed that the following method failed: public void BindControls() { NavigationItems objNav = new NavigationItems(); App.Current.Resources.Add("NavigationItems", objNav); mytree.ItemsSource = (ObservableCollection<NavigationItem>) ((NavigationItems)App.Current.Resources["NavigationItems"]).EnabledNavItems; } The object or class "NavigationItems" did not exist. I then rplaced "NavigationItems" with "BaseNavigationItemCollection" wich was in fact the collection and the method worked 100% Once again... Thank you!