Merge pull request #231 from linquize/a853199c497bb0977970974303fac7e42080809d
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / TreeNode.cs
1 //
2 // System.Web.UI.WebControls.TreeNode.cs
3 //
4 // Authors:
5 //      Lluis Sanchez Gual (lluis@novell.com)
6 //
7 // (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
29 //
30
31 #if NET_2_0
32
33 using System;
34 using System.Collections;
35 using System.Text;
36 using System.ComponentModel;
37 using System.Web.UI;
38
39 namespace System.Web.UI.WebControls
40 {
41         [ParseChildrenAttribute (true, "ChildNodes")]
42         public class TreeNode: IStateManager, ICloneable
43         {
44                 StateBag ViewState = new StateBag ();
45                 TreeNodeCollection nodes;
46                 bool marked;
47                 TreeView tree;
48                 TreeNode parent;
49                 int index;
50                 string path;
51                 int depth = -1;
52                 
53                 object dataItem;
54                 IHierarchyData hierarchyData;
55
56                 bool gotBinding;
57                 TreeNodeBinding binding;
58                 PropertyDescriptorCollection boundProperties;
59                 bool populating;
60                 bool hadChildrenBeforePopulating;
61                 
62                 internal TreeNode (TreeView tree)
63                 {
64                         Tree = tree;
65                 }
66                 
67                 public TreeNode ()
68                 {
69                 }
70                 
71                 public TreeNode (string text)
72                 {
73                         Text = text;
74                 }
75                 
76                 public TreeNode (string text, string value)
77                 {
78                         Text = text;
79                         Value = value;
80                 }
81                 
82                 public TreeNode (string text, string value, string imageUrl)
83                 {
84                         Text = text;
85                         Value = value;
86                         ImageUrl = imageUrl;
87                 }
88                 
89                 public TreeNode (string text, string value, string imageUrl, string navigateUrl, string target)
90                 {
91                         Text = text;
92                         Value = value;
93                         ImageUrl = imageUrl;
94                         NavigateUrl = navigateUrl;
95                         Target = target;
96                 }
97                 
98                 [MonoTODO ("Not implemented")]
99                 protected TreeNode (TreeView owner, bool isRoot)
100                 {
101                         throw new NotImplementedException ();
102                 }
103
104                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
105                 [Browsable (false)]
106                 public int Depth {
107                         get {
108                                 if (depth != -1) return depth;
109                                 depth = 0;
110                                 TreeNode nod = parent;
111                                 while (nod != null) {
112                                         depth++;
113                                         nod = nod.parent;
114                                 }
115                                 return depth;
116                         }
117                 }
118                 
119                 void ResetPathData ()
120                 {
121                         path = null;
122                         depth = -1;
123                         gotBinding = false;
124                         if (nodes != null) {
125                                 foreach (TreeNode node in nodes)
126                                         node.ResetPathData ();
127                         }
128                 }
129                 
130                 internal TreeView Tree {
131                         get { return tree; }
132                         set {
133                                 if (SelectedFlag) {
134                                         if (value != null)
135                                                 value.SetSelectedNode (this, false);
136                                 }
137                                 tree = value;
138                                 if (nodes != null)
139                                         nodes.SetTree (tree);
140                                 ResetPathData ();
141                                 if (PopulateOnDemand && !Populated && Expanded.HasValue && Expanded.Value)
142                                         Populate ();
143                         }
144                 }
145                 
146                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
147                 [DefaultValue (false)]
148                 [Browsable (false)]
149                 public bool DataBound {
150                         get { return ViewState ["DataBound"] == null ? false : (bool) ViewState ["DataBound"]; }
151                         private set { ViewState ["DataBound"] = value; }
152                 }
153                 
154                 [DefaultValue (null)]
155                 [Browsable (false)]
156                 public object DataItem {
157                         get { return dataItem; }
158                 }
159                 
160                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
161                 [DefaultValue ("")]
162                 [Browsable (false)]
163                 public string DataPath {
164                         get { return ViewState ["DataPath"] == null ? String.Empty : (String) ViewState ["DataPath"]; }
165                         private set { ViewState ["DataPath"] = value; }
166                 }
167                 
168                 [DefaultValue (false)]
169                 public bool Checked {
170                         get {
171                                 object o = ViewState ["Checked"];
172                                 if (o != null) return (bool)o;
173                                 return false;
174                         }
175                         set {
176                                 ViewState ["Checked"] = value;
177                                 if (tree != null)
178                                         tree.NotifyCheckChanged (this);
179                         }
180                 }
181
182                 [DefaultValue (null)]
183                 [MergableProperty (false)]
184                 [Browsable (false)]
185                 [PersistenceMode (PersistenceMode.InnerDefaultProperty)]
186                 public TreeNodeCollection ChildNodes {
187                         get {
188                                 if (nodes == null) {
189                                         nodes = new TreeNodeCollection (this);
190                                                 
191                                         if (IsTrackingViewState)
192                                                 ((IStateManager)nodes).TrackViewState();
193                                 }
194                                 return nodes;
195                         }
196                 }
197                 
198                 [DefaultValue (null)]
199                 public bool? Expanded {
200                         get {
201                                 object o = ViewState ["Expanded"];
202                                 return (bool?)o;
203                         }
204                         set {
205                                 bool? current = (bool?) ViewState ["Expanded"];
206                                 if (current == value)
207                                         return;
208                                 ViewState ["Expanded"] = value;
209                                 if (tree != null)
210                                         tree.NotifyExpandedChanged (this);
211                                 if (PopulateOnDemand && !Populated && value.HasValue && value.Value)
212                                         Populate ();
213                         }
214                 }
215
216                 [Localizable (true)]
217                 [DefaultValue ("")]
218                 public string ImageToolTip {
219                         get {
220                                 object o = ViewState ["ImageToolTip"];
221                                 if (o != null)
222                                         return (string)o;
223                                 return String.Empty;
224                         }
225                         set { ViewState ["ImageToolTip"] = value; }
226                 }
227                 
228                 [DefaultValue ("")]
229                 [UrlProperty]
230                 [Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
231                 public string ImageUrl {
232                         get {
233                                 object o = ViewState ["ImageUrl"];
234                                 if (o != null)
235                                         return (string)o;
236                                 return String.Empty;
237                         }
238                         set { ViewState ["ImageUrl"] = value; }
239                 }
240
241                 [DefaultValue ("")]
242                 [UrlProperty]
243                 [Editor ("System.Web.UI.Design.UrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
244                 public string NavigateUrl {
245                         get {
246                                 object o = ViewState ["NavigateUrl"];
247                                 if (o != null)
248                                         return (string)o;
249                                 return String.Empty;
250                         }
251                         set { ViewState ["NavigateUrl"] = value; }
252                 }
253
254                 internal bool HadChildrenBeforePopulating {
255                         get { return hadChildrenBeforePopulating; }
256                         set {
257                                 if (populating)
258                                         return;
259
260                                 hadChildrenBeforePopulating = value;
261                         }
262                 }
263                 
264                 [DefaultValue (false)]
265                 public bool PopulateOnDemand {
266                         get {
267                                 object o = ViewState ["PopulateOnDemand"];
268                                 if (o != null)
269                                         return (bool)o;
270                                 return false;
271                         }
272                         set {
273                                 ViewState ["PopulateOnDemand"] = value;
274                                 if (value && nodes != null && nodes.Count > 0)
275                                         HadChildrenBeforePopulating = true;
276                                 else
277                                         HadChildrenBeforePopulating = false;
278                         }
279                 }
280
281                 [DefaultValue (TreeNodeSelectAction.Select)]
282                 public TreeNodeSelectAction SelectAction {
283                         get {
284                                 object o = ViewState ["SelectAction"];
285                                 if (o != null)
286                                         return (TreeNodeSelectAction)o;
287                                 return TreeNodeSelectAction.Select;
288                         }
289                         set { ViewState ["SelectAction"] = value; }
290                 }
291
292                 [DefaultValue (null)]
293                 public bool? ShowCheckBox {
294                         get {
295                                 object o = ViewState ["ShowCheckBox"];
296                                 return (bool?)o;
297                         }
298                         set { ViewState ["ShowCheckBox"] = value; }
299                 }
300
301                 internal bool ShowCheckBoxInternal {
302                         get {
303                                 if (ShowCheckBox.HasValue)
304                                         return ShowCheckBox.Value;
305                                 else
306                                         return (Tree.ShowCheckBoxes == TreeNodeTypes.All) ||
307                                                  ((Tree.ShowCheckBoxes & TreeNodeTypes.Leaf) > 0 && IsLeafNode) ||
308                                                  ((Tree.ShowCheckBoxes & TreeNodeTypes.Parent) > 0 && IsParentNode && Parent != null) ||
309                                                  ((Tree.ShowCheckBoxes & TreeNodeTypes.Root) > 0 && Parent == null && ChildNodes.Count > 0);
310                         }
311                 }
312                 
313                 [DefaultValue ("")]
314                 public string Target {
315                         get {
316                                 object o = ViewState ["Target"];
317                                 if(o != null)
318                                         return (string)o;
319                                 return String.Empty;
320                         }
321                         set { ViewState ["Target"] = value; }
322                 }
323
324                 [Localizable (true)]
325                 [DefaultValue ("")]
326                 [WebSysDescription ("The display text of the tree node.")]
327                 public string Text {
328                         get {
329                                 object o = ViewState ["Text"];
330                                 if (o == null)
331                                         o = ViewState ["Value"];
332                                 if (o != null)
333                                         return (string)o;
334                                 return String.Empty;
335                         }
336                         set { ViewState ["Text"] = value; }
337                 }
338
339                 [Localizable (true)]
340                 [DefaultValue ("")]
341                 public string ToolTip {
342                         get {
343                                 object o = ViewState ["ToolTip"];
344                                 if(o != null)
345                                         return (string)o;
346                                 return String.Empty;
347                         }
348                         set { ViewState ["ToolTip"] = value; }
349                 }
350
351                 [Localizable (true)]
352                 [DefaultValue ("")]
353                 public string Value {
354                         get {
355                                 object o = ViewState ["Value"];
356                                 if (o == null)
357                                         o = ViewState ["Text"];
358                                 if(o != null)
359                                         return (string)o;
360                                 return String.Empty;
361                         }
362                         set { ViewState ["Value"] = value; }
363                 }
364                 
365                 [DefaultValue (false)]
366                 public bool Selected {
367                         get { return SelectedFlag; }
368                         set {
369                                 SelectedFlag = value;
370                                 
371                                 if (tree != null) {
372                                         if (!value && tree.SelectedNode == this)
373                                                 tree.SetSelectedNode (null, false);
374                                         else if (value)
375                                                 tree.SetSelectedNode (this, false);
376                                 }
377                         }
378                 }
379                 
380                 internal virtual bool SelectedFlag {
381                         get {
382                                 object o = ViewState ["Selected"];
383                                 if(o != null)
384                                         return (bool)o;
385                                 return false;
386                         }
387                         set { ViewState ["Selected"] = value; }
388                 }
389                 
390                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
391                 [Browsable (false)]
392                 public TreeNode Parent {
393                         get { return parent; }
394                 }
395                 
396                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
397                 [Browsable (false)]
398                 public string ValuePath {
399                         get {
400                                 if (tree == null) return Value;
401                                 
402                                 StringBuilder sb = new StringBuilder (Value);
403                                 TreeNode node = parent;
404                                 while (node != null) {
405                                         sb.Insert (0, tree.PathSeparator);
406                                         sb.Insert (0, node.Value);
407                                         node = node.Parent;
408                                 }
409                                 return sb.ToString ();
410                         }
411                 }
412                 
413                 internal int Index {
414                         get { return index; }
415                         set { index = value; ResetPathData (); }
416                 }
417                 
418                 internal void SetParent (TreeNode node)
419                 {
420                         parent = node;
421                         ResetPathData ();
422                 }
423                 
424                 internal string Path {
425                         get {
426                                 if (path != null)
427                                         return path;
428                                 StringBuilder sb = new StringBuilder (index.ToString());
429                                 TreeNode node = parent;
430                                 while (node != null) {
431                                         sb.Insert (0, '_');
432                                         sb.Insert (0, node.Index.ToString ());
433                                         node = node.Parent;
434                                 }
435                                 path = sb.ToString ();
436                                 return path;
437                         }
438                 }
439                 
440                 internal bool Populated {
441                         get {
442                                 object o = ViewState ["Populated"];
443                                 if (o != null)
444                                         return (bool) o;
445                                 return false;
446                         }
447                         set { ViewState ["Populated"] = value; }
448                 }
449
450                 internal bool HasChildData {
451                         get { return nodes != null; }
452                 }
453                 
454                 internal void Populate ()
455                 {
456                         if (tree == null)
457                                 return;
458
459                         populating = true;
460                         tree.NotifyPopulateRequired (this);
461                         populating = false;
462                         Populated = true;
463                 }
464                 
465                 public void Collapse ()
466                 {
467                         Expanded = false;
468                 }
469
470                 public void CollapseAll ()
471                 {
472                         SetExpandedRec (false, -1);
473                 }
474
475                 public void Expand ()
476                 {
477                         Expanded = true;
478                 }
479
480                 internal void Expand (int depth)
481                 {
482                         SetExpandedRec (true, depth);
483                 }
484
485                 public void ExpandAll ()
486                 {
487                         SetExpandedRec (true, -1);
488                 }
489                 
490                 void SetExpandedRec (bool expanded, int depth)
491                 {
492                         Expanded = expanded;
493                         if (depth == 0)
494                                 return;
495                         
496                         foreach (TreeNode nod in ChildNodes)
497                                 nod.SetExpandedRec (expanded, depth - 1);
498                 }
499                 
500                 public void Select ()
501                 {
502                         Selected = true;
503                 }
504                 
505                 public void ToggleExpandState ()
506                 {
507 #if TARGET_JVM //No support for Nullable<bool>.GetValueOrDefault() yet
508                         bool? value = Expanded;
509                         Expanded = value.HasValue ? !value.Value : true;
510 #else
511                         Expanded = !Expanded.GetValueOrDefault(false);
512 #endif
513                 }
514
515                 void IStateManager.LoadViewState (object savedState)
516                 {
517                         LoadViewState (savedState);
518                 }
519
520                 protected virtual void LoadViewState (object savedState)
521                 {
522                         if (savedState == null)
523                                 return;
524
525                         object[] states = (object[]) savedState;
526                         ViewState.LoadViewState (states [0]);
527                         
528                         if (tree != null && SelectedFlag)
529                                 tree.SetSelectedNode (this, true);
530                         
531                         if (!PopulateOnDemand || Populated)
532                                 ((IStateManager)ChildNodes).LoadViewState (states [1]);
533                 }
534                 
535                 object IStateManager.SaveViewState ()
536                 {
537                         return SaveViewState ();
538                 }
539
540                 protected virtual object SaveViewState ()
541                 {
542                         object[] states = new object[2];
543                         states[0] = ViewState.SaveViewState();
544                         states[1] = (nodes == null ? null : ((IStateManager)nodes).SaveViewState());
545                         
546                         for (int i = 0; i < states.Length; i++) {
547                                 if (states [i] != null)
548                                         return states;
549                         }
550                         return null;
551                 }
552
553                 void IStateManager.TrackViewState ()
554                 {
555                         TrackViewState ();
556                 }
557
558                 protected void TrackViewState ()
559                 {
560                         if (marked) return;
561                         marked = true;
562                         ViewState.TrackViewState();
563
564                         if (nodes != null)
565                                 ((IStateManager)nodes).TrackViewState ();
566                 }
567                 
568                 bool IStateManager.IsTrackingViewState {
569                         get { return IsTrackingViewState; }
570                 }
571
572                 protected bool IsTrackingViewState {
573                         get { return marked; }
574                 }
575                 
576                 internal void SetDirty ()
577                 {
578                         ViewState.SetDirty (true);
579                         if (nodes != null)
580                                 nodes.SetDirty ();
581                 }
582                 
583                 public virtual object Clone ()
584                 {
585                         TreeNode nod = tree != null ? tree.CreateNode () : new TreeNode ();
586                         foreach (DictionaryEntry e in ViewState)
587                                 nod.ViewState [(string)e.Key] = ((StateItem)e.Value).Value;
588                                 
589                         foreach (TreeNode c in ChildNodes)
590                                 nod.ChildNodes.Add ((TreeNode)c.Clone ());
591                                 
592                         return nod;
593                 }
594
595                 object ICloneable.Clone ()
596                 {
597                         return Clone ();
598                 }
599                 
600                 internal void Bind (IHierarchyData hierarchyData)
601                 {
602                         this.hierarchyData = hierarchyData;
603                         DataBound = true;
604                         DataPath = hierarchyData.Path;
605                         dataItem = hierarchyData.Item;
606                         
607                         TreeNodeBinding bin = GetBinding ();
608                         if (bin != null) {
609                         
610                                 // Bind ImageToolTip property
611
612                                 if (bin.ImageToolTipField.Length > 0) {
613                                         ImageToolTip = Convert.ToString (GetBoundPropertyValue (bin.ImageToolTipField));
614                                         if (ImageToolTip.Length == 0)
615                                                 ImageToolTip = bin.ImageToolTip;
616                                 } else if (bin.ImageToolTip.Length > 0)
617                                         ImageToolTip = bin.ImageToolTip;
618                                         
619                                 // Bind ImageUrl property
620
621                                 if (bin.ImageUrlField.Length > 0) {
622                                         ImageUrl = Convert.ToString (GetBoundPropertyValue (bin.ImageUrlField));
623                                         if (ImageUrl.Length == 0)
624                                                 ImageUrl = bin.ImageUrl;
625                                 } else if (bin.ImageUrl.Length > 0)
626                                         ImageUrl = bin.ImageUrl;
627                                         
628                                 // Bind NavigateUrl property
629
630                                 if (bin.NavigateUrlField.Length > 0) {
631                                         NavigateUrl = Convert.ToString (GetBoundPropertyValue (bin.NavigateUrlField));
632                                         if (NavigateUrl.Length == 0)
633                                                 NavigateUrl = bin.NavigateUrl;
634                                 } else if (bin.NavigateUrl.Length > 0)
635                                         NavigateUrl = bin.NavigateUrl;
636                                         
637                                 // Bind PopulateOnDemand property
638                                 
639                                 if (bin.HasPropertyValue ("PopulateOnDemand"))
640                                         PopulateOnDemand = bin.PopulateOnDemand;
641                                 
642                                 // Bind SelectAction property
643                                         
644                                 if (bin.HasPropertyValue ("SelectAction"))
645                                         SelectAction = bin.SelectAction;
646                                 
647                                 // Bind ShowCheckBox property
648                                         
649                                 if (bin.HasPropertyValue ("ShowCheckBox"))
650                                         ShowCheckBox = bin.ShowCheckBox;
651                                         
652                                 // Bind Target property
653
654                                 if (bin.TargetField.Length > 0) {
655                                         Target = Convert.ToString (GetBoundPropertyValue (bin.TargetField));
656                                         if (Target.Length == 0)
657                                                 Target = bin.Target;
658                                 } else if (bin.Target.Length > 0)
659                                         Target = bin.Target;
660                                         
661                                 // Bind Text property
662                                 string text = null;
663                                 if (bin.TextField.Length > 0) {
664                                         text = Convert.ToString (GetBoundPropertyValue (bin.TextField));
665                                         if (bin.FormatString.Length > 0)
666                                                 text = string.Format (bin.FormatString, text);
667                                 }
668                                 if (String.IsNullOrEmpty (text)) {
669                                         if (bin.Text.Length > 0)
670                                                 text = bin.Text;
671                                         else if (bin.Value.Length > 0)
672                                                 text = bin.Value;
673                                 }
674                                 if (!String.IsNullOrEmpty (text))
675                                         Text = text;
676                                         
677                                 // Bind ToolTip property
678
679                                 if (bin.ToolTipField.Length > 0) {
680                                         ToolTip = Convert.ToString (GetBoundPropertyValue (bin.ToolTipField));
681                                         if (ToolTip.Length == 0)
682                                                 ToolTip = bin.ToolTip;
683                                 } else if (bin.ToolTip.Length > 0)
684                                         ToolTip = bin.ToolTip;
685                                         
686                                 // Bind Value property
687                                 string value = null;
688                                 if (bin.ValueField.Length > 0) {
689                                         value = Convert.ToString (GetBoundPropertyValue (bin.ValueField));
690                                 }
691                                 if (String.IsNullOrEmpty (value)) {
692                                         if (bin.Value.Length > 0)
693                                                 value = bin.Value;
694                                         else if (bin.Text.Length > 0)
695                                                 value = bin.Text;
696                                 }
697                                 if (!String.IsNullOrEmpty (value))
698                                         Value = value;
699                         } else {
700                                 Text = Value = GetDefaultBoundText ();
701                         }
702
703                         INavigateUIData navigateUIData = hierarchyData as INavigateUIData;
704                         if (navigateUIData != null) {
705                                 SelectAction = TreeNodeSelectAction.None;
706                                 Text = navigateUIData.ToString ();
707                                 NavigateUrl = navigateUIData.NavigateUrl;
708                                 ToolTip = navigateUIData.Description;
709                         }
710                 }
711                 
712                 internal void SetDataItem (object item)
713                 {
714                         dataItem = item;
715                 }
716                 
717                 internal void SetDataPath (string path)
718                 {
719                         DataPath = path;
720                 }
721                 
722                 internal void SetDataBound (bool bound)
723                 {
724                         DataBound = bound;
725                 }
726                 
727                 string GetDefaultBoundText ()
728                 {
729                         if (hierarchyData != null)
730                                 return hierarchyData.ToString ();
731                         else if (dataItem != null)
732                                 return dataItem.ToString ();
733                         else
734                                 return string.Empty;
735                 }
736                 
737                 string GetDataItemType ()
738                 {
739                         if (hierarchyData != null)
740                                 return hierarchyData.Type;
741                         else if (dataItem != null)
742                                 return dataItem.GetType().ToString ();
743                         else
744                                 return string.Empty;
745                 }
746                                 
747                 internal bool IsParentNode {
748                         get { return ChildNodes.Count > 0 || (PopulateOnDemand && !Populated); }
749                 }
750                 
751                 internal bool IsLeafNode {
752                         get { return !IsParentNode; }
753                 }
754                 
755                 internal bool IsRootNode {
756                         get { return Depth == 0; }
757                 }
758                 
759                 TreeNodeBinding GetBinding ()
760                 {
761                         if (tree == null)
762                                 return null;
763                         if (gotBinding)
764                                 return binding;
765                         binding = tree.FindBindingForNode (GetDataItemType (), Depth);
766                         gotBinding = true;
767                         return binding;
768                 }
769                 
770                 object GetBoundPropertyValue (string name)
771                 {
772                         if (boundProperties == null) {
773                                 if (hierarchyData != null)
774                                         boundProperties = TypeDescriptor.GetProperties (hierarchyData);
775                                 else
776                                         boundProperties = TypeDescriptor.GetProperties (dataItem);
777                         }
778                         
779                         PropertyDescriptor prop = boundProperties.Find (name, true);
780                         if (prop == null)
781                                 throw new InvalidOperationException ("Property '" + name + "' not found in data bound item");
782                                 
783                         if (hierarchyData != null)
784                                 return prop.GetValue (hierarchyData);
785                         else
786                                 return prop.GetValue (dataItem);
787                 }
788
789                 internal void BeginRenderText (HtmlTextWriter writer)
790                 {
791                         RenderPreText (writer);
792                 }
793                 
794                 internal void EndRenderText (HtmlTextWriter writer)
795                 {
796                         RenderPostText (writer);
797                 }
798                 
799                 protected virtual void RenderPreText (HtmlTextWriter writer)
800                 {
801                 }
802                 
803                 protected virtual void RenderPostText (HtmlTextWriter writer)
804                 {
805                 }
806         }
807 }
808 #endif