One of the things that needs editing is the menu: a simple Key | ParentKey | MenuKey | Name | Url table (MenuKey because I have more than one menu). I bound a TreeView control to this menu using a set of classes I wrote a while ago to bind a TreeView to a folder structure. I decided to share my code, because it's considerably easier to use than anything else I've been able to find online.
I've based my code on the following article: http://www.codeproject.com/Articles/19639/Implementing-IHierarchy-Support-Into-Your-Custom-C . I was able to get my menu items bound to my TreeView using this method. It does not seem reasonable to have to write the same type of code every time I want to bind to a custom data type though. What's more; I'm adding a collection class, simply to implement the IHierarchicalEnumerable interface. The methods I'm simply patching through to existing methods and properties. I'm a programmer, not a plumber.
So here is a generic implementation of IHierarchyData and IHierarchicalEnumerable (download link at the end):
using System; using System.Collections.Generic; using System.Linq; using System.Web.UI; namespace Tabeoka { /// <summary> /// Utility class for creating a hierarchical data source /// </summary> public static class HierarchyData { /// <summary> /// Returns a hierarchical data list containing only the root item /// </summary> /// <typeparam name="T">The data type used for building up the hierarchical data source</typeparam> /// <param name="root">The root data item</param> /// <param name="childSelector">A delegate that returns the child items of the item passed as an argument</param> /// <param name="parentSelector">A delegate that returns the parent item of the item passed as an argument, or null if root</param> /// <param name="pathSelector">A delegate that is called for the path of the current item as a string</param> /// <returns></returns> public static HierarchyDataList<T> GetData<T>(T root, Func<T, IEnumerable<T>> childSelector, Func<T, T> parentSelector, Func<T, string> pathSelector) where T : class { if (root == null) throw new ArgumentNullException("root"); if (childSelector == null) throw new ArgumentNullException("childSelector"); if (parentSelector == null) throw new ArgumentNullException("parentSelector"); if (pathSelector == null) throw new ArgumentNullException("pathSelector"); return new HierarchyDataList<T>( new List<T> { root }, childSelector, parentSelector, pathSelector ); } /// <summary> /// Wrapper class for the data type that implements IHierarchyData /// </summary> /// <typeparam name="T">The underlying data type</typeparam> public class HierarchyDataItem<T> : IHierarchyData where T : class { internal T DataItem { get; set; } internal Func<T, IEnumerable<T>> ChildSelector { get; set; } internal Func<T, T> ParentSelector { get; set; } internal Func<T, string> PathSelector { get; set; } internal HierarchyDataItem( T dataItem, Func<T, IEnumerable<T>> childSelector, Func<T, T> parentSelector, Func<T, string> pathSelector ) { DataItem = dataItem; this.ChildSelector = childSelector; this.ParentSelector = parentSelector; this.PathSelector = pathSelector; } #region IHierarchyData Members /// <summary> /// Gets the child items wrapped in a IHierarchicalEnumerable /// </summary> /// <returns>A HierarchyDataList<T></returns> public IHierarchicalEnumerable GetChildren() { return new HierarchyDataList<T>( ChildSelector(DataItem), this.ChildSelector, this.ParentSelector, this.PathSelector ); } /// <summary> /// Gets the parent item, and wraps it in a HierarchyDataItem /// </summary> /// <returns>A HierarchyDataItem<T>, or null if not found</returns> public IHierarchyData GetParent() { var parent = this.ParentSelector(DataItem); if (parent != null) return new HierarchyDataItem<T>( parent, this.ChildSelector, this.ParentSelector, this.PathSelector ); return null; } /// <summary> /// Checks if there are any child nodes, and returns true if there are /// </summary> public bool HasChildren { get { return this.ChildSelector(DataItem).Count() > 0; } } /// <summary> /// The underlying data object /// </summary> public object Item { get { return DataItem; } } /// <summary> /// Is supposed to return the logical path according to the underlying data, /// just calls the pathSelector. /// </summary> public string Path { get { return PathSelector(DataItem); } } /// <summary> /// TypeOf(T) /// </summary> public string Type { get { return typeof(T).ToString(); } } #endregion } /// <summary> /// A list of T that implements IHierarchicalEnumerable /// </summary> /// <typeparam name="T">The underlying data type</typeparam> public class HierarchyDataList<T> : List<T>, IHierarchicalEnumerable where T : class { internal Func<T, IEnumerable<T>> ChildSelector { get; set; } internal Func<T, T> ParentSelector { get; set; } internal Func<T, string> PathSelector { get; set; } internal HierarchyDataList( IEnumerable<T> items, Func<T, IEnumerable<T>> childSelector, Func<T, T> parentSelector, Func<T, string> pathSelector ) : base(items) { this.ChildSelector = childSelector; this.ParentSelector = parentSelector; this.PathSelector = pathSelector; } #region IHierarchicalEnumerable Members /// <summary> /// Wraps the enumeratedItem object in a HierarchyDataItem /// </summary> /// <param name="enumeratedItem">The data item</param> /// <returns>an instance of HierarchyDataItem<T></returns> public IHierarchyData GetHierarchyData(object enumeratedItem) { return new HierarchyDataItem<T>( enumeratedItem as T, this.ChildSelector, this.ParentSelector, this.PathSelector); } #endregion } } }
I'm wrapping all of it in a static class: the public static method allows me to infer the generic type parameters.
Here's how you use it:
// The root node for my folder structure var root = Folders.GetRoot(); // Setting the delegates for getting // children, parent and path value var folderData = HierarchyData.GetData( root, d => d.SubFolders, d => d.ParentFolder, d => d.Name); tree = new TreeView(); this.Controls.Add(tree); if (!this.Page.IsPostBack) { // now just assign datasource and bindings tree.DataSource = folderData; tree.DataBindings.Add(new TreeNodeBinding() { TextField = "Name", ValueField = "Key" }); tree.DataBind(); }
And here's the download link: http://www.tabeoka.be/downloads/HierarchyData.zip .
I hope this helps someone, somewhere. I welcome comments and criticism.
Menno
No comments:
Post a Comment