asp.net user control : how to manage click on internal object

  • Thread starter Thread starter Remi Hugnon
  • Start date Start date
R

Remi Hugnon

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 ?

Thk's in adance
 
Remi Hugnon said:
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 ?

You should look into the postback mechanism. It allows you to specify the ID
of the control doing the postback as well as an optional parameter. In your
case, the parameter could be the id of the individual node. The control
could capture the postback event, then find the node given the id, and raise
a node-clicked event for the given node.

Yeah, ok, I admit I should give you an example, but I'll have to write it
from scratch, so it will be a little while...
 
Thk's

It's helpfull.

Remi

John Saunders said:
because

You should look into the postback mechanism. It allows you to specify the ID
of the control doing the postback as well as an optional parameter. In your
case, the parameter could be the id of the individual node. The control
could capture the postback event, then find the node given the id, and raise
a node-clicked event for the given node.

Yeah, ok, I admit I should give you an example, but I'll have to write it
from scratch, so it will be a little while...
 
Remi Hugnon said:
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 ?

Remi,

I've begun working on an example for you. Sorry to take so long.

I need to know if your treeview needs to be a UserControl, or if it's ok for
it to be a custom control. Also, I need to know a bit about what you want
the "click" event to be like. For instance, if all you need in the "click"
event is the string id of the clicked node, that's one thing. It would be a
bit more work to do something like the ItemCommand event of the DataGrid,
where the "click" event would receive an EventArgs which would include a
reference to the actual tree node.
 
Remi Hugnon said:
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, DemoTreeViewNode and
DemoTreeViewNodeCollection:

DemoTreeView Class declaration:

/// <summary>
/// A simple Tree View control as an example for Remi Hugnon
([email protected])
/// </summary>
[DefaultProperty("Text"),
ToolboxData("<{0}:DemoTreeView
runat=server></{0}:DemoTreeView>")]
[ParseChildren(true, "DemoTreeViewNodes")]
public class DemoTreeView : WebControl, IPostBackEventHandler

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.
IPostBackEventHandler is implemented in order to provide a Click event when
a node is clicked.

DemoTreeViewNodes property:

#region Public Properties
/// <summary>
/// Gets the top-level nodes in the tree
/// </summary>
[Category("Behavior"),
Description("The top-level nodes in the tree"),

DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
NotifyParentProperty(true),
PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public DemoTreeViewNodeCollection DemoTreeViewNodes
{
get
{
if (_nodes == null)
_nodes = new DemoTreeViewNodeCollection();

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. DesignerSerializationVisibility 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">The HtmlTextWriter to render to</param>
protected override void RenderContents(HtmlTextWriter writer)
{
DemoTreeViewNodeCollection nodes = DemoTreeViewNodes;
RenderNodes(writer, nodes, string.Empty, 0);

base.RenderContents(writer);
}

private void RenderNodes(HtmlTextWriter writer,
DemoTreeViewNodeCollection nodes, string prefix, int depth)
{
for (int i = 0; i < nodes.Count; i++)
{
DemoTreeViewNode node = nodes;
string path;

if (prefix.Length == 0)
path = i.ToString();
else
path = string.Format("{0}|{1}", prefix, i);
writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
Page.GetPostBackEventReference(this, path));
writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor,
"hand");
writer.AddStyleAttribute(HtmlTextWriterStyle.Left,
string.Format("{0}em", (depth*2)));

writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

writer.Write(node.Text);
writer.RenderEndTag();

RenderNodes(writer, node.DemoTreeViewNodes, path,
depth+1);
}
}
#endregion

Because I allow a DemoTreeViewNode 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.

IPostBackEventHandler implementation:

#region IPostBackEventHandler Members
/// <summary>
/// Raise the appropriate event, given the postback data
/// </summary>
/// <param name="eventArgument">The string path to the node
posting back</param>
public void RaisePostBackEvent(string eventArgument)
{
if (eventArgument != null)
{
Page.Trace.Write("RaisePostBackEvent",
eventArgument);
OnClick(new
DemoTreeViewNodeEventArgs(DemoTreeViewNodes[eventArgument]));
}
}
#endregion

RaisePostBackEvent 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 DemoTreeViewNodeCollection
indexer does the hard work of locating the node.

DemoTreeViewNode Class Declaration:

/// <summary>
/// A node in the DemoTreeView
/// </summary>
[ParseChildren(true, "DemoTreeViewNodes")]
[ToolboxItem(false)]
public class DemoTreeViewNode

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
DemoTreeViewNodes property is identical to the same property implemented in
DemoTreeView. The Text property is a simple string property with no
ViewState support.

DemoTreeViewNodeCollection:

This is a simple, strongly-typed collection of DemoTreeViewNode 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.DemoTreeViewNodes[1].DemoTreeViewNodes[2].DemoTreeViewNodes[0].

Test page:

<%@ Register TagPrefix="cc1" Namespace="JWS.WebInfrastructure.WebControls"
Assembly="JWS.WebInfrastructure.WebControls"%>
<%@ Page Language="C#" %>
<script runat="server">
void DemoTreeView1_Click(object sender,
JWS.WebInfrastructure.WebControls.DemoTreeView.DemoTreeViewNodeEventArgs e)
{
Label1.Text = string.Format("Node clicked - {0}", e.Node.Text);
}</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<cc1:DemoTreeView ID="DemoTreeView1" Runat="server"
OnClick="DemoTreeView1_Click">
<cc1:DemoTreeViewNode Text="treeViewNode5">
<cc1:DemoTreeViewNode Text="treeViewNode5.1">
</cc1:DemoTreeViewNode>
</cc1:DemoTreeViewNode>
<cc1:DemoTreeViewNode Text="treeViewNode6">
<cc1:DemoTreeViewNode Text="treeViewNode6.1">
<cc1:DemoTreeViewNode Text="treeViewNode6.1.0">
</cc1:DemoTreeViewNode>
</cc1:DemoTreeViewNode>
</cc1:DemoTreeViewNode>
</cc1:DemoTreeView>
<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="__doPostBack('TreeView1','1|0|0')"
style="cursor:hand;left:4em;position:relative;">
treeViewNode6.1.0
</div>

When clicked, IPostBackEventHandler.RaisePostBackEvent 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.
 
Back
Top