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