Custom Controls - Object-Oriented

Creating custom controls will be illustrated by using a simple example of a control that projects a picture, a name and a link to the user profile.

Stage 1: Preparing a Base

Step 1: Creating a Class

Every control needs to inherit from a MvcComponent class. We create a generic class that inherits from the MvcComponent class. The class contains two generic parameters: TComponent and TModel, that is, a type and a model of the control, respectively.

                
                    public class UserInfo<TModel> : MvcComponent<UserInfo<TModel>, TModel>
                    {
                    }
                
            
UserInfo<TModel> class.

Step 2: Creating a constructor

Then we need to invoke one of the base constructors, which a type of the HTML element will be conveyed to. This HTML element type will be a base for the control. We can select from several constructors. Every base constructor takes also an object implementing an IMvcComponent interface so our constructor must also take it. In the example we will use a constructor that takes an enum type called Html5 with the tag name.

                
                    public class UserInfo<TModel> : MvcComponent<UserInfo<TModel>, TModel>
                    {
                        public enum CustomTag
                        {
                            UserProfileComponent
                        }

                        /*
                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, HtmlTag.Create("div"))
                        {
                            // Result: <div></div>
                        }

                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, HtmlTags.Create("div", "div"))
                        {
                            // Result: <div></div><div></div>
                        }
                        */

                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, Html5.Div)
                        {
                            // Result: <div></div>
                        }

                        /*
                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, Html5.Div, Html5.Div)
                        {
                            // Result: <div></div><div></div>
                        }

                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, CustomTag.UserProfileComponent)
                        {
                            // Result: <user-profile-component></user-profile-component>
                        }

                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, WQ.CreateTag("div"))
                        {
                            // Result: <div></div>
                        }
                        */
                    }
                
            
Possible constructors for a UserInfo<TModel> class.

Step 3: Setting the Width

Already in the constructor we can edit a base HTML code. With the assistance of a Style method we set the width of the control to 100 percent. The method influences all the main objects of the control. In this case we have one major element div.

An equivalent for a Style method in jQuery is a css method.

 
                
                    public class UserInfo<TModel> : MvcComponent<UserInfo<TModel>, TModel>
                    {
                        public UserInfo(IMvcService<TModel> mvcService) : base(mvcService, Html5.Div)
                        {
                            Style(Css.Width, 100, Unit.Percent);
                        }
                    }
                
            
Adding a style.

Stage 2: Implementation of the Methods

Picture Method

A Picture method will add to the control the picture, which is located at the address given in the method parameter. In such a case we use an Image control that will create an img tag with a src attribute. The Image control is added by using a Prepend method which inserts it at the beginning in the major element. If the picture address is not given, the control will display a placeholder.

A MvcService.WB property provides access to every other control of the package.

                
                    public UserInfo<TModel> Picture(string pictureUrl)
                    {
                        if (string.IsNullOrWhiteSpace(pictureUrl))
                        {
                            pictureUrl = "https://placehold.it/200x200";
                        }

                        Prepend(MvcService.WB.Image().Style(Css.Width, 100, Unit.Percent).Fluid().Src(pictureUrl));
                        return this;
                    }
                
            
Implementation of the Picture method.

Name Method

A Name method adds a <p> tag to the control, and the content of this tag will be the user name given in the parameter.

The MT and MB methods add appropriate classes responsible for the the top and bottom margins.

The Append method inserts the element given in the parameter to the end of the major element.

                
                    public UserInfo<TModel> Name(string name)
                    {
                        if (string.IsNullOrWhiteSpace(name))
                        {
                            throw new ArgumentException("Name cannot be null, empty or whitespace.", nameof(name));
                        }

                        Append(MvcService.WB.Tag(Html5.P).Text(name).Style(Css.TextAlign, TextAlign.Center).MT(1).MB(0));
                        return this;
                    }
                
            
Implementation of the Name method.

Profile Method

The Profile method inserts a button which directs to a user profile.

A MvcService property contains also the UrlHelper which may be used to generate URL addresses.

                
                    public UserInfo<TModel> Profile(Func<IUrlHelperAdapter, string> url)
                    {
                        if (url == null)
                        {
                            throw new ArgumentNullException(nameof(url));
                        }

                        var button = MvcService.WB.LinkButton().Style(ButtonStyle.Primary)
                            .Size(ButtonSize.BtnSm)
                            .Style(Css.TextAlign, TextAlign.Center)
                            .Text("Profile").MT(1).Style(Css.Width, 100, Unit.Percent)
                            .Href(url(MvcService.UrlHelper));

                        Append(button);
                        return this;
                    }
                
            
Implementation of the Profile method

Extension Method

At the end, we need to create the extension method of the IWB interface, which returns a new control object and conveys to the object an instantion of the MvcService object.

                
                    public static class UserInfoExtensions
                    {
                        public static UserInfo<TModel> UserInfo<TModel>(this IWB<TModel> wb)
                        {
                            return new UserInfo<TModel>(wb.MvcService);
                        }
                    }
                
            
Extension method

Usage

                        
                            @(Html.WB().UserInfo().Name("John Doe")
                                .Picture("https://placehold.it/200x200")
                                .Profile(x => x.Action("Profile", new { id = 1 })))
                        
                    
The use of the control.

Result

UserInfo Component Result A part of the view

Summary

It is only a simple example aiming to show some basics of creating the custom controls. In practise, the controls may contain a very complicated logics.

Back