locked
How to Create HTML in a Server Control RRS feed

  • Question

  • User1062417797 posted

    Hi,

    I'm new to this subject. I would like to create a Server Control for my website. Until now I've created Web User Controls. I think a better approach is using Server Controls (although tougher to develop). What I would like to know is, what is the way to go about this. Should I use the DOM? I know the straightforward way to just use the 'output.write( HtmlTextWriterTag.Table)' etc... to create HTML but that doesn't seem the right way to me. DOM is more flexibel and less error prone. If the DOM is the right way to go, could someone please guide me to a tutorial on how to f.i. create the Document. I've already tried several approaches but a stuck on that part. In the Code below you can see what I've tried so far. Appreciate any help.

    Cheers,
    Paul.

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Text;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Windows.Forms;
    
    namespace MyControls
    {
        [ToolboxData("&lt;{0}:CCContainer runat=server></{0}:CCContainer>")]
        //[ToolboxBitmap(@"C:\AbsolutePath\CCContainer.bmp")]
        public class CCContainer : WebControl
        {
            private HtmlElement CreateElement(HtmlDocument pobjDoc, string pstrTag, string pstrID)
            {
                HtmlElement elem = pobjDoc.CreateElement(pstrTag);
    
                if (pstrID.Length > 0)
                {
                    elem.Id = pstrID;
                }
    
                return (elem);
            }
    
            private HtmlDocument CreateHTML()
            {
                HtmlDocument doc = ???? // How do I get this ????
                HtmlElement elem = null;
    
                elem = doc.Body.AppendChild(CreateElement(doc, "Table", "Table1"));
                elem = elem.AppendChild(CreateElement(doc, "tr", ""));
                elem = elem.AppendChild(CreateElement(doc, "td", ""));
                elem.InnerHtml = "This is a test test";
    
                return doc;
            }
    
            protected override void RenderContents(HtmlTextWriter output)
            {
                output.Write(CreateHTML().ToString());
            }
        }
    }
    
     
    Thursday, June 19, 2008 8:45 AM

Answers

All replies

  • User543031324 posted

    There is no DOM in server side code. All the HTML output should be done via the HtmlTextWriter. In RenderContents you would write something like this:

     

    output.RenderBeginTag(HtmlTextWriterTag.Table);
    output.RenderBeginTag(HtmlTextWriterTag.Tr);
    output.RenderBeginTag(HtmlTextWriterTag.Td);
    output.Write("Contents of cell");
    output.RenderEndTag();
    output.RenderEndTag();
    output.RenderEndTag();
      

     

    This is the way that all ASP.NET renders its server controls. It would be a waste to have something like you do above becuase to have to render the actual HTML anyway. Here is an article on creating custom controls http://www.code-magazine.com/Article.aspx?quickid=0309091. Hope that helps
    Thursday, June 19, 2008 9:01 AM
  • User853727733 posted

    In your case I would benefit from using the HtmlControls (HtmlTable, HtmlTableRow, HtmlTableCell) creating the tags you want and then adding them to your page. This link will show an example:

    http://www.developerfusion.co.uk/show/4410/4/

    This post creates a login control but the way it's code is similiar to what you want, take a look:

    http://blogs.microsoft.co.il/blogs/giladlavian/archive/2008/02/11/compositecontrol-or-webcontrol.aspx

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, June 19, 2008 9:13 AM
  • User1062417797 posted

    Hi awilinsk,

     Thanks for the quick reply. Aha. so all output is created sequentially and RenderEndTag automatically knows how to end a Tag. Ok, I'll give this a try. Thanks for the Link.

    Cheers,
    Paul.

    Thursday, June 19, 2008 9:15 AM
  • User543031324 posted

    You got it. The "RenderTag" methods keep a stack of what tags are open and when RenderEndTag is called it pops one of the begin tags off the stack.

    Thursday, June 19, 2008 9:24 AM
  • User1062417797 posted

    Hi gbogea,

    Wow, you guys are fast. Thanks for standing by.

    Looks even more promising.

    Cheers,
    Paul.

    Thursday, June 19, 2008 9:24 AM
  • User1187105292 posted

     

    I'll pull out some sample code for you to look at from a few server controls I wrote at home.  May take a day or two, depends on spare time and whether I've got electrical power and/or phones and/or internet connectivity at home.

    Learning how to write server controls is "a really good thing to do".

    Do not expect to figure it out on your own, either via intuition or logic.  You won't.

    Plan on buying every book with a sizeable section on the topic, plus reading dozens of semi-conflicting articles on the web, and then cursing MS for not providing decent documentation.  But when all the cursing is done and you've finally figured it out, it's really sweet.

     

    Thursday, June 19, 2008 10:40 AM
  • User1062417797 posted

    Hi David,

     Thanks in advance. I'm curious about your examples.

    Cheers,
    Paul.

    Friday, June 20, 2008 12:40 AM
  • User1187105292 posted

    I don't pretend to be an expert in custom server controls.  I've built a few really neat controls, but it's entirely possible that there are much better ways to do it than I did.  When I found a way that worked correctly, I quit looking. [<:o)]

    This one is a simple one that overrides an existing control, Compare Validator..  Key differences in behavior are:

    1. precede the error message with an error icon.
    2. alter a default value on initialization.
    3. provide a method to set many properties based upon the associated data entry control. 
    4. attempt to make it smart enough to find other controls on the page instead of just in the same container (didn't get that working [:(]).

    using System;
    using
    System.Collections.Generic;
    using
    System.Text;
    using
    System.Web;
    using
    System.Web.UI;
    using
    System.Web.UI.WebControls;
    using
    System.Web.UI.HtmlControls;
    using System.ComponentModel;

    namespace SoftwareRD.WebUI
    {

      /// <summary>
      ///
    This is a CompareValidator that displays an error icon in addition to any textual error message.
      ///
    </summary>
      [ToolboxData("<Srd:CompareValidatorIconed runat='server' Display='Dynamic'></Srd:CompareValidatorIconed>"
    )]
      public class CompareValidatorIconed :
    CompareValidator
      {

        /// <summary>
        /// Instantiate this validator and force its Display to Dynamic mode.
        /// </summary>
        public CompareValidatorIconed() : base()
        {
           // Why the default choice is Static I will never understand!

           this.Display = ValidatorDisplay.Dynamic;
        }

        /// <summary>
        /// Adds an icon and the error message literal text to the list of child controls in this validator,
        /// then raises the base class OnPreRender event.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
          Image severityIcon = new Image();

          severityIcon.ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(SrdWebUi.CustomValidatorIconed), "SrdWebUi.Images.error.gif");
          severityIcon.Visible =
    true;
          severityIcon.ID =
    this.ClientID + "IconedImage";
          severityIcon.Style.Add(
    HtmlTextWriterStyle.Display, "inline");
         
    severityIcon.Style.Add(
    HtmlTextWriterStyle.Visibility, "visible");
          this.Controls.Add(severityIcon);

          Literal msg = new Literal();
         
    msg.Text =
    this.ErrorMessage;
          this.Controls.Add(msg);

          // Now do what is normally done.
          base.OnPreRender(e);
        }

        /// <summary>
        /// Given a validator that is, or derives from, CompareValidator, and an IDataBox control that
        /// contains it, set default values for the validator based upon IDataBox properties.
        /// </summary>
        /// <param name="val"></param>
        /// <param name="iDataBox"></param>
        /// <returns></returns>
        public static CompareValidator SetDefaultProperties(CompareValidator val, IDataBox iDataBox)
        {
          val.ControlToValidate = iDataBox.DataEntryID;
          val.Type = DataTypeHelper.DataTypeHelper.ConvertToValidationDataType(iDataBox.DataType);
          val.Display =
    ValidatorDisplay.Dynamic;
          val.EnableClientScript = iDataBox.EnableClientScript;
          val.Enabled = iDataBox.Enabled;
          val.EnableTheming = iDataBox.EnableTheming;
          val.EnableViewState = iDataBox.EnableViewState;
          val.Visible = iDataBox.Visible;
          return val;
        }

        /// <summary>
        /// Find the control on the page, starting with an ASP.Net standard FindControl and, if not found,
        /// search the page for the first control with that ID.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public override Control FindControl(string id)
        {
          Control c = base.FindControl(id);

          if (c == null)
          {
            c = PageHelper.FindControl(
    this.Page, id);
          }
          return c;
        }

        /// <summary>
        /// @TODO
        /// </summary>
        /// <returns></returns>
        protected override bool ControlPropertiesValid()
        {
          // @TODO: determine whether the commented out code is needed, correct or remove as appropriate.
          //bool propertiesAreValid = true;
          //try
          //{
          //  propertiesAreValid = base.ControlPropertiesValid();
          //}
          //catch (Exception ex)
          //{
          //}
          // Make sure ControlToValidate is set
          if (this.ControlToValidate.Length == 0)
          {
            //@TODO: put in resource file
           
    throw new HttpException(string.Format("The ControlToValidate property of '{0}' cannot be blank.", this.ID));
          }

          if (   this.ControlToCompare.Length == 0
              &&
    this.Operator != ValidationCompareOperator.DataTypeCheck
             )
          {
            //@TODO: put in resource file
            throw new HttpException(string.Format("The ControlToCompare property of '{0}' cannot be blank.", this.ID));
          }
         
    return true; // if we reach here, everything checks out
        }

      }
    }

    Friday, June 20, 2008 2:16 AM