"Remi Hugnon" <r.******@voila .fr> wrote in message
news:40******** *************** @news.easynet.f r...
Hi,
I want to build an expandable treeview. I saw some code on VB but I know
only C#
I plan for my class that each nodes has its own id (using span for
example).
Into a single aspx page without any other control, it's very easy because
I play on URL and parameters and refresh my aspx.
But in a screen having 2 treeview, I have the param and other controls.
How can I organize my object in order to catch (meaning server side) wich node
had been clicked ?
Ok, I've got an example. It makes this post a bit long, but I've also
included a zip file with the sources.
The example consists of three classes, DemoTreeView, DemoTreeViewNod e and
DemoTreeViewNod eCollection:
DemoTreeView Class declaration:
/// <summary>
/// A simple Tree View control as an example for Remi Hugnon
(r.******@voila .fr)
/// </summary>
[DefaultProperty ("Text"),
ToolboxData("<{ 0}:DemoTreeView
runat=server></{0}:DemoTreeVie w>")]
[ParseChildren(t rue, "DemoTreeViewNo des")]
public class DemoTreeView : WebControl, IPostBackEventH andler
Note the use of the ParseChildren attribute. This allows the page parser to
interpret the content inside of the DemoTreeView as items to be added to the
DemoTreeNodes property. Each element will be parsed and then added to the
DemoTreeNodes collection via IList.Add.
IPostBackEventH andler is implemented in order to provide a Click event when
a node is clicked.
DemoTreeViewNod es property:
#region Public Properties
/// <summary>
/// Gets the top-level nodes in the tree
/// </summary>
[Category("Behav ior"),
Description("Th e top-level nodes in the tree"),
DesignerSeriali zationVisibilit y(DesignerSeria lizationVisibil ity.Content),
NotifyParentPro perty(true),
PersistenceMode (PersistenceMod e.InnerDefaultP roperty)]
public DemoTreeViewNod eCollection DemoTreeViewNod es
{
get
{
if (_nodes == null)
_nodes = new DemoTreeViewNod eCollection();
return _nodes;
}
}
#endregion
This is a simple implementation of a read-only property. The interesting
things are in the attributes. The PersistenceMode is set to match the
ParseChildren attribute on the class. DesignerSeriali zationVisibilit y is set
to cause the property to be persisted as content between the start and end
tags.
#region Rendering
/// <summary>
/// Renders the contents of the control - what's between the
beginning and ending tags
/// </summary>
/// <param name="writer">T he HtmlTextWriter to render to</param>
protected override void RenderContents( HtmlTextWriter writer)
{
DemoTreeViewNod eCollection nodes = DemoTreeViewNod es;
RenderNodes(wri ter, nodes, string.Empty, 0);
base.RenderCont ents(writer);
}
private void RenderNodes(Htm lTextWriter writer,
DemoTreeViewNod eCollection nodes, string prefix, int depth)
{
for (int i = 0; i < nodes.Count; i++)
{
DemoTreeViewNod e node = nodes[i];
string path;
if (prefix.Length == 0)
path = i.ToString();
else
path = string.Format(" {0}|{1}", prefix, i);
writer.AddAttri bute(HtmlTextWr iterAttribute.O nclick,
Page.GetPostBac kEventReference (this, path));
writer.AddStyle Attribute(HtmlT extWriterStyle. Cursor,
"hand");
writer.AddStyle Attribute(HtmlT extWriterStyle. Left,
string.Format(" {0}em", (depth*2)));
writer.AddStyle Attribute(HtmlT extWriterStyle. Position, "relative") ;
writer.RenderBe ginTag(HtmlText WriterTag.Div);
writer.Write(no de.Text);
writer.RenderEn dTag();
RenderNodes(wri ter, node.DemoTreeVi ewNodes, path,
depth+1);
}
}
#endregion
Because I allow a DemoTreeViewNod e to contain other nodes, the control has
to render recursively. The RenderNodes method takes care of that. It goes
through each node in the collection, calculates its path and position, and
then renders it. Each node is rendered as a div element with an OnClick
handler. The handler is what does the postback, passing the path of the
node.
Note that the div ends before the inner nodes are rendered. I found this
necessary so that each node could be clicked. Otherwise, only the outermost
div would receive clicks.
IPostBackEventH andler implementation:
#region IPostBackEventH andler Members
/// <summary>
/// Raise the appropriate event, given the postback data
/// </summary>
/// <param name="eventArgu ment">The string path to the node
posting back</param>
public void RaisePostBackEv ent(string eventArgument)
{
if (eventArgument != null)
{
Page.Trace.Writ e("RaisePostBac kEvent",
eventArgument);
OnClick(new
DemoTreeViewNod eEventArgs(Demo TreeViewNodes[eventArgument]));
}
}
#endregion
RaisePostBackEv ent is called upon a postback. The eventArgument parameter
receives the path of the node. This simply raises the Click event of the
DemoTreeView, passing the referenced node. The DemoTreeViewNod eCollection
indexer does the hard work of locating the node.
DemoTreeViewNod e Class Declaration:
/// <summary>
/// A node in the DemoTreeView
/// </summary>
[ParseChildren(t rue, "DemoTreeViewNo des")]
[ToolboxItem(fal se)]
public class DemoTreeViewNod e
At present, the nodes are very simple, so they don't need to derive from
Control or WebControl.
Note the ParseChildren attribute, which is identical to the attribute on the
DemoTreeView class. This permits nodes to contain other nodes. ToolboxItem
is set to false so that the class doesn't show up in the toolbox.
Public Properties
The property implementation is so simple that I won't show it. The
DemoTreeViewNod es property is identical to the same property implemented in
DemoTreeView. The Text property is a simple string property with no
ViewState support.
DemoTreeViewNod eCollection:
This is a simple, strongly-typed collection of DemoTreeViewNod e instances.
The only fancy part is the implementation of a second indexer. This indexer
accepts a pipe-separated list of integer node offsets and locates the
appropriate node. For instance, "1|2|0" would return
demoTreeView.De moTreeViewNodes[1].DemoTreeViewNo des[2].DemoTreeViewNo des[0].
Test page:
<%@ Register TagPrefix="cc1" Namespace="JWS. WebInfrastructu re.WebControls"
Assembly="JWS.W ebInfrastructur e.WebControls"% >
<%@ Page Language="C#" %>
<script runat="server">
void DemoTreeView1_C lick(object sender,
JWS.WebInfrastr ucture.WebContr ols.DemoTreeVie w.DemoTreeViewN odeEventArgs e)
{
Label1.Text = string.Format(" Node clicked - {0}", e.Node.Text);
}</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<cc1:DemoTreeVi ew ID="DemoTreeVie w1" Runat="server"
OnClick="DemoTr eeView1_Click">
<cc1:DemoTreeVi ewNode Text="treeViewN ode5">
<cc1:DemoTreeVi ewNode Text="treeViewN ode5.1">
</cc1:DemoTreeVie wNode>
</cc1:DemoTreeVie wNode>
<cc1:DemoTreeVi ewNode Text="treeViewN ode6">
<cc1:DemoTreeVi ewNode Text="treeViewN ode6.1">
<cc1:DemoTreeVi ewNode Text="treeViewN ode6.1.0">
</cc1:DemoTreeVie wNode>
</cc1:DemoTreeVie wNode>
</cc1:DemoTreeVie wNode>
</cc1:DemoTreeVie w>
<asp:Label ID="Label1" Runat="server" Text="Label">
</asp:Label>
</div>
</form>
</body>
</html>
So, the way this all works is that each node renders as:
<div onclick="__doPo stBack('TreeVie w1','1|0|0')"
style="cursor:h and;left:4em;po sition:relative ;">
treeViewNode6.1 .0
</div>
When clicked, IPostBackEventH andler.RaisePos tBackEvent will be called with
"1|0|0" as an argument. The indexer will locate node "treeViewNode6. 1.0",
and will raise the Click event, passing that node in the EventArgs. The
Click event handler will fire and display the Text property of the node in
the label.
I hope that helps and isn't too long. Any questions, please ask.
--
John Saunders
johnwsaundersii i at hotmail