Click here to Skip to main content
Click here to Skip to main content

ComponentModel for ASP.NET MVC3

, 26 Jun 2011
Rate this:
Please Sign up or sign in to vote.
In this article, I will show you how to build a component model for ASP.NET MVC3 with Builder and Factory design patterns. You could use this model to write your strongly type components to binding data, handling harchical objects and write jQuery UI controls / HTML5 controls for ASP.NET MVC3.

Overview

In this article, I will show you how to build a component model for ASP.NET MVC3 with Builder and Factory design patterns. You could use this model to write your strongly typed components to binding data, handling hierarchical objects and write jQuery UI controls / Html5 controls for ASP.NET MVC3.

Background

As we know, in classical ASP.NET, we could write controls by using ASP.NET control model for our web applications. Today in MVC3, we’re usually writing helpers instead of controls. I think helper methods are a good choice for some simple use scenarios but not for all. For rendering single tag, image or scripts, helper methods do well. I can help think how to develop more complex controls with help methods? How to write template container controls? How to write a data bindable treeview and menu? How to write and use those complex controls easily? In fact, I love the helper method and I hate them at the same time. Because helper methods are the easy way to output my tags on view, but that is not the OOP way ! Helpers are easy to use but hard to extend and maintain. How could you read, remember and maintain a method with over 10 overload versions? That is hell! So we need another way out! The way that could be easy to learn, more readable, OOP, extensible and familiar for our control developing.

Before You Start

There are some concepts you need to know:

Objectives

We need a component model for ASP.NET MVC.

  • Supports Razor and Web Form syntax
  • Offers interfaces or abstract classes to implement
  • Supports control template
  • Supports composite components
  • Supports data binding
  • Easy to integrate with client ability (JavaScripts jQuery)

Usage Design

The biggest difference between MVC3 and classic ASP.NET is MVC3 uses Views and helpers to render HTML tags directly but not any component model like classic ASP.NET. So before designing the component model, we need to know how many ways we could write controls in MVC3, then we also need to know the common behaviors, properties, usages of the controls. Now let’s look back in MVC3 and do some testing.

Usage 1: Extension Method

We call “Html.XXX()” and “Ajax.XXX()” MVC Helpers or Extensions, actually they are static extension methods that are a good way to inject new methods to an existing class. When you are using Razor, it provides a new syntax to write the helper method by using @helper. I put build-in helpers which we are often using in the following list (notes: each method has more then 5 overloaded versions):

  • Html.Display()
  • Html.DisplayFor()
  • Html.Label()
  • Html.LabelFor()
  • Html.LablelForModel()
  • Html.ActionLink()
  • Html.RoutLink()
  • Html.Editor()
  • Html.EditorFor()
  • Html.EditorForModel()
  • Html.CheckBox()
  • Html.CheckBoxFor()
  • Html.Hidden()
  • Html.Password()
  • Html.PasswordFor()
  • Html.RadioButton()
  • Html.RadioButtonFor()
  • Html.TextBox()
  • Html.TextBoxFor()
  • Html.ListBox()
  • Html.ListBoxFor()
  • Html.DropDownList()
  • Html.DropDownListFor()
  • Html.TextArea()
  • Html.TextAreaFor()

There are almost over 100 methods with overload versions of this list! Please note that these helpers only use to render <input> <a> <select> <textArea> tags maybe call forms. Why does helper have so many overloads ?! Just for “DIY“ and “Clean” I think. Although the helpers would be more simple to use but as we see they will bring a large number of overloads. They become more unreadable, hard to remember and hard to maintain.

Conclusion: The helper methods should be used for simply controls without many options. That is not the OOP way.

Usage 2: Template

In classic ASP.NET, we would usually use Template in our container control that is awesome! But how could we do that in MVC? I couldn’t find any solution in the ASP.NET official website but only write helper methods or view files. How to write a strongly typed control that enables template feature? For example how to write a Panel control in MVC3 ? Even for form tag, the MVC offers two strange methods ”BeginForm”, “EndForm” to implement it. Why not provide a method like this:

Razor

@{
    Html.Form()
        .Body(@<text>
                <p>Here is form body</p>
         </text>)
        .End();
}

Web Form

<%
    Html.Form()
        .Body(()=>{%>
             <p>Here is form body</p>
         <%})
        .End();
%>

That is what I want. As you see, Web Form and Razor have implantation of Template. In Web Form syntax, we should use Action delegate to embed other HTML tags. For Razor ViewEngine only allows using inline block text @<text></text>, in the example above, I use “Builder” design pattern act as a control role of M-V-C pattern.

OK, I have found the way to implement Template for controls. That is very useful for every container control.

Usage 3: Composite Controls

At the age of the classic ASP.NET (in my time, I am an old man you know ^^), I remember I had often implemented the CompositeControl base class of my complex controls. Obviously, when our control becomes more complex, we need to provide more options for them to control their behaviors, only using helper methods is not enough. We use a component model to implement the composite controls such as TreeView, Grid, ComobBox, ListBox, Menu, SiteMap, Toolbar and so on. These controls are common controls we would use anywhere and anytime.

So let’s imagine how to use these “Controls” in our MVC project:

@{
  Ajax.DJME().ComboBox()
                    .Width(200)
                    .Items(items=>{
                        items.Add("text1","value1");
                        items.Add("text1","value1");
                        items.Add().Template(@<text><a href="#">
				I am template item.</a></text>);
                    })
                    .Render();
}

@{
Ajax.DJME().TreeView()
                 .Nodes(nodes=>{
                      nodes.Add("node1","http://domain.com");
                      nodes.Add("node2","http://domain.com");
                      nodes.Add("node3","http://domain.com")
                               .Nodes(children=>{
                                    children.Add("subNode1","http://sub.domain.com");
                                });
                 })
}

Composite usage is very similar to the Template usage. It also uses Action delegate to implement it.

Usage 4: Data Binding

In MVC way, we will generate the Model instance in Controller side and pass it in ViewData.Model to View side. So our component only runs in View side, we should not be concerned how to generate model, it just a parameter for controls. And data binding is just the automatic version of the Composites usage. The data binding usage may be like this:

@{
  Ajax.DJME().ListBox()
             .Bind(Model)
             .Render();
}

@{
  Ajax.DJME().Grid(Model)
             .AutoGenerateColumns()
             .Sortable()
             .Pagable()
             .Render();
}

ComponentModel:Using Builder and Factory Method Design Patterns to Implement M-V-C Pattern

We previously discussed a number of usages in how the MVC may be written, where we will be able to explore in depth how to build an abstract level object model and implementation.

First, before discussing the object model design, you need to remember that ASP.NET MVC is only a framework or a set of development tools, in fact M-V-C is a very classic design pattern. So our component model will be implementing the MVC pattern. Now let’s take a look in the class diagram of the abstraction level objects.

djme_abstraction.png

Model

We should not be concerned about how to generate the model. For components Model, just use as an external data source.

View

The ViewComponent base class acts as View, it uses to hold properties values and render the HTML content. The sub classes override the RenderXXX() methods to write the HTML contents to View response. The ViewComponent is similar to classic ASP.NET Control base class. The following code is written in C# for the ViewComponent.

    public abstract class ViewComponent
    {
        private ViewContext _viewContext;
        private string _id;

        public ViewContext ViewContext
        {
            get { return _viewContext; }
        }

        public ViewComponent(ViewContext viewContext)
        {
            this._viewContext = viewContext;
        }

        public virtual string Name { get; set; }

        public virtual string ClientID
        {
            get
            {
                if ((string.IsNullOrEmpty(_id)) && (!string.IsNullOrEmpty(Name)))
                {
                    var _tag = new TagBuilder(TagName);
                    _tag.GenerateId(Name);
                    if (_tag.Attributes.ContainsKey("id"))
                        _id = _tag.Attributes["id"];
                    else
                        _id = Name;
                }
                return _id;
            }
            private set { _id = value; }
        }

        public virtual string TagName
        {
            get
            {
                return "div";
            }
        }

        public virtual void Render(HtmlTextWriter writer)
        {
            RenderBeginContent(writer);

            RenderConent(writer);

            RenderEndConent(writer);
        }

        public virtual void RenderBeginContent(HtmlTextWriter writer)
        {
            TagBuilder _tag = new TagBuilder(TagName);

            if (!string.IsNullOrEmpty(Name))
            {
                _tag.GenerateId(Name);
                if (!_tag.Attributes.ContainsKey("id"))
                    _tag.MergeAttribute("id", Name);

                Id = _tag.Attributes["id"];

                if (TagName.Equals("input", StringComparison.OrdinalIgnoreCase) || 
		TagName.Equals("textarea", StringComparison.OrdinalIgnoreCase))
                    _tag.MergeAttribute("name", Name);
            }

            writer.Write(_tag.ToString(TagRenderMode.StartTag));
        }

        public virtual void RenderConent(HtmlTextWriter writer) { }

        public virtual void RenderEndTag(HtmlTextWriter writer)
        {
            writer.WriteEndTag(TagName);
        }

        public string GetHtml()
        {
            var result = new StringBuilder();
            using (var writer = new HtmlTextWriter(new StringWriter(result)))
            {
                Render(writer);
            }
            return result.ToString();
        }
    }

Control

Then ViewComponentBuilder act as Control role of MVC pattern and it implements the Builder design pattern. It controls the ViewComponent build progress.

    public class ViewComponentBuilder<TComponent,TBuilder>
        where TComponent : ViewComponent
        where TBuilder : ViewComponentBuilder<TComponent, TBuilder>
    {
        public ViewComponentBuilder(TComponent component)
        {
            this.Component = component;
        }

        private TComponent component;

        public TComponent Component
        {
            get { return component; }
            private set { component = value; }
        }

        public ViewContext ViewContext
        {
            get
            {
                return component.ViewContext;
            }
        }

        public TBuilder GenerateId()
        {
            if (string.IsNullOrEmpty(Component.Name))
            {
                string prefix = Component.GetType().Name;
                string key = "DJME_IDSEQ_" + prefix;
                int seq = 1;

                if (ViewContext.HttpContext.Items.Contains(key))
                {
                    seq = (int)ViewContext.HttpContext.Items[key] + 1;
                    ViewContext.HttpContext.Items[key] = seq;
                }
                else
                    ViewContext.HttpContext.Items.Add(key, seq);
                Component.Name = prefix + seq.ToString();
            }

            return this as TBuilder;
        }

        public virtual TBuilder Name(string name)
        {
            Component.Name = name;
            return this as TBuilder;
        }

        public virtual void Render()
        {
            RenderComponent();
        }

        protected void RenderComponent()
        {
            using (var writer = new HtmlTextWriter(ViewContext.Writer))
            {
                Component.Render(writer);
            }
        }

        public virtual MvcHtmlString GetHtml()
        {
            return MvcHtmlString.Create(Component.GetHtml());
        }
    }

In client side, the developer will not use the ViewComponent directly but ViewComponentBuilder. In order to use the builder and write the code inline, each control method of builder should return itself. We need to call Render() or GetHtml() method to get the build result after finishing the build progress.

The following code is the usage of the ViewComponentBuilder:

@{
    Ajax.YourControl()
        .Name("Your control name")
        .Render();
}  

Why the ViewComponentBuilder has Render() and GetHtml() result getting methods? Because of considerations of the WebForm and Razor's ViewEngines output mechanisms. The following list is the explanation of these methods:

  • Render - Invoke by ViewEngine and write the ViewComponent output result to response. It will be lazy called and return nothing.
  • GetHtml – It will return the ViewComponent output result as MvcHtmlString when it will be invoked.

Factory Method and Helper Method

Finally we need a class to construct the ViewComponent and ViewCompoentBuilder and put them to work together. And we also need to return the builder and inject the construct method into HtmlHelper or AjaxHelper. Now we will be using Factory method design pattern to implement it.

#1. Create an extension method and inject to HtmlHelper that makes Html.Demo() could return a ComponentFactory instance.

   public static class Extensions
  {
     public static SimpleFactory Demo(this HtmlHelper helper)
     {
            return new SimpleFactory (helper.ViewContext);
     }
  }

#2. Create a component factory that contains the ViewComponentBuilder construct methods.

  public class SimpleFactory
  {
      private ViewContext _viewContext;

      public ViewContext ViewContext
      {
         get { return _viewContext; }
      }

      public SimpleFactory (ViewContext viewContext)
      {
          this._viewContext = viewContext;
      }

      public TextViewComponentBuilder Text()
      {
          return new TextViewComponentBuilder
		(new TextViewComponent(this.ViewContext)).GenerateId();
      }
  }

#3. We could use the factory like this:

@Html.Demo().Text().GetHtml()

The factory does not inherit from any base classes. It only supports construction of ViewComponentBuilder. You can define various factories to group different kinds of components methods. The factory method and static extension method together can bring us various challenges. We can use abstract factory to create a serial of component models having the same behavior. You can even use DI in static extension method to create/construct component factory dynamically. Imagine you write a component set of HTML Input, and you can provide a component set of Html5 input or jQuery input later. Just modify abstract factory in static methods.You can inject the implement of abstract factory via DI. How to dynamically construct component is a vivid topic, while I just offer here. I would write some articles to share with you later on this topic.

Tutorial 1: Implement HTML5 Input Controls

Now let’s use this component mode to create a Html5 inputs control set.

The following figure is the class diagram of the Html5 input classes.

Html5Input_Classes.png
Step 1: Implement ViewComponent

Create Html5InputViewComponent derived from ViewComponent.Override the Render method and render the input HTML tag:

    public class Html5InputViewComponent:ViewComponent
    {
        public Html5InputViewComponent(ViewContext viewContext) : base(viewContext) { }

        private Html5InputTypes inputType = Html5InputTypes.Text;

        public Html5InputTypes InputType
        {
            get { return inputType; }
            set { inputType = value; }
        }

        public object Value { get; set; }

        public override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            TagBuilder input = new TagBuilder("input");
            input.GenerateId(this.Name);

            if (InputType.Equals(Html5InputTypes.DatetimeLocal))
                input.MergeAttribute("type", "datetime-local");
            else
                input.MergeAttribute("type", inputType.ToString().ToLower());

            if (!input.Attributes.ContainsKey("name"))
                input.Attributes.Add("name", this.Name);

            if (Value != null)
                input.MergeAttribute("value", Value.ToString());

            writer.Write(input.ToString(TagRenderMode.SelfClosing));
        }
    }
Step 2: Implement ViewComponentBuilder

Create a Html5InputViewComponentBuilder to control the Html5InputViewComponent. In this case, we only add a "Value" method to set the Html5InputViewComponent.Value property.

public class Html5InputViewComponentBuilder :
	ViewComponentBuilder<Html5InputViewComponent, Html5InputViewComponentBuilder>
    {
        public Html5InputViewComponentBuilder(Html5InputViewComponent component) :
				base(component) { }

        public Html5InputViewComponentBuilder InputType(Html5InputTypes type)
        {
            Component.InputType = type;
            return this;
        }

        public Html5InputViewComponentBuilder Value(object value)
        {
            Component.Value=value;
            return this;
        }
    }
Step 3: Create a Factory

The Html5ViewComopnentBuilderFactory class contains a set of construct methods. This factory provide some simplify wrapper method that pre build the Html5InputViewComponent.

    public class Html5ViewComponentBuilderFactory
    {
        private ViewContext _viewContext;

        public ViewContext ViewContext
        {
            get { return _viewContext; }
        }

        public Html5ViewComponentBuilderFactory(ViewContext viewContext)
        {
            this._viewContext = viewContext;
        }

        public Html5InputViewComponentBuilder Input(Html5InputTypes type)
        {
            return new Html5InputViewComponentBuilder
		(new Html5InputViewComponent(this.ViewContext))
                .GenerateId()
                .InputType(type);
        }

        public MvcHtmlString Number(int value=0)
        {
            return new Html5InputViewComponentBuilder
		(new Html5InputViewComponent(this.ViewContext))
                .GenerateId()
                .InputType(Html5InputTypes.Number)
                .Value(value)
                .GetHtml();
        }

        public MvcHtmlString Range(int value = 0)
        {
            return new Html5InputViewComponentBuilder
		(new Html5InputViewComponent(this.ViewContext))
                .Value(value)
                .GenerateId()
                .InputType(Html5InputTypes.Range)
                .GetHtml();
        }

        public Html5InputViewComponentBuilder Date()
        {
            return new Html5InputViewComponentBuilder
		(new Html5InputViewComponent(this.ViewContext))
                .GenerateId()
                .InputType(Html5InputTypes.Date);
        }
    }
Step 4: Write an extension method to inject the Factory into HtmlHelper
    public static class Extenstions
    {
        public static Html5ViewComponentBuilderFactory Html5(this HtmlHelper helper)
        {
            return new Html5.Html5ViewComponentBuilderFactory(helper.ViewContext);
        }
    }
Step 5: Write the client code in View
<fieldset>
      <legend>Html5 Input components demo</legend>
      <p>
          Number:
      </p>
      <p>
          @Html.Html5().Number(20)
      </p>
      <p>
          Range:
      </p>
      <p>
          @Html.Html5().Range(5)
      </p>
      <p>
          Date:
      </p>
      <p>
          @Html.Html5().Date()
      </p>
 </fieldset>

html5_run_result.png

Templating

In order to add the template ability to component, I defined an IHtmlTemplate interface.

    public interface IHtmlTemplate
    {
        Action Content { get; set; }

        Func<object, object> InlineContent { get; set; }

        bool IsEmpty { get; }

        void WriteTo(HtmlTextWriter writer);
    }

The IHtmlTemplate provides two template properties:

  • Content – This template property is an Action delegate that allows developers using it in View like this: Content(()=>{ @*content body*@ }. It can support WebForm and Razor syntax.
  • InlineContent – This template property only works for Razor ViewEngine. Razor recognizes Func<object,object> deletage and renders it as inline text block @<text> .

Implement IHtmlTemplate interface:

      public class HtmlTemplate : IHtmlTemplate
      {
        public Action Content { get; set; }

        public Func<object, object> InlineContent { get; set; }

        public void WriteTo(System.Web.UI.HtmlTextWriter writer)
        {
            if (Content != null)
            {
                Content.Invoke();
            }
            else
            {
                if (InlineContent != null)
                    writer.Write(InlineContent(null).ToString());
            }
        }

        public bool IsEmpty
        {
            get
            {
                return ((Content == null) && (InlineContent == null));
            }
        }
    }

In the following tutorial, I will show you how to use the IHtmlTemplate and HtmlTemplate in components.

Tutorial 2: Container Component

This tutorial is how to create a Panel component with body template.

Step 1. Implement PanelViewComponent

Add a PanelViewComponent derived from ViewComponent. Add a Content property and set the type as HtmlTemplate (It also could be set to IHtmlTemplate).

   public class PanelViewComponent:ViewComponent
   {
        public PanelViewComponent(ViewContext context) : base(context)
        {
            Content = new HtmlTemplate();
        }

        public HtmlTemplate Content { get; set; }

        public string Title { get; set; }

        public override void RenderConent(System.Web.UI.HtmlTextWriter writer)
        {
            writer.WriteFullBeginTag("div");
            writer.Write(Title);
            writer.WriteEndTag("div");

            if (!Content.IsEmpty)
                Content.WriteTo(writer);
        }
    }
Step 2. Implement PanelViewComponentBuilder
    public class PanelViewComponentBuilder:ViewComponentBuilder
		<PanelViewComponent,PanelViewComponentBuilder>
    {
        public PanelViewComponentBuilder(PanelViewComponent component) :
		base(component) { }

        public PanelViewComponentBuilder Title(string title)
        {
            Component.Title = title;
            return this;
        }

        public PanelViewComponentBuilder Body(Action body)
        {
            if (body != null)
                Component.Content.Content = body;
            return this;
        }

        public PanelViewComponentBuilder Body(Func<object, object> body)
        {
            if (body != null)
                Component.Content.InlineContent = body;
            return this;
        }
    }

We expose the Action and Func delegate in Body methods to developers.

Step 3: Add the factory method
    public static class Extenstions
    {
        public static DNA.Mvc.ComponentModel.Html5.Html5ViewComponentBuilderFactory Html5
			(this HtmlHelper helper)
        {
            return new Html5.Html5ViewComponentBuilderFactory(helper.ViewContext);
        }

        public static PanelViewComponentBuilder Panel(this HtmlHelper helper)
        {
            return new PanelViewComponentBuilder
		(new PanelViewComponent(helper.ViewContext)).GenerateId();
        }
    }
Step 4: Using the Panel in View
@{
    Html.Panel()
        .Title("Panel title")
        .Body(@<text>
           <p> Hi here is panel body contents. </p>
         </text>)
        .Render();
} 

Composites Component

As is stated above, it becomes a complex component when our component combines different sub-components. Basically, ViewComponent, ViewComponentBuilder and IHtmlTemplate can implement most of the components. And what we mostly think of complex components is:

  • How to construct children components programmatically
  • How to construct children components for data binding

Simply, composite components are made of component container and children components. In many cases, the composite component is made up of many different types of children components. In order to make the usage of children components with container in the same way, we should pass a constructor method of child component builder in container Builder method context. It reduces the coupling and is easy to make changes.

Tutorial 3: LinkList

In this tutorial, I will show you how to create a simple list component. It is made up of a list container (LinkList) and numbers of link list item components. I will show how to add the link list item within the linklist builder method.

Step1: Create container component and children component
 public class LinkListComponent:ViewComponent
    {
        public LinkListComponent(ViewContext context) : base(context)
		{ Links = new List<LinkComopnent>(); }

        public virtual ICollection<LinkComopnent> Links { get; set; }

        public override string TagName
        {
            get
            {
                return "ul";
            }
        }

        public override void RenderConent(System.Web.UI.HtmlTextWriter writer)
        {
            foreach (var link in Links)
            {
                link.Render(writer);
            }
        }
    }

    public class LinkListComponentBuilder:ViewComponentBuilder
			<LinkListComponent,LinkListComponentBuilder>
    {
        public LinkListComponentBuilder(LinkListComponent component) : base(component) { }

        public LinkListComponentBuilder Items(Action<LinkComponentBuilderFactory> items)
        {
            var factory=new LinkComponentBuilderFactory(this.Component);

            if (items != null)
                items.Invoke(factory);

            return this;
        }
    }

    public class LinkComopnent:ViewComponent
    {
        public LinkComopnent(ViewContext context) : base(context) { }

        public string Title { get; set; }

        public string Url { get; set; }

        public override string TagName
        {
            get
            {
                return "li";
            }
        }

        public override void RenderConent(System.Web.UI.HtmlTextWriter writer)
        {
            var a = new TagBuilder("a");
            a.MergeAttribute("href", Url);
            a.InnerHtml = Title;
            writer.Write(a.ToString());
        }
    }

    public class LinkComponentBuilder:ViewComponentBuilder
			<LinkComopnent,LinkComponentBuilder>
    {
        public LinkComponentBuilder(LinkComopnent component) : base(component) { }

        public LinkComponentBuilder Title(string title)
        {
            this.Component.Title = title;
            return this;
        }

        public LinkComponentBuilder Url(string url)
        {
            Component.Url = url;
            return this;
        }
    }
Step2: Add children component builder factory and container component builder
    public class LinkComponentBuilderFactory
    {
        public LinkListComponent ItemContainer { get; set; }

        public LinkComponentBuilderFactory(LinkListComponent container)
        {
            this.ItemContainer = container;
        }

        public LinkComponentBuilder Add()
        {
            var item=new LinkComopnent(ItemContainer.ViewContext);
            ItemContainer.Links.Add(item);
            return new LinkComponentBuilder(item);
        }
    }    
  • Create the children component and add to container component.
  • Create children component builder and bind to children component.
  • Return the children component builder to client view.
   public class LinkListComponentBuilder:ViewComponentBuilder
			<LinkListComponent,LinkListComponentBuilder>
    {
        public LinkListComponentBuilder(LinkListComponent component) : base(component) { }

        public LinkListComponentBuilder Items(Action<LinkComponentBuilderFactory> items)
        {
            var factory=new LinkComponentBuilderFactory(this.Component);

            if (items != null)
                items.Invoke(factory);

            return this;
        }
    }

We pass the LinkComponentBuilderFactory as a parameter in Action delegate to client view.

Step 3: Using in View
@{
    Html.LinkList()
        .Items(items =>
        {
            items.Add().Title("Item1").Url("#");
            items.Add().Title("Item1").Url("#");
            items.Add().Title("Item1").Url("#");
        })
        .Render();
}

Now we provide the way to add the children component in View manually. When we want the component could create the child itself, that is another usage - DataBinding.

We only make some changes that make the LinkListComponentBuilder support binding to data Model.

public class LinkListComponentBuilder : ViewComponentBuilder<LinkListComponent,
		LinkListComponentBuilder>
    {
        public LinkListComponentBuilder(LinkListComponent component) : base(component) { }

        public LinkListComponentBuilder Items(Action<LinkComponentBuilderFactory> items)
        {
            var factory = new LinkComponentBuilderFactory(this.Component);

            if (items != null)
                items.Invoke(factory);

            return this;
        }

        public LinkListComponentBuilder Bind<T, TKey, TValue>(IEnumerable<T> model,
           Expression<Func<T, TKey>> keySelector,
           Expression<Func<T, TValue>> valueSelector)
            where T : class
        {
            if (model != null)
            {
                this.Items(items=>{
                    foreach (var t in model)
                    {
                        items.Add()
                             .Title(keySelector.Compile().Invoke(t) as string)
                             .Url(valueSelector.Compile().Invoke(t) as string);
                    }
               });
            }
            return this;
        }
    }
@{
    var listitems = new List<Dna.Demo.Web.Collation>();
    listitems.Add(new DNA.Demo.Web.Collation() { Key="BindingItem1",Value="#" });
    listitems.Add(new DNA.Demo.Web.Collation() { Key = "BindingItem2", Value = "#" });
    listitems.Add(new DNA.Demo.Web.Collation() { Key = "BindingItem3", Value = "#" });

    Html.LinkList()
        .Bind(listitems, m => m.Key, m => m.Value)
        .Render();
}

Addition

I used this component model and jQuery client ability to create an open source project DJME - The jQuery MVC Extensions. Including all jQuery UI widgets and many common components such as TreeView, Menu,Toolbar, SiteMap, ContextMenu, DropDownMenu, Textbox, ComobBox, ListBox, RichTextBox and as so on. If you are interested in jQuery MVC extensions, you can go to http://djme.codeplex.com and download it. At the same time, you can also go to http://www.dotnetage.com to try the online demo.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Ray_Liang
Architect DotNetAge
China China
In 1999, I started programming using Delphi, VB, VJ.From 2002 I started with .NET using C#.Since 2005 when i had became an EIP product manager I was focus on EIP and CMS technique. In 2008 i established dotnetage.com and started to shared my ideas and projects online. I believe "No shared no grow"
 
www.dotnetage.com
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermanoj kumar choubey24-May-12 3:17 
GeneralMy vote of 5 Pinmembers_e_r_g_e_y3-Feb-12 5:13 
GeneralMy vote of 5 PinmemberMrBretticus24-Nov-11 16:38 
GeneralMy vote of 5 Pinmemberksafford13-Oct-11 3:41 
QuestionWell and good PinmemberBattusuman27-Jun-11 2:09 
AnswerRe: Well and good Pinmemberphung van hung10-Aug-12 17:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140814.1 | Last Updated 27 Jun 2011
Article Copyright 2011 by Ray_Liang
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid