|
First, thank you soooo much for this article. There is so little information out there on how to implement IHierarchyData on a custom class ( not the FileInfo).
I used this with a custom class called Group, that has some simple props for Id, ParentId, and GroupName. However, I cannot seem to figure out how to get the TextField and ValueField to bind to a treeview.
here is what I have ( the binding is commented out because it threw errors)
HierarchyData<infocus.group> parent =
new HierarchyData<infocus.group>(new inFocus.Group(0, 0, "Parent"), null);
HierarchyData<infocus.group> child1 =
new HierarchyData<infocus.group>(new inFocus.Group(1, 0, "Child1"), parent);
HierarchyData<infocus.group> child2 =
new HierarchyData<infocus.group>(new inFocus.Group(1, 0, "Child2"), parent);
HierarchyDataCollection<hierarchydata<infocus.group>> collection =
new HierarchyDataCollection<hierarchydata<infocus.group>>();
collection.Add(parent);
this.TreeView1.DataSource = collection;
//TreeNodeBinding binding = new TreeNodeBinding();
//binding.Depth = 1;
//binding.DataMember = "inFocus.Group";
//binding.TextField = "GroupName";
//binding.ValueField = "Id";
//this.TreeView1.DataBindings.Add(binding);
this.TreeView1.DataBind();
I added this to the HierarchyDataT<>:
public override string ToString()
{
return m_item.ToString();
}
|
|
|
|
|
When I knew I was going to have to have a hierarchical control, I started searching and yours was the best I found. You gave me a great head start.
I fixed the recursion algorithm and created an algorithm such that viewstate will work. I thought you'd might like a copy The code is in Boo, a smaller .NET language. It should be pretty readable in any case.
<br />
import System<br />
import System.Collections.Generic<br />
import System.ComponentModel<br />
import System.Text<br />
import System.Web.UI<br />
import System.Web.UI.WebControls<br />
<br />
enum HierarchicalListItemType:<br />
Header<br />
Footer<br />
ItemHeader<br />
ItemFooter<br />
Item<br />
<br />
#region event args<br />
<br />
callable HierarchicalRepeaterItemEventHandler(sender as object, e as HierarchicalRepeaterItemEventArgs)<br />
callable HierarchicalRepeaterCommandEventHandler(sender as object, e as HierarchicalRepeaterCommandEventArgs)<br />
<br />
class HierarchicalRepeaterItemEventArgs(EventArgs):<br />
[getter(Item)]<br />
_item as HierarchicalRepeaterItem<br />
<br />
def constructor(item as HierarchicalRepeaterItem):<br />
_item = item<br />
<br />
<br />
class HierarchicalRepeaterCommandEventArgs(CommandEventArgs):<br />
[getter(CommandSource)]<br />
private _commandSource as object<br />
<br />
[getter(Item)]<br />
private _item as HierarchicalRepeaterItem<br />
<br />
def constructor(item as HierarchicalRepeaterItem, commandSource as object, originalArgs as CommandEventArgs):<br />
super(originalArgs)<br />
_item = item<br />
_commandSource = commandSource<br />
<br />
#endregion<br />
<br />
#region template<br />
<br />
class ItemTemplateList(List[of ItemTemplate]):<br />
def GetTemplate(depth as ushort, listItemType as HierarchicalListItemType):<br />
return self.Find({ item as ItemTemplate | (item.Depth == depth and item.ListItemType == listItemType) })<br />
<br />
def InsertTemplate(index as int, item as ItemTemplate):<br />
assert self.GetTemplate(item.Depth, item.ListItemType) is null, 'ITemplate Definition already Exists'<br />
self.Insert(index, item)<br />
<br />
<br />
class ItemTemplate(ITemplate):<br />
[property(Depth)]<br />
private _depth as ushort<br />
<br />
[property(ListItemType)]<br />
private _listItemType as HierarchicalListItemType<br />
<br />
[property(Template, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _template as ITemplate<br />
<br />
def InstantiateIn(container as Control):<br />
_template.InstantiateIn(container)<br />
<br />
#endregion<br />
<br />
[DefaultEvent('ItemCommand')]<br />
[DefaultProperty('DataSource')]<br />
[ParseChildren(true)]<br />
[PersistChildren(false)]<br />
class HierarchicalRepeater(HierarchicalDataBoundControl, INamingContainer):<br />
[property(TemplateCollection, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(typeof(IDataItemContainer))<br />
]<br />
)]<br />
private _templateList = ItemTemplateList()<br />
<br />
[property(HeaderTemplate, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _headerTemplate as ITemplate<br />
<br />
[property(FooterTemplate, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _footerTemplate as ITemplate<br />
<br />
[property(ItemHeaderTemplate, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _itemHeaderTemplate as ITemplate<br />
<br />
[property(ItemFooterTemplate, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _itemFooterTemplate as ITemplate<br />
<br />
[property(ItemTemplate, Attributes:<br />
[<br />
PersistenceMode(PersistenceMode.InnerProperty),<br />
TemplateContainer(IDataItemContainer)<br />
]<br />
)]<br />
private _itemTemplate as ITemplate<br />
<br />
private _itemArray as List[of HierarchicalRepeaterItem]<br />
private _items as List[of HierarchicalRepeaterItem]<br />
<br />
Items as List[of HierarchicalRepeaterItem]:<br />
get:<br />
if _items is null:<br />
if _itemArray is null:<br />
self.EnsureChildControls()<br />
<br />
_items = List[of HierarchicalRepeaterItem](_itemArray)<br />
<br />
return _items<br />
<br />
private m_Arguments as DataSourceSelectArguments<br />
protected SelectArguments:<br />
get:<br />
self.m_Arguments = DataSourceSelectArguments.Empty if self.m_Arguments is null <br />
return self.m_Arguments<br />
<br />
override TagKey as HtmlTextWriterTag:<br />
get:<br />
return HtmlTextWriterTag.Div <br />
<br />
#region events<br />
<br />
event ItemCommand as HierarchicalRepeaterCommandEventHandler<br />
protected virtual def OnItemCommand(e as HierarchicalRepeaterCommandEventArgs):<br />
ItemCommand(self, e)<br />
<br />
event ItemDataBound as HierarchicalRepeaterItemEventHandler<br />
protected virtual def OnItemDataBound(args as HierarchicalRepeaterItemEventArgs):<br />
ItemDataBound(self, args)<br />
<br />
event ItemCreated as HierarchicalRepeaterItemEventHandler<br />
protected virtual def OnItemCreated(args as HierarchicalRepeaterItemEventArgs):<br />
ItemCreated(self, args)<br />
<br />
override def OnBubbleEvent(source as object, args as EventArgs):<br />
if args isa HierarchicalRepeaterCommandEventArgs:<br />
self.OnItemCommand(cast(HierarchicalRepeaterCommandEventArgs, args))<br />
return true<br />
<br />
return false<br />
<br />
#endregion<br />
<br />
override def PerformDataBinding():<br />
self.Controls.Clear()<br />
self.ClearChildViewState()<br />
self.CreateControlHierarchy(true)<br />
<br />
override def CreateChildControls():<br />
self.Controls.Clear()<br />
<br />
unless (self.ViewState['itemGraph'] is null):<br />
CreateControlHierarchy(false)<br />
<br />
protected virtual def CreateControlHierarchy(useDataSource as bool):<br />
_itemArray = List[of HierarchicalRepeaterItem]()<br />
<br />
if useDataSource:<br />
CreateControlHierarchyRecursive(GetData('').Select(), 0)<br />
<br />
<br />
sb = StringBuilder()<br />
for it in _itemArray:<br />
if it.ItemType == HierarchicalListItemType.Header:<br />
sb.Append('H')<br />
elif it.ItemType == HierarchicalListItemType.ItemHeader:<br />
sb.Append('h')<br />
elif it.ItemType == HierarchicalListItemType.Item:<br />
sb.Append('i')<br />
elif it.ItemType == HierarchicalListItemType.ItemFooter:<br />
sb.Append('f')<br />
elif it.ItemType == HierarchicalListItemType.Footer:<br />
sb.Append('F')<br />
<br />
self.ViewState['itemGraph'] = sb.ToString()<br />
else:<br />
depth = 0<br />
dataItemIndex = 0<br />
displayIndex = 0<br />
itemGraph = cast(string, self.ViewState['itemGraph'])<br />
for chr in itemGraph:<br />
repeaterItem as HierarchicalRepeaterItem<br />
if chr == char('H'):<br />
repeaterItem = HierarchicalRepeaterItem(0, displayIndex++, depth++, HierarchicalListItemType.Header)<br />
elif chr == char('h'):<br />
repeaterItem = HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemHeader)<br />
elif chr == char('i'):<br />
repeaterItem = HierarchicalRepeaterItem(dataItemIndex++, displayIndex++, depth, HierarchicalListItemType.Item)<br />
elif chr == char('f'):<br />
repeaterItem = HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemFooter)<br />
elif chr == char('F'):<br />
repeaterItem = HierarchicalRepeaterItem(0, displayIndex++, --depth, HierarchicalListItemType.Footer)<br />
<br />
InitializeItem(repeaterItem)<br />
self.Controls.Add(repeaterItem)<br />
<br />
self.ChildControlsCreated = true<br />
<br />
protected virtual def CreateControlHierarchyRecursive(enumerable as IHierarchicalEnumerable, depth as int):<br />
dataIndex = 0<br />
displayIndex = 0<br />
<br />
header = self.CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.Header, null, false)<br />
_itemArray.Add(header)<br />
<br />
for it in enumerable:<br />
data = enumerable.GetHierarchyData(it)<br />
<br />
itemHeader = self.CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemHeader, null, false)<br />
_itemArray.Add(itemHeader)<br />
<br />
item = self.CreateItem(dataIndex++, displayIndex++, depth, HierarchicalListItemType.Item, data, true)<br />
_itemArray.Add(item)<br />
if data.HasChildren:<br />
CreateControlHierarchyRecursive(data.GetChildren(), depth+1)<br />
<br />
itemFooter = self.CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemFooter, null, false)<br />
_itemArray.Add(itemFooter)<br />
<br />
footer = self.CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.Footer, null, false)<br />
_itemArray.Add(footer) <br />
<br />
protected virtual def CreateItem(dataItemIndex as int, displayIndex as int, depth as int, itemType as HierarchicalListItemType, dataItem as object, dataBind as bool) as HierarchicalRepeaterItem:<br />
repeaterItem = HierarchicalRepeaterItem(dataItemIndex, displayIndex, depth, itemType)<br />
InitializeItem(repeaterItem)<br />
self.Controls.Add(repeaterItem)<br />
args = HierarchicalRepeaterItemEventArgs(repeaterItem)<br />
self.OnItemCreated(args)<br />
<br />
if dataBind:<br />
repeaterItem.DataItem = dataItem<br />
repeaterItem.DataBind()<br />
self.OnItemDataBound(args)<br />
<br />
return repeaterItem<br />
<br />
protected virtual def InitializeItem(item as HierarchicalRepeaterItem):<br />
template as ITemplate = TemplateCollection.GetTemplate(item.Depth, item.ItemType)<br />
if template is null:<br />
if item.ItemType == HierarchicalListItemType.Header:<br />
template = _headerTemplate<br />
elif item.ItemType == HierarchicalListItemType.Footer:<br />
template = _footerTemplate<br />
elif item.ItemType == HierarchicalListItemType.ItemHeader:<br />
template = _itemHeaderTemplate<br />
elif item.ItemType == HierarchicalListItemType.ItemFooter:<br />
template = _itemFooterTemplate<br />
elif item.ItemType == HierarchicalListItemType.Item:<br />
template = _itemTemplate <br />
else:<br />
raise ApplicationException('invalid item type')<br />
<br />
template.InstantiateIn(item) unless template is null<br />
<br />
<br />
class HierarchicalRepeaterItem(Control, INamingContainer, IDataItemContainer):<br />
[property(DataItem)]<br />
private _dataItem as object<br />
<br />
[getter(DataItemIndex)]<br />
private _dataItemIndex as int<br />
<br />
[getter(DisplayIndex)]<br />
private _displayIndex as int<br />
<br />
[getter(Depth)]<br />
private _depth as int<br />
<br />
[getter(ItemType)]<br />
private _itemType as HierarchicalListItemType <br />
<br />
override def OnBubbleEvent(sender as object, e as EventArgs):<br />
if e isa CommandEventArgs:<br />
e2 = HierarchicalRepeaterCommandEventArgs(self, sender, cast(CommandEventArgs, e))<br />
super.RaiseBubbleEvent(self, e2)<br />
return true<br />
<br />
return false<br />
<br />
def constructor(dataItemIndex as int, displayIndex as int, depth as int, listType as HierarchicalListItemType):<br />
_dataItemIndex = dataItemIndex<br />
_displayIndex = displayIndex<br />
_depth = depth<br />
_itemType = listType<br />
|
|
|
|
|
Kewl Work,
I didnt know I had a recursion problem or was just to lazy to figure it out. I am glad that the control was helpful. I was beginning to question if I even thought it was useful
Hopefully, when I have more time to focus on the control I will fix all the outstanding bugs. I will use your code as a base.
|
|
|
|
|
Hi jw, thanks for the code.
Unfortunatly I have some problems translating to C#, especially with the main class 'HierarchicalRepeater'. Could you please explain in a view words which changes you made in this class and why you did so? Understanding the problem you obviously solved would be very helpful for me.
I will go on to translate and will then publish the result in C# in this thread.
Thanks, Andreas
|
|
|
|
|
You can find language features at http://boo.codehaus.org. It is basically a hybrid of c# and python.
1) Instead of { } to signify code blocks, : and indentation is used. So:
<br />
protected override void PerformDataBinding()<br />
{<br />
this.Controls.Clear()<br />
this.ClearChildViewState()<br />
this.CreateControlHierarchy(true)<br />
}<br />
<br />
override def PerformDataBinding():<br />
self.Controls.Clear()<br />
self.ClearChildViewState()<br />
self.CreateControlHierarchy(true)<br />
<br />
2) properties
<br />
[property(FooterTemplate)]<br />
_footerTemplate as ITemplate<br />
<br />
ITemplate _footerTemplate;<br />
public ITemplate FooterTemplate<br />
{<br />
get { return _footerTemplate; }<br />
set { _footerTemplate = value; }<br />
}<br />
3) for is like foreach in c#.
4) unless test: if !test { }
You should read this article if you haven't already: http://msdn2.microsoft.com/en-US/library/aa479322.asp
It is my little bible of templated databound controls. I essentially used your code base and adapted it to fit the best practices laid down in the article.
1) CreateChildControls builds the control from viewstate. PerformDataBinding builds the control from data. Look up PerformDataBinding in the sdk - it is a better choice than OnDataBinding. Even better, look at it using reflector.net
2) I create a method, as per the article, called CreateControlHierarchy which takes an argument whether to build the control from viewstate or from the datasource. If from the datasource, we call CreateControlHierarchyRecursive.
3) I think the trickiest part is tracking viewstate. In a linear scenario, we know where things go. For example, a datagrid:
header
item
item
item
footer
So we can just save 3 to !ItemCount. But a hierarchical databound control is harder, especially because you allow for such flexible templates, which is cool btw:
header
itemheader
item
header
item header
item
item footer
item header
header
item header
footer
footer
The point of that is just to show it gets real messy because you don't know when you should create a header, item header, item, item footer or footer on postback. My solution was to serialize it this way:
<br />
After the control is generated from the datasource, I create a string that looks like this for example: HhifhifHhifFF and save it to viewstate. Then, on postback, H tells me to create a Header, h an item header, etc. Then viewstate can do its magic.
|
|
|
|
|
Thanks for your comments! I already did the translation and the control seems to work
After some debugging I found one issue that was not working correct.
This may be an issue either in your code or in my translation.
To reproduce it, use a templatecolletion item for depth=0 and itemtype = ItemHeader.
The template will not be used when the children are built from viewstate.
So I changed the code in the function CreateControlHierarchy - in the branch where the HierarchicalRepeaterItem are created from the viewstate:
1. initial value for the depth: int depth = -1;
2. change depth++ to ++depth
3. change --depth to depth--
To debug the failure insert in the function InitializeItem:
System.Diagnostics.Debug.WriteLine(item.Depth)
and compare the output when the data comes from the datasource and when it comes from the viewstate
Regards, Andreas
|
|
|
|
|
Here is my translation to C#. Feel free to search for failures and comment them:
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.ComponentModel;<br />
using System.Text;<br />
using System.Web.UI;<br />
using System.Web.UI.WebControls;<br />
using System.Web;<br />
using System.Security.Permissions;<br />
<br />
namespace WebCustomControls<br />
{<br />
public delegate void HierarchicalRepeaterItemEventHandler(object sender, HierarchicalRepeaterItemEventArgs e);<br />
public delegate void HierarchicalRepeaterCommandEventHandler(object sender, HierarchicalRepeaterCommandEventArgs e);<br />
<br />
public enum HierarchicalListItemType<br />
{<br />
Header,<br />
Footer,<br />
ItemHeader,<br />
ItemFooter,<br />
Item<br />
}<br />
<br />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
public class HierarchicalRepeaterItemEventArgs : EventArgs<br />
{<br />
private HierarchicalRepeaterItem myItem;<br />
<br />
public HierarchicalRepeaterItemEventArgs(HierarchicalRepeaterItem item)<br />
{<br />
myItem = item;<br />
}<br />
<br />
public HierarchicalRepeaterItem Item<br />
{<br />
get<br />
{<br />
return myItem;<br />
}<br />
}<br />
}<br />
<br />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
public class HierarchicalRepeaterCommandEventArgs : CommandEventArgs<br />
{<br />
private object myCommandSource;<br />
private HierarchicalRepeaterItem myItem;<br />
<br />
public HierarchicalRepeaterCommandEventArgs(<br />
HierarchicalRepeaterItem item,<br />
object commandSource,<br />
CommandEventArgs originalArgs)<br />
: base(originalArgs)<br />
{<br />
myItem = item;<br />
myCommandSource = commandSource;<br />
}<br />
<br />
public object CommandSource<br />
{<br />
get<br />
{<br />
return myCommandSource;<br />
}<br />
}<br />
<br />
public HierarchicalRepeaterItem Item<br />
{<br />
get<br />
{<br />
return myItem;<br />
}<br />
}<br />
}<br />
<br />
public class ItemTemplateList : List<ItemTemplate><br />
{<br />
public ItemTemplate GetTemplate(int depth, HierarchicalListItemType listItemType)<br />
{<br />
foreach (ItemTemplate item in this)<br />
{<br />
if ((item.Depth == depth) && (item.ListItemType == listItemType))<br />
return item;<br />
}<br />
return null;<br />
}<br />
<br />
protected void InsertTemplate(int index, ItemTemplate item)<br />
{<br />
if (GetTemplate(item.Depth, item.ListItemType) == null)<br />
throw new ArgumentException("ITemplate Definition already Exists");<br />
else<br />
base.Insert(index, item);<br />
}<br />
}<br />
<br />
public class ItemTemplate : ITemplate<br />
{<br />
private int myDepth;<br />
private HierarchicalListItemType myListItemType;<br />
private ITemplate myTemplate;<br />
<br />
<br />
public int Depth<br />
{<br />
get<br />
{<br />
return myDepth;<br />
}<br />
set<br />
{<br />
myDepth = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate Template<br />
{<br />
get<br />
{<br />
return myTemplate;<br />
}<br />
set<br />
{<br />
myTemplate = value;<br />
}<br />
}<br />
<br />
public HierarchicalListItemType ListItemType<br />
{<br />
get<br />
{<br />
return myListItemType;<br />
}<br />
set<br />
{<br />
myListItemType = value;<br />
}<br />
}<br />
<br />
<br />
public void InstantiateIn(Control container)<br />
{<br />
myTemplate.InstantiateIn(container);<br />
}<br />
}<br />
<br />
public class HierarchicalRepeaterItem : Control, INamingContainer, IDataItemContainer<br />
{<br />
private object myDataItem;<br />
private int myDataItemIndex;<br />
private int myDisplayIndex;<br />
private int myDepth;<br />
private HierarchicalListItemType myItemType;<br />
<br />
<br />
public HierarchicalRepeaterItem(int dataItemIndex, int displayIndex, int depth, HierarchicalListItemType itemType)<br />
{<br />
myDataItemIndex = dataItemIndex;<br />
myDisplayIndex = displayIndex;<br />
myDepth = depth;<br />
myItemType = itemType;<br />
}<br />
<br />
<br />
public object DataItem<br />
{<br />
get<br />
{<br />
return myDataItem;<br />
}<br />
set<br />
{<br />
myDataItem = value;<br />
}<br />
}<br />
<br />
public int DataItemIndex<br />
{<br />
get<br />
{<br />
return myDataItemIndex;<br />
}<br />
}<br />
<br />
public int DisplayIndex<br />
{<br />
get<br />
{<br />
return myDisplayIndex;<br />
}<br />
}<br />
<br />
public int Depth<br />
{<br />
get<br />
{<br />
return myDepth;<br />
}<br />
}<br />
<br />
public HierarchicalListItemType ItemType<br />
{<br />
get<br />
{<br />
return myItemType;<br />
}<br />
}<br />
<br />
<br />
protected override bool OnBubbleEvent(object source, EventArgs e)<br />
{<br />
if (e is CommandEventArgs)<br />
{<br />
HierarchicalRepeaterCommandEventArgs e2 = new HierarchicalRepeaterCommandEventArgs(this, source, (CommandEventArgs)e);<br />
base.RaiseBubbleEvent(this, e2);<br />
return true;<br />
}<br />
return false;<br />
}<br />
}<br />
<br />
[DefaultEvent("ItemCommand"), DefaultProperty("DataSource"), ParseChildren(true), PersistChildren(false)]<br />
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]<br />
[ToolboxData("<{0}:HierarchicalRepeater runat=server></{0}:HierarchicalRepeater>")]<br />
public class HierarchicalRepeater : HierarchicalDataBoundControl, INamingContainer<br />
{<br />
private ItemTemplateList myTemplateList = new ItemTemplateList();<br />
private ITemplate myHeaderTemplate;<br />
private ITemplate myFooterTemplate;<br />
private ITemplate myItemTemplate;<br />
private ITemplate myItemHeaderTemplate;<br />
private ITemplate myItemFooterTemplate;<br />
private List<HierarchicalRepeaterItem> myItemArray;<br />
private List<HierarchicalRepeaterItem> myItems;<br />
private DataSourceSelectArguments myArguments;<br />
<br />
<br />
public event HierarchicalRepeaterCommandEventHandler ItemCommand;<br />
public event HierarchicalRepeaterItemEventHandler ItemDataBound;<br />
public event HierarchicalRepeaterItemEventHandler ItemCreated;<br />
<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ItemTemplateList TemplateCollection<br />
{<br />
get<br />
{<br />
return myTemplateList;<br />
}<br />
set<br />
{<br />
myTemplateList = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate HeaderTemplate<br />
{<br />
get<br />
{<br />
return myHeaderTemplate;<br />
}<br />
set<br />
{<br />
myHeaderTemplate = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate FooterTemplate<br />
{<br />
get<br />
{<br />
return myFooterTemplate;<br />
}<br />
set<br />
{<br />
myFooterTemplate = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate ItemTemplate<br />
{<br />
get<br />
{<br />
return myItemTemplate;<br />
}<br />
set<br />
{<br />
myItemTemplate = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate ItemHeaderTemplate<br />
{<br />
get<br />
{<br />
return myItemHeaderTemplate;<br />
}<br />
set<br />
{<br />
myItemHeaderTemplate = value;<br />
}<br />
}<br />
<br />
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]<br />
public ITemplate ItemFooterTemplate<br />
{<br />
get<br />
{<br />
return myItemFooterTemplate;<br />
}<br />
set<br />
{<br />
myItemFooterTemplate = value;<br />
}<br />
}<br />
<br />
public List<HierarchicalRepeaterItem> Items<br />
{<br />
get<br />
{<br />
if (myItems == null)<br />
{<br />
if (myItemArray == null)<br />
this.EnsureChildControls();<br />
<br />
myItems = new List<HierarchicalRepeaterItem>(myItemArray);<br />
}<br />
<br />
return myItems;<br />
}<br />
}<br />
<br />
protected DataSourceSelectArguments SelectArguments<br />
{<br />
get<br />
{<br />
if (myArguments == null)<br />
myArguments = DataSourceSelectArguments.Empty;<br />
<br />
return myArguments;<br />
}<br />
}<br />
<br />
protected override HtmlTextWriterTag TagKey<br />
{<br />
get<br />
{<br />
return HtmlTextWriterTag.Div;<br />
}<br />
}<br />
<br />
<br />
protected override void PerformDataBinding()<br />
{<br />
this.Controls.Clear();<br />
this.ClearChildViewState();<br />
this.CreateControlHierarchy(true);<br />
}<br />
<br />
protected override void CreateChildControls()<br />
{<br />
Controls.Clear();<br />
if (ViewState["itemGraph"] != null)<br />
CreateControlHierarchy(false);<br />
}<br />
<br />
protected override bool OnBubbleEvent(object source, EventArgs e)<br />
{<br />
HierarchicalRepeaterCommandEventArgs e2 = e as HierarchicalRepeaterCommandEventArgs;<br />
if (e2 != null)<br />
{<br />
OnItemCommand(e2);<br />
return true;<br />
}<br />
return false;<br />
}<br />
<br />
protected virtual void CreateControlHierarchy(bool useDataSource)<br />
{<br />
myItemArray = new List<HierarchicalRepeaterItem>();<br />
if (useDataSource)<br />
{<br />
CreateControlHierarchyRecursive(GetData("").Select(), 0);<br />
<br />
StringBuilder sb = new StringBuilder();<br />
foreach (HierarchicalRepeaterItem item in myItemArray)<br />
{<br />
if (item.ItemType == HierarchicalListItemType.Header)<br />
sb.Append("H");<br />
else if (item.ItemType == HierarchicalListItemType.ItemHeader)<br />
sb.Append("h");<br />
else if (item.ItemType == HierarchicalListItemType.Item)<br />
sb.Append("i");<br />
else if (item.ItemType == HierarchicalListItemType.ItemFooter)<br />
sb.Append("f");<br />
else if (item.ItemType == HierarchicalListItemType.Footer)<br />
sb.Append("F");<br />
}<br />
<br />
ViewState["itemGraph"] = sb.ToString();<br />
}<br />
else<br />
{<br />
int depth = -1;<br />
int dataItemIndex = 0;<br />
int displayIndex = 0;<br />
string itemGraph = (string)ViewState["itemGraph"];<br />
<br />
HierarchicalRepeaterItem repeaterItem = null;<br />
foreach (char c in itemGraph)<br />
{<br />
if (c.Equals('H'))<br />
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, ++depth, HierarchicalListItemType.Header);<br />
if (c.Equals('h'))<br />
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemHeader);<br />
if (c.Equals('i'))<br />
repeaterItem = new HierarchicalRepeaterItem(dataItemIndex++, displayIndex++, depth, HierarchicalListItemType.Item);<br />
if (c.Equals('f'))<br />
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemFooter);<br />
if (c.Equals('F'))<br />
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth--, HierarchicalListItemType.Footer);<br />
<br />
InitializeItem(repeaterItem);<br />
Controls.Add(repeaterItem);<br />
}<br />
}<br />
ChildControlsCreated = true;<br />
}<br />
<br />
protected virtual void CreateControlHierarchyRecursive(IHierarchicalEnumerable enumerable, int depth)<br />
{<br />
int dataIndex = 0;<br />
int displayIndex = 0;<br />
<br />
HierarchicalRepeaterItem header = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.Header, null, false);<br />
myItemArray.Add(header);<br />
<br />
foreach (IHierarchyData data in enumerable)<br />
{<br />
HierarchicalRepeaterItem itemHeader = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemHeader, null, false);<br />
myItemArray.Add(itemHeader);<br />
<br />
HierarchicalRepeaterItem item = CreateItem(dataIndex++, displayIndex++, depth, HierarchicalListItemType.Item, data, true);<br />
myItemArray.Add(item);<br />
<br />
if (data.HasChildren)<br />
CreateControlHierarchyRecursive(data.GetChildren(), depth + 1);<br />
<br />
HierarchicalRepeaterItem itemFooter = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemFooter, null, false);<br />
myItemArray.Add(itemFooter);<br />
}<br />
<br />
HierarchicalRepeaterItem footer = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.Footer, null, false);<br />
myItemArray.Add(footer);<br />
}<br />
<br />
protected virtual HierarchicalRepeaterItem CreateItem(int dataItemIndex, int displayIndex, int depth, HierarchicalListItemType itemType, object dataItem, bool dataBind)<br />
{<br />
HierarchicalRepeaterItem repeaterItem = new HierarchicalRepeaterItem(dataItemIndex, displayIndex, depth, itemType);<br />
InitializeItem(repeaterItem);<br />
Controls.Add(repeaterItem);<br />
<br />
HierarchicalRepeaterItemEventArgs e = new HierarchicalRepeaterItemEventArgs(repeaterItem);<br />
OnItemCreated(e);<br />
<br />
if (dataBind)<br />
{<br />
repeaterItem.DataItem = dataItem;<br />
repeaterItem.DataBind();<br />
OnItemDataBound(e);<br />
}<br />
<br />
return repeaterItem;<br />
}<br />
<br />
protected virtual void InitializeItem(HierarchicalRepeaterItem item)<br />
{<br />
System.Diagnostics.Debug.WriteLine(item.DisplayIndex);<br />
ITemplate template = TemplateCollection.GetTemplate(item.Depth, item.ItemType);<br />
<br />
if (template == null)<br />
{<br />
if (item.ItemType == HierarchicalListItemType.Header)<br />
template = myHeaderTemplate;<br />
else if (item.ItemType == HierarchicalListItemType.Footer)<br />
template = myFooterTemplate;<br />
else if (item.ItemType == HierarchicalListItemType.ItemHeader)<br />
template = myItemHeaderTemplate;<br />
else if (item.ItemType == HierarchicalListItemType.ItemFooter)<br />
template = myItemFooterTemplate;<br />
else if (item.ItemType == HierarchicalListItemType.Item)<br />
template = myItemTemplate;<br />
else<br />
throw new ApplicationException("Invalid ItemType");<br />
}<br />
else<br />
{<br />
bool a = true;<br />
}<br />
<br />
if (template != null)<br />
template.InstantiateIn(item);<br />
}<br />
<br />
protected virtual void OnItemCommand(HierarchicalRepeaterCommandEventArgs e)<br />
{<br />
if (ItemCommand != null)<br />
ItemCommand(this, e);<br />
}<br />
<br />
protected virtual void OnItemDataBound(HierarchicalRepeaterItemEventArgs e)<br />
{<br />
if (ItemDataBound != null)<br />
ItemDataBound(this, e);<br />
}<br />
<br />
protected virtual void OnItemCreated(HierarchicalRepeaterItemEventArgs e)<br />
{<br />
if (ItemCreated != null)<br />
ItemCreated(this, e);<br />
}<br />
}<br />
}<br />
|
|
|
|
|
Thanks for your efforts on this control guys - it works really well now.
I made a couple of changes to the Hierarchical Repeater. The main difference is that I call OnItemDataBound for ItemHeaderTemplate and ItemFooterTemplate. I think this should be fine - I use this sometimes to change the item's header or footer based on the data. Just be sure to check the ItemType during ItemDataBound handlers.
Also the object code used a class called "List" which is not part of the .NET framework. I changed this to "List<object>".
[DefaultEvent("ItemCommand"), DefaultProperty("DataSource"), ParseChildren(true), PersistChildren(false)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[ToolboxData("<{0}:HierarchicalRepeater runat="server">")]
public class HierarchicalRepeater : HierarchicalDataBoundControl, INamingContainer
{
private ItemTemplateCollection myTemplateList = new ItemTemplateCollection();
private ITemplate myHeaderTemplate;
private ITemplate myFooterTemplate;
private ITemplate myItemTemplate;
private ITemplate myItemHeaderTemplate;
private ITemplate myItemFooterTemplate;
private List<object> myItemArray;
private List<object> myItems;
private DataSourceSelectArguments myArguments;
public event HierarchicalRepeaterCommandEventHandler ItemCommand;
public event HierarchicalRepeaterItemEventHandler ItemDataBound;
public event HierarchicalRepeaterItemEventHandler ItemCreated;
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ItemTemplateCollection TemplateCollection
{
get
{
return myTemplateList;
}
set
{
myTemplateList = value;
}
}
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ITemplate HeaderTemplate
{
get
{
return myHeaderTemplate;
}
set
{
myHeaderTemplate = value;
}
}
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ITemplate FooterTemplate
{
get
{
return myFooterTemplate;
}
set
{
myFooterTemplate = value;
}
}
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ITemplate ItemTemplate
{
get
{
return myItemTemplate;
}
set
{
myItemTemplate = value;
}
}
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ITemplate ItemHeaderTemplate
{
get
{
return myItemHeaderTemplate;
}
set
{
myItemHeaderTemplate = value;
}
}
[PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(IDataItemContainer))]
public ITemplate ItemFooterTemplate
{
get
{
return myItemFooterTemplate;
}
set
{
myItemFooterTemplate = value;
}
}
public List<object> Items
{
get
{
if (myItems == null)
{
if (myItemArray == null)
EnsureChildControls();
if (myItemArray == null)
throw new NullReferenceException("Item Array was not created during CreateChildControls.");
myItems = new List<object>(myItemArray);
}
return myItems;
}
}
protected DataSourceSelectArguments SelectArguments
{
get
{
if (myArguments == null)
myArguments = DataSourceSelectArguments.Empty;
return myArguments;
}
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
protected override void PerformDataBinding()
{
Controls.Clear();
ClearChildViewState();
CreateControlHierarchy(true);
}
protected override void CreateChildControls()
{
Controls.Clear();
if (ViewState["itemGraph"] != null)
CreateControlHierarchy(false);
}
protected override bool OnBubbleEvent(object source, EventArgs e)
{
HierarchicalRepeaterCommandEventArgs e2 = e as HierarchicalRepeaterCommandEventArgs;
if (e2 != null)
{
OnItemCommand(e2);
return true;
}
return false;
}
protected virtual void CreateControlHierarchy(bool useDataSource)
{
myItemArray = new List<object>();
if (useDataSource)
{
CreateControlHierarchyRecursive(GetData("").Select(), 0);
StringBuilder sb = new StringBuilder();
foreach (HierarchicalRepeaterItem item in myItemArray)
{
if (item.ItemType == HierarchicalListItemType.Header)
sb.Append("H");
else if (item.ItemType == HierarchicalListItemType.ItemHeader)
sb.Append("h");
else if (item.ItemType == HierarchicalListItemType.Item)
sb.Append("i");
else if (item.ItemType == HierarchicalListItemType.ItemFooter)
sb.Append("f");
else if (item.ItemType == HierarchicalListItemType.Footer)
sb.Append("F");
}
ViewState["itemGraph"] = sb.ToString();
}
else
{
int depth = -1;
int dataItemIndex = 0;
int displayIndex = 0;
string itemGraph = (string)ViewState["itemGraph"];
HierarchicalRepeaterItem repeaterItem = null;
foreach (char c in itemGraph)
{
switch (c)
{
case ('H'):
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, ++depth, HierarchicalListItemType.Header);
break;
case ('h'):
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemHeader);
break;
case ('i'):
repeaterItem = new HierarchicalRepeaterItem(dataItemIndex++, displayIndex++, depth, HierarchicalListItemType.Item);
break;
case ('f'):
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth, HierarchicalListItemType.ItemFooter);
break;
case ('F'):
repeaterItem = new HierarchicalRepeaterItem(0, displayIndex++, depth--, HierarchicalListItemType.Footer);
break;
}
InitializeItem(repeaterItem);
Controls.Add(repeaterItem);
}
}
ChildControlsCreated = true;
}
protected virtual void CreateControlHierarchyRecursive(IHierarchicalEnumerable enumerable, int depth)
{
int dataIndex = 0;
int displayIndex = 0;
HierarchicalRepeaterItem header = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.Header, null, false);
myItemArray.Add(header);
foreach (IHierarchyData data in enumerable)
{
HierarchicalRepeaterItem itemHeader = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemHeader, data, true);
myItemArray.Add(itemHeader);
HierarchicalRepeaterItem item = CreateItem(dataIndex++, displayIndex++, depth, HierarchicalListItemType.Item, data, true);
myItemArray.Add(item);
if (data.HasChildren)
CreateControlHierarchyRecursive(data.GetChildren(), depth + 1);
HierarchicalRepeaterItem itemFooter = CreateItem(-1, displayIndex++, depth, HierarchicalListItemType.ItemFooter, data, true);
myItemArray.Add(itemFooter);
}
HierarchicalRepeaterItem footer = CreateItem(-1, displayIndex, depth, HierarchicalListItemType.Footer, null, false);
myItemArray.Add(footer);
}
protected virtual HierarchicalRepeaterItem CreateItem(int dataItemIndex, int displayIndex, int depth, HierarchicalListItemType itemType, object dataItem, bool dataBind)
{
HierarchicalRepeaterItem repeaterItem = new HierarchicalRepeaterItem(dataItemIndex, displayIndex, depth, itemType);
InitializeItem(repeaterItem);
Controls.Add(repeaterItem);
HierarchicalRepeaterItemEventArgs e = new HierarchicalRepeaterItemEventArgs(repeaterItem);
OnItemCreated(e);
if (dataBind)
{
repeaterItem.DataItem = dataItem;
repeaterItem.DataBind();
OnItemDataBound(e);
}
return repeaterItem;
}
protected virtual void InitializeItem(HierarchicalRepeaterItem item)
{
System.Diagnostics.Debug.WriteLine(item.DisplayIndex);
ITemplate template = TemplateCollection.GetTemplate(item.Depth, item.ItemType);
if (template == null)
{
if (item.ItemType == HierarchicalListItemType.Header)
template = myHeaderTemplate;
else if (item.ItemType == HierarchicalListItemType.Footer)
template = myFooterTemplate;
else if (item.ItemType == HierarchicalListItemType.ItemHeader)
template = myItemHeaderTemplate;
else if (item.ItemType == HierarchicalListItemType.ItemFooter)
template = myItemFooterTemplate;
else if (item.ItemType == HierarchicalListItemType.Item)
template = myItemTemplate;
else
throw new ApplicationException("Invalid ItemType");
}
if (template != null)
template.InstantiateIn(item);
}
protected virtual void OnItemCommand(HierarchicalRepeaterCommandEventArgs e)
{
if (ItemCommand != null)
ItemCommand(this, e);
}
protected virtual void OnItemDataBound(HierarchicalRepeaterItemEventArgs e)
{
if (ItemDataBound != null)
ItemDataBound(this, e);
}
protected virtual void OnItemCreated(HierarchicalRepeaterItemEventArgs e)
{
if (ItemCreated != null)
ItemCreated(this, e);
}
}
</object></object></object></object></object>
|
|
|
|
|
Hi Gary,
It seems that ViewState and/or ControlState of your control does not work. I want to use your control for a hierarchical view of complex user controls that need the viewstate.
Can you please give me a hint how to get the viewstate working?
Thanks for your answer and your code!
Andreas
|
|
|
|
|
I dont quite understand your problem? Could you explain a bit more about your viewstate problem?
Gary
|
|
|
|
|
Thanks for your answer. Please try this simple example, it compares the behavior of a datalist and your hierarchical repeater:
<code>
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="WebCustomControls" Namespace="WebCustomControls" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Unbenannte Seite</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DataList ID="DataList1" runat="server">
<ItemTemplate>
Text:
<asp:Label ID="Label1" runat="server" Text='<%# Eval("Item") %>'></asp:Label>
</ItemTemplate>
</asp:DataList>
<p />
<cc1:HierarchicalRepeater ID="HierarchicalRepeater1" runat="server">
<TemplateCollection>
<cc1:ItemTemplate Depth="0" ListItemType="ItemHeaderTemplate">
<Template>
<div style="margin-left: 0;">
</Template>
</cc1:ItemTemplate>
</TemplateCollection>
<ItemHeaderTemplate>
<div style="margin-left: 20px;">
</ItemHeaderTemplate>
<ItemTemplate>
<div class="WorkStepPanel">
<asp:Label ID="Label1" runat="server" Text='<%# Eval("Item") %>'></asp:Label>
</div>
</ItemTemplate>
<ItemFooterTemplate>
</div>
</ItemFooterTemplate>
</cc1:HierarchicalRepeater>
<p />
<asp:Button ID="Button1" runat="server" PostBackUrl="~/Default.aspx" Text="Button" />
</div>
</form>
</body>
</html>
</code>
Code behind:
<code>
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.ComponentModel;
using WebCustomControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
DataList1.DataSource = GetData();
DataList1.DataBind();
HierarchicalRepeater1.DataSource = GetData();
HierarchicalRepeater1.DataBind();
}
}
private static HierarchyDataCollection<HierarchyData<string>> GetData()
{
HierarchyDataCollection<HierarchyData<string>> collection =
new HierarchyDataCollection<HierarchyData<string>>();
HierarchyData<string> h0 = new HierarchyData<string>("h0", null);
HierarchyData<string> h1 = new HierarchyData<string>("h1", null);
HierarchyData<string> h2 = new HierarchyData<string>("h2", null);
HierarchyData<string> h3 = new HierarchyData<string>("h3", null);
HierarchyData<string> h1_1 = new HierarchyData<string>("h1_1", h1);
HierarchyData<string> h1_2 = new HierarchyData<string>("h1_2", h1);
HierarchyData<string> h3_1 = new HierarchyData<string>("h3_1", h3);
HierarchyData<string> h1_1_1 = new HierarchyData<string>("h1_1_1", h1_1);
HierarchyData<string> h1_1_2 = new HierarchyData<string>("h1_1_2", h1_1);
HierarchyData<string> h1_1_3 = new HierarchyData<string>("h1_1_3", h1_1);
HierarchyData<string> h1_1_2_1 = new HierarchyData<string>("h1_1_2_1", h1_1_2);
collection.Add(h0);
collection.Add(h1);
collection.Add(h2);
collection.Add(h3);
return collection;
}
}
</code>
EDIT: troubles with < and > - sorry for that!
Andreas
|
|
|
|
|
Try removing the line
base.ClearChildViewState();
in the HierarchicalRepeater
|
|
|
|
|
Sorry, does not help. I also tried Page.RegisterRequiresControlState(this); in OnInit, no change. Unfortunatly my knowledge in developing Controls is poor.
|
|
|
|
|
This jumped out at me while I was reviewing the source:
if (ViewState["!ItemCount"] == null)
{
ViewState["!ItemCount"] = 0;
}
ViewState["!ItemCount"] = 0;
I think this is a case of Paris in the the spring.
|
|
|
|
|
I'm having a similar problem with ViewState in a control I created by extending HierarchicalDataBoundControl.
jwessel wrote: This jumped out at me while I was reviewing the source:
if (ViewState["!ItemCount"] == null)
{
ViewState["!ItemCount"] = 0;
}
ViewState["!ItemCount"] = 0;
I think this is a case of Paris in the the spring.
From this quote it sounds like the problem was resolved but I can't see how.
The code is nonsensical (the if test is not necessary). In addition nothing ever seems to save anything else to this property bag and where it is retrieved the value is never modified.
From what I can see the problem is that control is made to always render directly from the DataSource and that is null on a postback. I'm a bit confused how the ViewState is supposed to work in this case.
|
|
|
|
|
This is exactly what I'm looking for, but I want to be able to attach it to an XMLDataSource. It doesn't seem to be working for me...
|
|
|
|
|
Gary,
This control is exactly what I needed. But the DHTML people working with me are a little bit too "demanding", and I fear that, because of a small problem of implementation, I won't be able to use it. Or, maybe the error is mine; anyway, I decided to humbly ask your help...
Looking at a simplified version of the first example in this article:
<div class="CODE" id="DIV1">
<ul>
<li>Home
<ul>
<li>Node1
<ul>
<li>Node1 1</li>
</ul>
<ul>
<li>Node1 2</li>
</ul>
<ul>
<li>Node1 3</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
There are pairs of <ul></ul> tags that are not really needed. The generated HTML code should be:
<div class="CODE" id="DIV1">
<ul>
<li>Home
<ul>
<li>Node1
<ul>
<li>Node1 1</li>
<li>Node1 2</li>
<li>Node1 3</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
It seems that there are extra calls to <ItemHeaderTemplate> and <ItemFooterTemplate>. <ItemHeaderTemplate> should be called ONLY before the first child node of a given node, and <ItemFooterTemplate> after the last one. Is this correct?
I will try to fix the code, but I decided to first check if this is really a bug, or if this is an expected behaviour...
Any help will be welcome!
- Senna.
|
|
|
|
|
Here is the fix that will render correctly thank you for catching that error. I had a flaw in my hierarchy rendering
protected virtual void CreateControlHierarchyRecursive(IHierarchicalEnumerable dataItems)
{
int itemCount = (int)ViewState["!ItemCount"];
bool firstItem = true;
int depth = 0;
foreach (object e in dataItems)
{
IHierarchyData data = dataItems.GetHierarchyData(e);
depth = GetHierarchyDepth(data);
if (firstItem)
{
HierarchicalRepeaterItem headerItem;
headerItem = this.CreateItem(itemCount, HierarchicalListItemType.HeaderTemplate, depth, m_IsDataBound, data);
m_ItemArray.Add(headerItem);
firstItem = false;
}
HierarchicalRepeaterItem itemHeader;
itemHeader = this.CreateItem(itemCount, HierarchicalListItemType.ItemHeaderTemplate, depth, m_IsDataBound, data);
m_ItemArray.Add(itemHeader);
HierarchicalRepeaterItem item;
item = this.CreateItem(itemCount, HierarchicalListItemType.ItemTemplate, depth, m_IsDataBound, data);
m_ItemArray.Add(item);
CreateControlHierarchyRecursive(data.GetChildren());
HierarchicalRepeaterItem itemFooter;
itemFooter = this.CreateItem(itemCount, HierarchicalListItemType.ItemFooterTemplate, depth, m_IsDataBound, data);
m_ItemArray.Add(itemFooter);
}
if (firstItem == false)
{
HierarchicalRepeaterItem footer = this.CreateItem(itemCount, HierarchicalListItemType.FooterTemplate, depth, m_IsDataBound, null);
m_ItemArray.Add(footer);
}
}
Here is the modified template in example 1.
<cc1:HierarchicalRepeater runat="server" ID="repeater">
<HeaderTemplate><ul></HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><%#Eval("Title") %></ItemTemplate>
<ItemHeaderTemplate><li></ItemHeaderTemplate>
<ItemFooterTemplate></li></ItemFooterTemplate>
</cc1:HierarchicalRepeater>
I will post an update asap. Thank you
-- modified at 1:09 Monday 18th September, 2006
|
|
|
|
|
Gary,
Thanx for the quickest reply I've ever got on a forum...
In fact I first thought that the answer could be using <HeaderTemplate> and <FooterTemplate>, but they didn't work at all... So I did implement a quicky & dirty workaround; it works only when "data" is a SiteMapNode object (I was using the PreviousSibling and NextSibling properties).
I'll try your fix soon. Thank you again, and keep the good work!
- Senna
|
|
|
|
|
Your control is great but you need to rethink your recursion algorithm.
1) You do not need GetHierarchyDepth. This is a sign of a bad algorithm. Instead, do this:
<br />
CreateControlHierarchyRecursive(GetData("").Select(), 0);<br />
...<br />
CreateControlHierarchyRecursive(IHierarchicalEnumerable dataItems, int depth)<br />
...<br />
CreateControlHierarchyRecursive(data.GetChildren(), depth+1);<br />
2) You should not use firstItem. It is voodoo programming and your algorithm still doesn't work. You must decide whether you are going to recurse by level (IHierarchicalEnumerable) or by node (IHierarchyData). You are currently recursing by level but I don't think that's what you want to do. I prefer recursing by node. This alters the initial state (CreateControlHierarchy) and where your loop goes (CreateControlHierarchyRecursive):
<br />
protected virtual def CreateControlHierarchy():<br />
nodes = GetData('').Select()<br />
for node in nodes:<br />
CreateControlHierarchyRecursive(node, 0)<br />
<br />
protected virtual def CreateControlHierarchyRecursive(node as IHierarchyData, depth as int):<br />
itemCount = ViewState['!ItemCount']<br />
<br />
item = self.CreateItem(itemCount, HierarchicalListItemType.ItemTemplate, depth, m_IsDataBound, node)<br />
m_ItemArray.Add(item)<br />
<br />
if node.HasChildren:<br />
header = self.CreateItem(itemCount, HierarchicalListItemType.HeaderTemplate, depth, m_IsDataBound, node)<br />
m_ItemArray.Add(header)<br />
<br />
childNodes = node.GetChildren()<br />
for childNode in childNodes:<br />
CreateControlHierarchyRecursive(<br />
childNodes.GetHierarchyData(childNode),<br />
depth+1<br />
)<br />
<br />
footer = self.CreateItem(itemCount, HierarchicalListItemType.FooterTemplate, depth, m_IsDataBound, node)<br />
m_ItemArray.Add(footer)<br />
3) You do not need Header nor Footer because in a hierarchical data structure, every node is an item. I think I understand why you did it (for the same reason that Repeater has a header/footer) but it is not necessary (for the same reason that Repeater does not heed a header/footer).
|
|
|
|
|
Actually, I retract statement (3). You do need ItemHeader and ItemFooter. For example, a sublist should be nested inside a listitem, not after the list item.
|
|
|
|
|
Gary,
Thanks for the nice control. I wrote a directory - file tree control for Asp.Net using the Hierarchical stuff last year, and was very impressed with the whole design. I used the control to browse my music collection, and select songs into a playlist that I then streamed using Windows Media Server. My treeview control had special code to filter files so that only my media files (*.mp3, *.wmv, etc) were displayed.
I have not thought much about it since then, but reading your article has made me think about using your control or digging up my old treeview control to solve several issues that I have with a current project.
Several questions that I had on looking at your control in relation to the TemplateCollection-ItemTemplate-Depth Attribute.
Question 1:
--------------------
Have you given any though extending the depth attribute to handle wildcards and ranges?
For Example:
<code>
<TemplateCollection>
<!-- Show Depth Zero With A Blue Background -->
<cc1:ItemTemplate Depth="0" ListItemType="ItemTemplate">
<Template>
<div style="color:Yellow;background:Blue;"><%#Eval("Title") %></div>
</Template>
</cc1:ItemTemplate>
<!-- Show all other depths with a gray background -->
<cc1:ItemTemplate Depth="*" ListItemType="ItemTemplate">
<Template>
<div style="color:Yellow;background:gray;"><%#Eval("Title") %></div>
</Template>
</cc1:ItemTemplate>
</TemplateCollection>
</code>
Or perhaps a Depth="<Range>", which would apply the ItemTemplate to the selected ranges.
As bad as I am with regular expressions I hate to suggest it, (I had to write a little expression matcher program to help me write regular expressions.) but I would think you could use a regular expression match to support wildcards, ranges, and even-odd support.
I realize that the "catch all" effect can be created with the following code, but thought I might ask anyway.
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" SiteMapProvider="MenuProvider" />
<cc1:HierarchicalRepeater runat="server" ID="repeater">
<TemplateCollection>
<!-- Top Level Node With A Blue Background -->
<cc1:ItemTemplate Depth="0" ListItemType="ItemTemplate">
<Template>
<div style="color: Yellow;Background:blue;"><%#Eval("Title") %></div>
</Template>
</cc1:ItemTemplate>
<!-- First Level Children Have A Gray Background -->
<cc1:ItemTemplate Depth="1" ListItemType="ItemTemplate">
<Template>
<div style="color: Yellow;Background:gray;"><%#Eval("Title") %></div>
</Template>
</cc1:ItemTemplate>
</TemplateCollection>
<!-- Catch All Template: Items Prefaced With "Hello World" -->
<ItemTemplate><p>Hello World <%#Eval("Title") %></p></ItemTemplate>
<ItemHeaderTemplate><ul></ItemHeaderTemplate>
<ItemFooterTemplate></ul></ItemFooterTemplate>
</cc1:HierarchicalRepeater>
Question 2:
--------------------
Actually the code presented above will not run because I added the comments to the code for this message. Trying to compile the code results in the following three errors being generated:
Literal content ('<!-- Top Level Node With A Blue Background -->') is not allowed within a 'WebCustomControls.ItemTemplateCollection'.
Literal content ('<!-- First Level Children Have A Gray Background -->') is not allowed within a 'WebCustomControls.ItemTemplateCollection'.
Content ('<!-- Catch All Template: Items Prefaced With "Hello World" -->') does not match any properties within a 'WebCustomControls.HierarchicalRepeater', make sure it is well-formed.
So... Would you be willing to further extend your control to ignore HTML comments?
Again, thanks for the great control and well written article.
- ward
|
|
|
|
|
Ward,
Thank you for your response to my control and article. As far as Extending the Template Collection to include wildcards or RegularExpression's I will look into adding a ExpressionFilter to handle the situation you described. Its funny you mentioned that as I have been formulating ways to make the templateCollection more flexible in that regard.
As far as your second question regarding comments, there is a special syntax that will allow for comments in Asp.net
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconserver-sidecomments.asp[^]
Your code comment:
Acceptable code comment:
<%-- First Level Children Have A Gray Background --%>
Again thank you for your comments and hopefully I will have some more feedback to first request
|
|
|
|
|
I have a bunch of classes with methods which return IQueryable Collections which I can set to any datasources. How would I use this with your control?
IQueryable is part of microsofts DLINQ/LINQ o/r mapping
Also I would like to say great article and great control, haev you any examples of it working with real data?
Thanks!
-- modified at 11:20 Wednesday 26th July, 2006
|
|
|
|
|
Thank you for the kudos. I have not worked with DLINQ as of yet, sorry.
Most of the examples I provide are trivial in nature one uses the SiteMapDatasource and the other is posted above. I will try to create a complex example that would help illustrate its functionality in a future update.
Thank you.
Gary Vidal
|
|
|
|
|