November 2009 - Posts

Dear readers, as you know that we have released our beta few weeks back, we are currently looking for your feedback on the existing features as well as the features that you would like to see in our next release. This is a very short survey only 4/5 screens to complete, click here to submit your valuable feedback.

Thanks in advance for your precious time.

Shout it

Scott showed how to render the Grid in a Transaction. Certainly it does the job but in my opinion view component should not be responsible for this kind of cross cutting concerns, instead we can use the Action Filters. Lets see how we can utilize the Action Filter in this scenario instead of modifying the Grid code. What Scott is trying to do is encapsulate the data access operation in a transaction, the Action Filter has several methods which the ASP.NET MVC framework executes in different stages of a request. In this case, we will use the OnActionExecuting which fires before the code enters into the controller method to start a transaction and OnResultExecuted which fires when the view is processed, we will commit/rollback based upon the status, here is the code that would process the action result in a transaction:

namespace AltNorthwind
{
    using System.Web.Mvc;
    using System.Transactions;

    public class TransactionAttribute : ActionFilterAttribute
    {
        private CommittableTransaction transaction;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            transaction = new CommittableTransaction();

            base.OnActionExecuting(filterContext);
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);

            try
            {
                if ((filterContext.Exception != null) && (!filterContext.ExceptionHandled))
                {
                    transaction.Rollback();
                }
                else
                {
                    transaction.Commit();
                }
            }
            finally
            {
                transaction.Dispose();
            }
        }
    }
}

Next, we will decorate the Controller Action method with this attribute like the following:

[Transaction]
public ActionResult Index()
{
    return View(repository.All());
}

Hope, you would find it useful.

Shout it

[Updated: Source code attached]

In the recent release, there has been few enhancements in the Web Asset Management. One of the new thing that we introduced which was actually requested by the community is Shared Web Asset. In this post, I will show you, how to use it in your ASP.NET MVC Application.

In the previous version, you can only define the web assets either in the ScriptRegistrar or StyleSheetRegistrar like the following:

<% Html.Telerik()
       .ScriptRegistrar()
       .Scripts(scripts =>
                scripts.AddGroup("myScripts",
                                  group => group.Add("script1.js")
                                                .Add("script2.js")
                                                .Add("script3.js")
                                                .Combined(true)
                                                .Compress(true)
                                )
               )
       .Render();%>

if you want to reuse it in another page, you have to copy the exact same thing. Also the url that it generates is bit cryptic and very long, for example for the above the following html snippet is generated:

<script type="text/javascript" src="http://weblogs.asp.net/asset.axd?id=rgAAAB-LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee--997o7nU4n99__P1xmZAFs9s5K2smeIYCqyB8_fnwfPyJ-8UfT9qNHH2WrVVlMs7aolnffbf90dpk107pYtR-NPrqkr_d2dh6O7413d3fujXfos-lHj9p6ndMvs48e3fv0_ugj-vm9X_zRitq-5hcbanXOn1UfPdoZfbSkbwTk7vinm49-yYi_2PW_2PO-2PO_uMdffP-XfP-X_D-iwlMArgAAAA%3d%3d"></script>

After evaluating quite a different scenarios, we found that we have to include the complete details of the web assets in the script/stylesheet url and this is the reason the url becomes long and cryptic. Behind the scene, we are serializing and compressing the web asset group and then writing it as a base64 encoded string in the page.

With our new Shared Web Asset, you will not only be able to share the same web assets across the different pages, you will also be able to control the generated urls. There are two ways you can define the shared web assets, through the configuration file or with the fluent syntax. First, lets see, how you can define it the in the configuration file.

For example, for the above web assets we can declare it in the web.config file like the following:

<configuration>
    <configSections>
        <sectionGroup name="telerik">
            <section name="webAssets" type="Telerik.Web.Mvc.Configuration.WebAssetConfigurationSection, Telerik.Web.Mvc"/>
        </sectionGroup>
    </configSections>
    <telerik>
        <webAssets>
            <scripts>
                <add name="myScripts" combined="true" compress="true" enabled="true">
                    <items>
                        <add source="script1.js"/>
                        <add source="script2.js"/>
                        <add source="script3.js"/>
                    </items>
                </add>
            </scripts>
        </webAssets>
    </telerik>
</configuration>

Or you can place it the global.asax like the following:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    SharedWebAssets.Scripts(scripts =>
                            scripts.AddGroup("myScripts",
                                              group => group.Add("Script1.js")
                                                            .Add("Script2.js")
                                                            .Add("Script3.js")
                                                            .Combined(true)
                                                            .Compress(true)
                                            )
                           );
}

Now, use the AddSharedGroup of the ScriptRegistrar like the following:

<% Html.Telerik()
       .ScriptRegistrar()
       .Scripts(scripts => scripts.AddSharedGroup("myScripts"))
       .Render(); %>

And it would render the following:

<script type="text/javascript" src="http://weblogs.asp.net/asset.axd?id=myScripts"></script>

You can also mix the configuration file and fluent syntax, in that case, the configuration file assets will be registered first and then the fluent syntax.

Before completing this post, I would like to mention one more feature, though it was available from the CTP release. If you have downloaded the release zip file, in the Example Project, you have seen, we have placed the css and js files under the “2009.3.1103.0” folder in there corresponding assets folders. The “2009.3.1103.0” is actually the version number of our component. When locating any asset the version number folder is first scanned for the requested asset name, if it does not exist, it search its parent folder which is Content for css and Scripts for the js files. The next rule is whether the application is running in debug or release mode (compilation debug="true" in web.config file), when the application is running in debug mode, and lets say the assets is script1.js, then it will be searched in the following order:

1. script1.debug.js
2. script1.js
3. script1.min.js

and in the release mode:

1. script1.min.js
2. script1.js
3. script1.min.js

And this is the reason our examples, we did not mention the actual file name, instead we depend on the framework to resolve it based upon the runtime environment and don’t worry about the performance, in the release mode the resolve logic gets only executed when the asset is accessed for the first time and then we cache the resolve path for the consequent request.

That’s it for today.

Download: SharedWebAsset.zip

Shout it

I am proud to inform you that yesterday we released our Q3 2009 version of Telerik Extensions for ASP.NET MVC. As promised this release includes:

  • Grid
  • Menu
  • PanelBar
  • TabStrip

You can find the live version and source codes in the following locations:

Also checkout the product home page and part-II of Tod’s unofficial faq.

In this post, I will show you how to create a basic CRUD(Create/Read/Update/Delete) application with our new MVC Grid. I will be using both Entity Framework v1.0 with the default web form view engine and NHibernate with Spark to create the CRUD screens for the Customer table of Northwind database.

Lets start with the Entity Framework and Default View Engine.

First, lets create a new ASP.NET MVC application and name it as Northwind, when Visual Studio prompts you for the Unit Test project, just skip it. Now, right click the Models folder and add an Entity Data Model and name it as Database. Next, drag the Customers table of the Northwind database from Server Explorer in the VS design surface.

Now, we will create a generic repository to access the database, lets define the interface first.

public interface IRepository<TEntity, TId>
{
    void Add(TEntity entity);

    void Delete(TId id);

    TEntity Get(TId id);

    IEnumerable<TEntity> All();
}

And the implementation:

public class Repository<TEntity, TId> : IRepository<TEntity, TId>
{
    private readonly Database database;

    public Repository() : this(EntityFrameworkObjectContextPerRequest.CurrentDatabase)
    {
    }

    public Repository(Database database)
    {
        this.database = database;
    }

    public void Add(TEntity entity)
    {
        database.AddObject(TypeName(), entity);
    }

    public void Delete(TId id)
    {
        database.DeleteObject(Get(id));
    }

    public TEntity Get(TId id)
    {
        var typeName = TypeName();

        var keyName = database.MetadataWorkspace
                              .GetItems<EntityType>(DataSpace.CSpace)
                              .Single(meta => meta.Name == typeName)
                              .KeyMembers[0].Name;

        var param = Expression.Parameter(typeof(TEntity), "x");
        var left = Expression.Property(param, keyName);
        var right = Expression.Constant(id);
        var equal = Expression.Equal(left, right);

        var predicate = Expression.Lambda<Func<TEntity, bool>>(equal, param).Compile();

        return All().SingleOrDefault(predicate);
    }

    public IEnumerable<TEntity> All()
    {
        return database.CreateQuery<TEntity>("[" + TypeName() + "]");
    }

    private static string TypeName()
    {
        return typeof(TEntity).Name;
    }
}

Very basic stuff,except the Get() method, since we are creating a generic repository which entity’s identifier is not known, we are using the Entity Framework meta data for building the identifier lambda expression. Please note that the stuffs that I have shown in the above has nothing to do with our Grid, we can directly use the Entity Framework ObjectContext in our Controller instead of this repository.

Now, right click the Controllers folder and a new Controller named CustomerController with the following code:

[HandleError]
public class CustomerController : Controller
{
    private readonly IRepository<Customers, string> repository;

    public CustomerController() : this(new Repository<Customers, string>())
    {
    }

    public CustomerController(IRepository<Customers, string> repository)
    {
        this.repository = repository;
    }

    public ActionResult Index()
    {
        return View(repository.All());
    }

    public ActionResult Details(string id)
    {
        return View(repository.Get(id));
    }

    public ActionResult Create()
    {
        return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create([Bind(Exclude = "CustomerId")]Customers customer)
    {
        customer.CustomerID = CreateNewId();
        repository.Add(customer);

        return RedirectToAction("Index");
    }

    public ActionResult Edit(string id)
    {
        return View(repository.Get(id));
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(string id, FormCollection collection)
    {
        var customer = repository.Get(id);

        UpdateModel(customer, collection.ToValueProvider());

        return RedirectToAction("Index");
    }

    public ActionResult Delete(string id)
    {
        return View(repository.Get(id));
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Delete(string id, string confirm)
    {
        repository.Delete(id);

        return RedirectToAction("Index");
    }

    // Not a bullet proof method, but it should work for the demo
    private string CreateNewId()
    {
        Func<int, string> generateId = length =>
                                       {
                                           string generatingId = string.Empty;
                                           Random rnd = new Random();

                                           for (int i = 1; i <= length; i++)
                                           {
                                               int characterCode = rnd.Next(65, 90); // Only uppercase;
                                               generatingId += Convert.ToChar(characterCode).ToString();
                                           }

                                           return generatingId;
                                       };

        string id = generateId(5);

        while (repository.Get(id) != null)
        {
            id = generateId(5);
        }

        return id;
    }
}

There are one more thing we have to do before we start working on the Views, as you can see in the above the Repository uses a special class EntityFrameworkObjectContextPerRequest in the constructor to get the Database reference. EntityFrameworkObjectContextPerRequest is a HttpModule which creates a new instance of the Database in the BeginRequest and stores it in the HttpContext.Items to reuse it in the same request and in the EndRequest it commit the changes and Dispose the Database instance. Here is the code:

public class EntityFrameworkObjectContextPerRequest : IHttpModule
{
    private static readonly string key = typeof(Database).FullName;

    public static Database CurrentDatabase
    {
        get
        {
            return HttpContext.Current.Items[key] as Database;
        }
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += OnBeginRequest;
        context.EndRequest += OnEndRequest;
    }

    public void Dispose()
    {
    }

    private static void OnBeginRequest(object sender, EventArgs e)
    {
        var database = HttpContext.Current.Items[key] as Database;

        if (database == null)
        {
            database = new Database();
            HttpContext.Current.Items[key] = database;
        }
    }

    private static void OnEndRequest(object sender, EventArgs e)
    {
        var database = HttpContext.Current.Items[key] as Database;

        if (database != null)
        {
            database.SaveChanges();
            database.Dispose();
        }
    }
}

Now, add the Telerik.Web.Mvc.dll from the binary folder that you have downloaded previously.

Once you include the dll, the first thing you should do is register the HttpHandler for the Web Assets, lets put these lines in the web.config file (I am only showing the relevant section of the web.config):

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc" />
        </httpHandlers>
    </system.web>
    <system.webServer>
        <handlers>
            <remove name="AssetHandler" />
            <add name="AssetHandler" preCondition="integratedMode" verb="GET,HEAD" path="asset.axd" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc" />
        </handlers>
    </system.webServer>
</configuration>

Now, Copy the telerik.common.min.css, telerik.vista.min.css and Vista from the download Content folder to your project Content folder. We also have to copy few javascripts files, please copy the telerik.common.min.js, telerik.grid.min.js and telerik.grid.filtering.min.js from the downloaded Scripts folder to your project Scripts folder. Once you are done, it will look very similar to the following:

SE

Now open the Site.Master from Views/Shared directory and put the following lines in the head section:

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <%= Html.Telerik().StyleSheetRegistrar()
                      .DefaultGroup(group => group.Add("Site.css")
                                                  .Add("telerik.common.css")
                                                  .Add("telerik.vista.css")
                                                  .Combined(true)
                                   ) %>
</head>

This will ensure the stylesheets files that we have added in the previous steps will be included when the page renders. We have do the same for javascript files, but this time we will only add a ScriptRegistrar at the bottom of the page, but we will not add the javascript files like we did for the stylesheet files. Lets put the following code at the bottom of the page:

    <% Html.Telerik().ScriptRegistrar()
                     .DefaultGroup(group => group.Combined(true).Compress(true))
                     .Render(); %>
</body>
</html>

Now, go back to the Index method of the CustomerController, right click and select Add View, this will show the Add View Dialog. When the dialog appears, turn on the strongly typed view and select the Customers class as the view data class and finally select the List in View content drop down.

AV

Once the view is generated replace the generated code with the following:

<% Html.Telerik()
       .Grid(Model)
       .Name("customers")
       .PrefixUrlParameters(false)
       .Columns(columns =>
                {
                    columns.Add(c =>
                                {
                                    %>
                                        <%= Html.ActionLink("Edit", "Edit", new { id = c.CustomerID })%>
                                        <%= Html.ActionLink("Delete", "Delete", new { id = c.CustomerID })%>
                                    <%
                                }).Title("Action");

                    columns.Add(c => c.CompanyName).Width(200);
                    columns.Add(c => c.ContactName);
                    columns.Add(c => c.Address);
                    columns.Add(c => c.City);
                    columns.Add(c => c.PostalCode);
                    columns.Add(c => c.Country);
                    columns.Add(c => c.Phone);
                    columns.Add(c => c.Fax);
                })
        .Filterable()
        .Sortable(sort => sort.SortMode(GridSortMode.MultipleColumn))
        .Pageable()
        .Scrollable(scrolling => scrolling.Height(250))
        .Render(); %>

Now press F5 and navigate to /Customers, you will find a nice looking Grid like the following:

GRID

As you can see the Grid has out of the box support for data sorting/filtering/paging, all you have to do is set an IEnumerable<T> as its DataSource, the rest is taken care by itself. If the IEnumerable<T> has a Linq Provider the data will be processed at the database level. If you run the SQL Profiler for the above example and navigate to page 2 you will find the following SQL is generated:

SELECT TOP (10) 
[Project1].[CustomerID] AS [CustomerID], 
[Project1].[CompanyName] AS [CompanyName], 
[Project1].[ContactName] AS [ContactName], 
[Project1].[ContactTitle] AS [ContactTitle], 
[Project1].[Address] AS [Address], 
[Project1].[City] AS [City], 
[Project1].[Region] AS [Region], 
[Project1].[PostalCode] AS [PostalCode], 
[Project1].[Country] AS [Country], 
[Project1].[Phone] AS [Phone], 
[Project1].[Fax] AS [Fax]
FROM ( SELECT [Project1].[C1] AS [C1], [Project1].[CustomerID] AS [CustomerID], [Project1].[CompanyName] AS [CompanyName], [Project1].[ContactName] AS [ContactName], [Project1].[ContactTitle] AS [ContactTitle], [Project1].[Address] AS [Address], [Project1].[City] AS [City], [Project1].[Region] AS [Region], [Project1].[PostalCode] AS [PostalCode], [Project1].[Country] AS [Country], [Project1].[Phone] AS [Phone], [Project1].[Fax] AS [Fax], row_number() OVER (ORDER BY [Project1].[C1] ASC) AS [row_number]
	FROM ( SELECT 
		CASE WHEN ([Extent1].[CustomerID] IS NOT NULL) THEN [Extent1].[CustomerID] END AS [C1], 
		[Extent1].[CustomerID] AS [CustomerID], 
		[Extent1].[CompanyName] AS [CompanyName], 
		[Extent1].[ContactName] AS [ContactName], 
		[Extent1].[ContactTitle] AS [ContactTitle], 
		[Extent1].[Address] AS [Address], 
		[Extent1].[City] AS [City], 
		[Extent1].[Region] AS [Region], 
		[Extent1].[PostalCode] AS [PostalCode], 
		[Extent1].[Country] AS [Country], 
		[Extent1].[Phone] AS [Phone], 
		[Extent1].[Fax] AS [Fax]
		FROM [dbo].[Customers] AS [Extent1]
	)  AS [Project1]
)  AS [Project1]
WHERE [Project1].[row_number] > 10
ORDER BY [Project1].[C1] ASC

The rest of the screens are pretty simple, just use the ASP.NET MVC Add View dialog to implement them and that is it for the Entity Framework and Web form view engine.

Now, lets do the same for NHibernate and Spark.

First, Create a new ASP.NET MVC Project named AltNorthwind and skip the Unit Test Project. Now, the add following the references:

  • NHibernate
  • NHibernate.Linq
  • FluentNHibernate
  • Spark.

Now, right click the Models folder add a new class named Customers and create simple getter/setter properties for the Customers table fields, also ensure the properties are declared as virtual.Next, Add a new class named CustomerMap which will be used by the NHibernate to map the Customers class with the Customer table.

public class CustomerMap : ClassMap<Customers>
{
    public CustomerMap()
    {
        Id(c => c.CustomerID).Length(5).Not.Nullable();
        Map(c => c.CompanyName).Length(40).Not.Nullable();
        Map(c => c.ContactName).Length(30);
        Map(c => c.ContactTitle).Length(30);
        Map(c => c.Address).Length(60);
        Map(c => c.City).Length(15);
        Map(c => c.Region).Length(15);
        Map(c => c.PostalCode).Length(10);
        Map(c => c.Country).Length(15);
        Map(c => c.Phone).Length(24);
        Map(c => c.Fax).Length(24);
    }
}

Lets create the Repository, we will be using the same IRepository interface that we used in the Entity Framework section.

public class Repository<TEntity, TId> : IRepository<TEntity, TId>
{
    private readonly ISession session;

    public Repository() : this(NHibernateSessionPerRequest.CurrentSession)
    {
    }

    public Repository(ISession session)
    {
        this.session = session;
    }

    public void Add(TEntity entity)
    {
        session.SaveOrUpdate(entity);
    }

    public void Delete(TId id)
    {
        session.Delete(Get(id));
    }

    public TEntity Get(TId id)
    {
        return session.Get<TEntity>(id);
    }

    public IEnumerable<TEntity> All()
    {
        return session.Linq<TEntity>();
    }
}

As we can see that we are using the NHibernate ISession which is passed in the constructor similar to the Entity Framework version, we are also using an HttpModule for managing NHibernate session.

public class NHibernateSessionPerRequest : IHttpModule
{
    private static readonly ISessionFactory sessionFactory = CreateSessionFactory();

    public static ISession CurrentSession
    {
        get
        {
            return sessionFactory.GetCurrentSession();
        }
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += OnBeginRequest;
        context.EndRequest += OnEndRequest;
    }

    public void Dispose()
    {
    }

    private static void OnBeginRequest(object sender, EventArgs e)
    {
        var session = sessionFactory.OpenSession();

        session.BeginTransaction();

        CurrentSessionContext.Bind(session);
    }

    private static void OnEndRequest(object sender, EventArgs e)
    {
        var session = CurrentSessionContext.Unbind(sessionFactory);

        if (session != null)
        {
            try
            {
                session.Transaction.Commit();
            }
            catch
            {
                session.Transaction.Rollback();
            }
            finally
            {
                session.Close();
                session.Dispose();
            }
        }
    }

    private static ISessionFactory CreateSessionFactory()
    {
        const string ConnectionStringName = "NorthwindConnectionString";

        var configuration = Fluently.Configure()
                                    .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.FromConnectionStringWithKey(ConnectionStringName)))
                                    .ExposeConfiguration(c => c.SetProperty("current_session_context_class", "web"))
                                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<NHibernateSessionPerRequest>());

        return configuration.BuildSessionFactory();
    }
}

The Controller is same as the Entity Framework version. Now, lets configure the Spark view engine, first put the following lines in the web.config file.

<configSections>
    <section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark"/>
</configSections>
<spark>
    <compilation debug="true">
        <assemblies>
            <add assembly="AltNorthwind"/>
            <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        </assemblies>
    </compilation>
    <pages automaticEncoding="true">
        <namespaces>
            <add namespace="System.Collections.Generic"/>
            <add namespace="System.Linq"/>
            <add namespace="System.Web.Mvc"/>
            <add namespace="System.Web.Mvc.Html"/>
            <add namespace="System.Web.Routing"/>
            <add namespace="Telerik.Web.Mvc.UI"/>
        </namespaces>
    </pages>
</spark>

And register the view engine in the global.asax, like the following:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    SparkEngineStarter.RegisterViewEngine();
}

Now, Copy the telerik.common.min.css, telerik.vista.min.css and Vista from the download Content folder to your project Content folder and  telerik.common.min.js, telerik.grid.min.js and telerik.grid.filtering.min.js from the downloaded Scripts folder to your project Scripts folder, same as we did in the Entity Framework version.

Now, lets add an application.spark file in Views/Shared for the stylesheets and javascripts and add the following code in the head section for the stylesheets registration:

<% Html.Telerik().StyleSheetRegistrar()
                  .DefaultGroup(group => group.Add("Site.css")
                                              .Add("telerik.common.css")
                                              .Add("telerik.vista.css")
                                              .Combined(true)
                               ) 
                   .Render(); %>

And at the bottom for the javascript files:

<% Html.Telerik().ScriptRegistrar()
                 .DefaultGroup(group => group.Combined(true).Compress(true))
                 .Render(); %>

Now, create a new folder named Customer under the Views folder and add new spark file named Index.spark and put the following code:

<content name="TitleContent">
    Customers
</content>
<content name="MainContent">
    <h2>Customers</h2>
    <viewdata model="IEnumerable[[Customers]]"/>
    <% Html.Telerik()
           .Grid(Model)
           .Name("customers")
           .PrefixUrlParameters(false)
           .Columns(columns =>
                    {
                        columns.Add(c =>
                                    {
                                        %>
                                            !{ Html.ActionLink("Edit", "Edit", new { id = c.CustomerID })}
                                            !{ Html.ActionLink("Delete", "Delete", new { id = c.CustomerID })}
                                        <%
                                    }).Title("Action");

                        columns.Add(c => c.CompanyName).Width(200);
                        columns.Add(c => c.ContactName);
                        columns.Add(c => c.Address);
                        columns.Add(c => c.City);
                        columns.Add(c => c.PostalCode);
                        columns.Add(c => c.Country);
                        columns.Add(c => c.Phone);
                        columns.Add(c => c.Fax);
                    })
            .Sortable(sort => sort.SortMode(GridSortMode.MultipleColumn))
            .Pageable()
            .Scrollable(scrolling => scrolling.Height(250))
            .Render(); %>
    <p>!{ Html.ActionLink("Create New", "Create") }</p>
</content>

Once you are done, press F5 and navigate to /Customer, you will find a nice looking Grid. I am skipping the rest of the views as they are pretty easy to implement, but you will find the complete code in the following link.

That’s it for today, I hope I will be posting more on these components, so stay tuned.

Source Codes: TelerikMVCGridCRUDDemo.zip (Sorry for the earlier inconvenience, just made it public, it is accessible now.)

Shout it
More Posts