I thought I would compose a short how to on programmatic web controls.  Often I have looked at the core set of controls within ASP.NET and wonder what design is required in order to get the design time mark up similar to that of the various controls.  The types of markup I am referring to includes:

  • Containers
  • Lists
  • Templates
  • Nested Controls of a certain type
  • Data bound controls
    • Only one example here, as I have wanted to do for ages and could not quite put my finger on how it was achieved.  Turns out, really simple lol

The following imports are used in these examples:

using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

Simple Container Control

The first custom control structure I want to make is a custom panel.  Not inherited from a panel, although the same would be achieved but rather the simplest custom container. 

Desired Mark up

        <aebs:CustomPanel ID="Panel1" runat="server">
            Hell World
        </aebs:CustomPanel>
This is as simple as it gets.  This is a web control which persists any children controls or elements which you put in side.

    [ParseChildren(false)]
    [PersistChildren(true)]
    public class CustomPanel : WebControl
    {
        public CustomPanel()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }

A Control with an object list

This control is a web control but it has a list of an object, so you can add numerous types of the object to its collection.  The collection could be of a web control and if so it would be wise to inherit from a composite control as opposed to a web control.

Desired mark-up

        <aebs:ListControl1 ID="ListControl1" runat="server">
            <CustomObjects>
                <aebs:CustomObject CustomProperty="Hello World" />
                <aebs:CustomObject CustomProperty="Hello World 2" />
            </CustomObjects>
        </aebs:ListControl1>

So in design time you will see the intellisense prompt you for a tag called CustomObjects followed by nested prompts of a tag called Custom Object. 

image

    [ParseChildren(true)]
    [PersistChildren(false)]
    public class ListControl1 : WebControl
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<CustomObject> CustomObjects { get; set; }

        public ListControl1()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }

The Custom object is nothing more than a class with a property. see here

    public class CustomObject
    {
        public string CustomProperty { get; set; }

        public CustomObject()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }

A Template Control

The mark up achieved with this type of control is like the type you see with for example the form view control which allows for:

  • Item Template
  • Insert Item Template
  • Edit Item Template

All I am doing is showing the mark-up required for the design time mark-up, so when using you will need to use the ITemplate InstantiateIn method providing a web control or html control as the container.

Desired mark-up

        <aebs:TemplateControl ID="TemplateControl1" runat="server">
            <HeaderTemplate>
                Hello Header World
            </HeaderTemplate>
            <ContentTemplate>
                Hello Content World
            </ContentTemplate>
            <FooterTemplate>
                Hello Footer World
            </FooterTemplate>
        </aebs:TemplateControl>

image

The code to achieve this is as follows.

    [ParseChildren(true)]
    [PersistChildren(false)]
    public class TemplateControl : WebControl
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate HeaderTemplate { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate ContentTemplate { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate FooterTemplate { get; set; }

        public TemplateControl()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }

You are going to want to control how each template is rendered but for the purposes of this example I am only showing the bare bones of how to achieve the mark-up.

A container control with specific object types as options

A good example of this type of control is when you use the object or sql data source control.  It allows you to specify parameters for the select, insert, update etc…  The options though which are provided to you are restricted so you can only choose parameter objects.  The key here is to specify a list property of the control with the type being a class other inherit from, not necessarily abstract.

Desired mark-up

        <aebs:ConstrainedCollection ID="Constrained1" runat="server">
            <AbstractProperties>
                <aebs:ConcreteOne />
                <aebs:ConcreteTwo />
            </AbstractProperties>
        </aebs:ConstrainedCollection>

image

The code to achieve this mark-up is as follows:

    [ParseChildren(true)]
    [PersistChildren(false)]
    public class ConstrainedCollection : WebControl
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<AbstractClass1> AbstractProperties { get; set; }

        public ConstrainedCollection()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }

So you can see that the only difference between this and the list control example above is that I use a type for the list which is inherited by two other types being ConcreteOne and also ConcreteTwo.

A DataPanel

I have wanted to do this for a while but could not quite put my finger on how it was achieved.  Like I said up top, this turns out to be really simple.  The same could be achieved with an ObjectDataSource and a FormView but i want a light weight container which I could throw an object at and use Eval inside it.  I have many many uses for such a light weight singular object display.  Plus I also wondered how the use of Eval was achieved in Custom Controls, turns out that it is through the use the System.Web.UI.IDataItemContainer Interface.

Desired Mark-up

        <aebs:DataPanel ID="datapanel1" runat="server">
            <%# Eval("ProjectName") %>
        </aebs:DataPanel>

So I am not doing anything more than requesting a property of the object which I throw at the control.  Throwing the object at the control I do inside the Page_Load event just for demo.

    public void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            DataPanel.Project p = new DataPanel.Project();
            p.ProjectName = "Project 101";

            datapanel1.BoundObject = p;
            datapanel1.DataBind();
        }
    }

image

So the code to achieve this is just the simple container control above but this time I implement the interface.  For the purposes of demonstration I have also banged the class inside this type as nested too.

    [ParseChildren(false)]
    [PersistChildren(true)]
    public class DataPanel : WebControl, IDataItemContainer
    {
        public class Project
        {
            public string ProjectName { get; set; }
        }

        private object boundObject;

        public DataPanel()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public object BoundObject
        {
            set
            {
                boundObject = value;
            }
        }

        #region IDataItemContainer Members

        public object DataItem
        {
            get { return boundObject; }
        }

        public int DataItemIndex
        {
            get { return 0; }
        }

        public int DisplayIndex
        {
            get { return 0; }
        }

        #endregion
    }

Well I hope this is of some use to others,  I will try and update this post soon with examples of custom DataSource controls and also custom DataBound Controls.  When you start get into List Controls from a data source it gets real fun.

Cheers

Andrew



.NET | Architecture | ASP.NET | C#
Tuesday, March 17, 2009 5:47:28 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer

This morning I followed an Adobe Flex tutorial, well 3, about consuming Twitters restful API inside Flex.  After this it got me thinking about WCF and their Rest Starter Kit.  I looked and they are now up to Preview 2, so I downloaded it and in my spare time focused on this recently release white paper:

A Guide to Designing and Building RESTful Web Services with WCF 3.5

Installing the starter kit, which also installs some project templates, gives you a good starting point.  It also introduced me to some new concepts.  The starter kit actually generates a help page for your service which is an xml feed and XSLT Style Sheet e.g.

image

So It provides all the relevant information required i.e. the method, the uri template, the request and response schema.  I tested out a quick code up for the simplest form for consumption by C# and it worked out pretty well.  I focused on the fact that the info can be returned currently in two formats being XML and also JSON.  I wanted an abstract class to hold the URI’s for each method and also abstract methods which each derived object needs to override.

The idea is ultimately to have strongly typed helper methods for consuming restful apis.  So the tool, similar to the WSDL or SVCUTIL could be supplied with the following information:

  • namespace
  • language
  • url of help feed
  • asynchronous

What I would want to build on is the language part, as the interoperability of a rest api is huge.  If you make an implementation of the help generator in C# for example and use a Strategy pattern for the generation then this gives rise to the following:

  • C# Generation Strategy
  • VB.NET Generation Strategy
  • ActionScript Generation Strategy
  • C++ Generation Strategy
  • Pure JavaScript Generation Strategy
  • Jquery Generation Strategy (Yes I differentiated from the JavaScript option)
  • PHP Generation Strategy
  • Java Strategy
  • Python Strategy
  • Ruby on rails strategy

So you get the idea, it could be built then extended over time.  Am I getting ahead of myself here?  Is there one in production? WHO KNOWS? but it is fun never the less to jump in and have a go. 

So the quick Code Up I did is as follows:

Duplicate the type used in the REST Service and decorate with the namespace for XML Serialization needs

    [XmlRoot(Namespace="http://schemas.datacontract.org/2004/07/Swissmod.Service.Model")]
    public class Project
    {
        public string ID { get; set; }
        public string UserID { get; set; }
        public string ClientID { get; set; }
        public string ProjectTitle { get; set; }
        public string ProjectDescription { get; set; }
        public DateTime Created { get; set; }
        public DateTime LastModified { get; set; }
        public string Version { get; set; }

        public override string ToString()
        {
            StringBuilder sb1 = new StringBuilder();

            foreach (PropertyInfo pi in this.GetType().GetProperties())
            {
                sb1.AppendLine(pi.Name + " : " + pi.GetValue(this, null));
            }

            return sb1.ToString();
        }
    }

Define the abstract class for the Project Service

    public abstract class ProjectRestClient
    {
        protected string baseUrl = "http://test.@yoursite.com/ProjectService.svc/";

        public abstract Project GetProject(string id);
    }

Implement an XML Version of the Project Rest Client

    public class ProjectXmlRestClient : ProjectRestClient
    {
        public override Project GetProject(string id)
        {
            WebRequest wr = WebRequest.Create(baseUrl + id);
            wr.Method = "GET";
            wr.ContentType = "text/xml";
            WebResponse wresp = wr.GetResponse();
            XmlSerializer serializer = new XmlSerializer(typeof(Project));
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationFlags = XmlSchemaValidationFlags.None;
            settings.ValidationType = ValidationType.None;
            settings.ConformanceLevel = ConformanceLevel.Auto;
            settings.IgnoreProcessingInstructions = true;
            settings.NameTable = new NameTable();
            settings.NameTable.Add("http://schemas.datacontract.org/2004/07/Swissmod.Service.Model");
            XmlReader reader = XmlReader.Create(wresp.GetResponseStream(),settings);
            Project p = (Project)serializer.Deserialize(reader);
            return p;
        }
    }

Implement a JSON Version of the Project Rest Client

    public class ProjectJsonRestClient : ProjectRestClient
    {
        public override Project GetProject(string id)
        {
            WebRequest wr = WebRequest.Create(baseUrl + id + "?format=json");
            wr.Method = "GET";
            WebResponse wresp = wr.GetResponse();
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Project));
            Project p = (Project)serializer.ReadObject(wresp.GetResponseStream());
            return p;
        }
    }

Give it a whirl

At this point I know what is going to happen, but I am just protyping to give myself ideas for when I come to make a generation tool for the actual complete feed.  IF I CAN OFCOURSE! lol :-)

    public static class Program
    {
        const string XML = "XML";
        const string JSON = "JSON";

        [STAThread]
        public static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();
            container.RegisterType<ProjectRestClient, ProjectXmlRestClient>(XML, new ContainerControlledLifetimeManager());
            container.RegisterType<ProjectRestClient, ProjectJsonRestClient>(JSON, new ContainerControlledLifetimeManager());

            ProjectRestClient xmlCient = container.Resolve<ProjectRestClient>(XML);
            ProjectRestClient jsonCient = container.Resolve<ProjectRestClient>(JSON);

            Console.WriteLine(xmlCient.GetProject("1"));
            Console.WriteLine(jsonCient.GetProject("1"));

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }

P.S. I have used unity here for a few reasons but mainly because I love the idea of Inversion of Control and Dependency Injections.  The above simply allows me to obtain a singleton instance of either class whenever I want to execute a service method.

image

So to summarise, the only reason I am doing this is so that the consumption of the Rest service is strongly typed, I am not doing this because I think it is a necessity, simply because I think it would be extremely helpful for me.  The ability to strongly type things gives me much more happiness when working across language barriers.

Anyways,

Cheers for now,

Andrew



.NET | ASP.NET | C# | WCF
Tuesday, March 17, 2009 4:42:57 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer

I had a situation recently where:

  1. I needed a cascading drop down interface
  2. I needed it to execute on the client
  3. I needed to reload the parent and child items' selection.

The Cascading DropDownList Extender is an excellent control as are its siblings inside the AjaxControlToolkit.  At first I was scouring the outputted JavaScript that it uses inline with the ServiceMethod to find a method by which I could programmatically set the selected index, which in turn should trigger the data retrieval.

"All other things being equal, the simplest solution is the best."

Occam's razor

It was at this point I saw that the Cascading DropDownList extender actually has a SelectedValue property.  I could have kicked myself when i saw it, as really I should have researched the control more and its capabilities and limitations.  Ever heard of the 7 P's - British SAS lol ?

So I integrated it with a control I built.  The control was a Region, County, TownCity and Area selection in respective order, so as to bring more structure to clients addresses for a project I am currently on.  Similar to a previous post where I create a composite control I declare the control extenders inside this custom composite control and control which I render dependant on other properties which are set. 

        private void CreateRegion()
        {
            DropDownListRegion = new DropDownList();
            DropDownListRegion.ID = this.ID + "_DropDownListRegion";
            CascadingDropDownRegion = new CascadingDropDown();
            CascadingDropDownRegion.ID = this.ID + "_CascadingDropDownRegion";
            CascadingDropDownRegion.Category = REGION_CATEGORY;
            CascadingDropDownRegion.EmptyText = REGION_EMPTYTEXT;
            CascadingDropDownRegion.EmptyValue = "0";
            CascadingDropDownRegion.LoadingText = REGION_LOADINGTEXT;
            CascadingDropDownRegion.PromptText = REGION_PROMPTTEXT;
            CascadingDropDownRegion.PromptValue = "0";
            CascadingDropDownRegion.ServiceMethod = REGION_SERVICE_METHOD;
            CascadingDropDownRegion.ServicePath = SERVICE_PATH;
            CascadingDropDownRegion.TargetControlID = DropDownListRegion.ID;
            if (RegionID != 0)
                CascadingDropDownRegion.SelectedValue = RegionID.ToString();
            Controls.Add(DropDownListRegion);
            Controls.Add(CascadingDropDownRegion);


            if (UseAddressLevel == AddressLevel.Region)
            {
                RequiredFieldValidatorRegion = new RequiredFieldValidator();
                RequiredFieldValidatorRegion.ID = this.ID + "_RequiredFieldValidatorRegion";
                RequiredFieldValidatorRegion.ControlToValidate = DropDownListRegion.ID;
                RequiredFieldValidatorRegion.InitialValue = "0";

                ValidatorCalloutExtenderRegion = new ValidatorCalloutExtender();
                ValidatorCalloutExtenderRegion.ID = this.ID + "_ValidatorCalloutExtenderRegion";
                ValidatorCalloutExtenderRegion.TargetControlID = RequiredFieldValidatorRegion.ID;

                Controls.Add(RequiredFieldValidatorRegion);
                Controls.Add(ValidatorCalloutExtenderRegion);
            }
        }

image

So the whole thing above is a control, but the amount of collapsible extenders displayed is control by an enum called AddressLevel.  How it works is simply like this, if you only require the Town and City, it is this level with which you set it, what happens then is that the area will not be displayed and also the TownAndCity will gain validation.

Line 16 of the code above will set selected index based on whether the RegionID has been set.  I find it so handy and useful that I can strongly type my .NET controls to work with the control extenders as it just adds a greater level to the UI experience.  Strongly Typing Javascript is also a good IDEA and something which I want to touch on in later posts aswell.

Cheers,

Andrew

P.S. setting the selected index for multiple cascading dropdownlists will work so its parent gets populated and set's the selected value using client side code, the child cascading dropdownlist will wait for this event, then populate and THEN check for the presence of the selected value in its items and again use client side to set the selected index.  After that it CASCADES!! lol



AJAX | ASP.NET | C# | JavaScript
Tuesday, January 27, 2009 11:07:54 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer