Kendo MVC Extensions : Building a Forum Browser

Written by John DeVightJohn DeVight on 30 Dec 2011 14:29

rating: 0+x

Download Sample Code
The latest version of the sample code is maintained at: Kendo MVC Extensions for ASP.NET
Note: The sample code provided uses the Kendo UI Open Source (GPL-3.0) release. By downloading the sample code you agree to the Open GPL-3.0 License as defined at: http://www.opensource.org/licenses/GPL-3.0

Overview

Kendo UI is a fantastic new JavaScript framework for developing rich user interfaces leveraging the power of jQuery and HTML 5. I decided that I wanted to contribute to the community and make it easier for ASP.NET MVC developers to implement Kendo UI by creating MVC Extensions for it. I have created, and will continue to develop and support the Kendo MVC Extensions. However, to help ASP.NET MVC developers to be able to see how to use Kendo MVC Extensions, I've started an effort to create sample applications that demonstrate the use of Kendo MVC Extensions. The first sample application that I have created is a Forum Browser that allows the user to browse all the Kendo UI Forums.

ForumBrowser.png

Setting up the Kendo MVC Extensions

I created a new ASP.NET MVC 3 Web Application called "Kendo.Mvc.ForumBrowser" and selected the "Empty" project template. Once the project was created, I following the instructions for Getting Started and added a reference to the Kendo.Mvc.Extensions.dll. I downloaded the latest version of Kendo UI and copied the contents of the js folder to "~/Scripts/kendo"; and I copeid the contents of the scripts folder to "~/Content/kendo". In my Layout.cshtml page, I added a reference to the Kendo.Mvc.Extensions namespace at the top of the page, and the references to the kendo styles and kendo scripts in the <head> element:

@using Kendo.Mvc.Extensions;
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <!-- Kendo styles -->
    <link href="@Url.Content("~/Content/kendo/kendo.common.min.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/kendo/kendo.blueopal.min.css")" rel="stylesheet" type="text/css" />
 
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
 
    <!-- Kendo scripts -->
    <script src="@Url.Content("~/Scripts/kendo/jquery.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/kendo/kendo.all.min.js")" type="text/javascript"></script>
</head>

Before the body closing tag, I added the following:

@Html.Kendo().ScriptManager().Render()

Implementing the Splitter

Rendering the Splitter

I used the Splitter to divide the page into 3 panes. The left pane is used for navigation. The top right pane displays a list of the forum posts. The bottom right pane displays the details of the selected forum post. I put the Spllitter in the Layout.cshtml page. The Kendo MVC Extensions Splitter supports nested splitters. The Splitter is divided into 2 panes; one for Navigation and one for the nested splitter. The navigation pane renders a partial view for the navigation. The nested splitter contains 2 panes, one for the body, and one for a section called Details:

@{
    Html.Kendo().Splitter()
        .Name("splitter")
        .Panes(panes =>
        {
            panes.AddContentPane(pane => pane
                .Content(
                    @<text>
                        @Html.Partial("_Navigation")
                    </text>
                )
                .Size("300px")
            );
            panes.AddNestedSplitter(pane => pane
                .Name("nestedSplitter")
                .Orientation(OrientationType.Vertical)
                .Panes(nestedPanes =>
                {
                    nestedPanes.AddContentPane(nestedPane => nestedPane
                        .Content(
                            @<text>
                                @RenderBody()
                            </text>
                        )
                    );
                    nestedPanes.AddContentPane(nestedPane => nestedPane
                        .Content(
                            @<text>
                                @RenderSection("Details")
                            </text>
                        )
                        .Size("200px")
                    );
                })
            );
        })
        .Render();
}

Resizing the Splitter

I wanted the Splitter to fill the entire page. To do this, I wrote some JavaScript. I added a <script> section before the body closing tag with a jQuery.ready() function that calls Layout.init. I then create the Layout static class with a Layout.init function and a Layout.resize function. The Layout.resize function gets called very second to determine whether the splitter needs to be resized:

jQuery(document).ready(function () {
    Layout.init();
})
 
var Layout = {}
 
Layout._height = null;
 
Layout.init = function () {
    $($('#splitter').children('div.k-pane')[0]).css('overflow-x', 'hidden');
    setInterval('Layout.resize()', 1000);
}
 
Layout.resize = function () {
    var height = $(window).height();
    if (height != Layout._height) {
        Layout._height = height;
        var splitter = $('#splitter');
        splitter.height(height - 25);
        splitter.resize();
 
        var nestedSplitter = $('#nestedSplitter');
        nestedSplitter.height(height - 27);
        nestedSplitter.resize();
    }
}

Implementing the Navigation Partial View

The Kendo UI Forums are divided into 3 sections, Kendo UI Framework, Kendo UI Web and Kendo UI Integration. To display the 3 sections with the list of forums for each section, I used the Kendo MVC Extensions PanelBar. I created a partial view called _Navigation.cshtml. The partial view is rendered by the Layout.cshtml page in the Splitter using the following: @Html.Partial("_Navigation").

Rendering the PanelBar

In the PanelBar, I create 3 panels. Within each panel, I create an unordered list of list items that have an anchor tag that, when clicked, calls the Navigation.displayForum function:

@{
    Html.Kendo().PanelBar()
        .Name("navigationPanelBar")
        .Width(298)
        .Panels(panels =>
        {
            panels.Add(panel => panel
                .Text("Kendo UI Framework")
                .Content(
                    @<text>
                        <ul>
                            <li><a href="#" onclick="Navigation.displayForum({id:'699'})">Data Source</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'701'})">Drag and Drop</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'760'})">Globalization</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'700'})">Templates</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'759'})">Validation</a></li>
                        </ul>
                    </text>
                )
                .Expanded(true)
            );
            panels.Add(panel => panel
                .Text("Kendo UI Web")
                .Content(
                    @<text>
                        <ul>
                            <li><a href="#" onclick="Navigation.displayForum({id:'720'})">General Discussions</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'703'})">AutoComplete</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'729'})">Calendar</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'704'})">Chart</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'705'})">ComboBox</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'730'})">Date/Time Pickers</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'706'})">DropDownList</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'707'})">Grid</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'708'})">Menu</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'757'})">NumericTextBox</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'709'})">PanelBar</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'710'})">Slider</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'722'})">Splitter</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'712'})">TabStrip</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'713'})">TreeView</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'714'})">Upload</a></li>
                            <li><a href="#" onclick="Navigation.displayForum({id:'723'})">Window</a></li>
                        </ul>
                    </text>
                )
            );
            panels.Add(panel => panel
                .Text("Kendo UI Integration")
                .Content(
                    @<text>
                        <ul>
                            <li><a href="#" onclick="Navigation.displayForum({id:'721'})">Integration with other JS libraries</a></li>
                        </ul>
                    </text>
                )
            );
        })
        .Render();
}

Displaying the Forum List

When a forum is selected, the Navigation.displayForum function is called. The Navigation.displayForum gets a reference to the Kendo UI Grid and updates the grid's dataSource to request the forum items for the selected forum:

var Navigation = {}
 
Navigation.displayForum = function (e) {
    Index.displayItem();
    var rssItemGrid = $('#RssItemGrid').data('kendoGrid');
    rssItemGrid.dataSource.transport.options.read.url = '/Home/GetItems/' + e.id;
    rssItemGrid.dataSource.read();
}

Implementing the List of Forum Items

To display a list of forum items, I implemented the Kendo MVC Extensions Grid in Index.cshtml. The grid uses an Ajax dataSource to populate the grid with a list of RssItem objects. The a row is selected, the OnChange client event is raised that calls the Index.RssItemGrdi_onChange event handler:

@{
    Html.Kendo().Grid<Kendo.Mvc.ForumBrowser.Models.RssItem>()
        .Name("RssItemGrid")
        .Columns(columns =>
        {
            columns.Add(model => model.Title);
            columns.Add(model => model.Author).Width(250);
            columns.Add(model => model.PublicationDate).Width(250);
        })
        .DataSource(dataSource => dataSource.Ajax(ajax => ajax.Select("GetItems", "Home")))
        .Selectable(Kendo.Mvc.Extensions.GridSelectableType.Row)
        .ClientEvents(events => events.OnChange("Index.RssItemGrid_onChange"))
        .Render();
}

Displaying the Details for the Selected Forum Item

The Index.RssItemGrid_onChange function gets the selected item from the grid and then calls the Index.displayItem to display the item details. The item details is rendered in the Details section:

@section Details {
    <div class='k-widget k-alt' style='padding: 5px 5px 5px 5px;visibility:hidden;'>
        <a id='url'></a>
    </div>
    <div id="description" style='padding: 5px 5px 5px 5px;'>
    </div>
}

Here is the JavaScript code:

var Index = {};
 
Index.RssItemGrid_onChange = function (e) {
    var selected = $.map(this.select(), function (item) {
        return $(item).text();
    });
    var rssItem = $('#RssItemGrid').data('kendoGrid').dataSource._data[this.select().index()];
    Index.displayItem(rssItem);
}
 
Index.displayItem = function (rssItem) {
    if (rssItem != undefined) {
        $('#url').attr('href', rssItem.Link).text(rssItem.Link).closest('div').css('visibility', 'visible');
        $('#description').html(rssItem.Description);
    } else {
        $('#url').closest('div').css('visibility', 'hidden');
        $('#description').html('&nbsp;');
    }
}

Implementing the Controller

The Controller has an Index Controller Action and a GetItems Controller Action. Index displays the Index.cshtml page. GetItems gets the forum items from the appropriate Kendo UI Forum and returns the forum items:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Kendo.Mvc.ForumBrowser.Models;
 
namespace Kendo.Mvc.ForumBrowser.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
 
        public JsonResult GetItems(string id)
        {
            IList<RssItem> items = null;
 
            if (id != null)
            {
                RssFeed rssFeed = RssReader.Read(string.Format("http://www.kendoui.com/forums/rss.aspx?forumThreadId={0}&rssType=1", id));
                items = rssFeed.Channel.Items;
            }
            else
            {
                items = new List<RssItem>();
            }
 
            return Json(items, JsonRequestBehavior.AllowGet);
        }
    }
}

Implementing the RssReader

The RssReader class uses the WebClient class to retrieve the forum rss feed. I used an XmlSerializer to deserialize the XML into a RssFeed object. Here is the RssReader:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Xml.Serialization;
 
namespace Kendo.Mvc.ForumBrowser.Models
{
    public static class RssReader
    {
        public static RssFeed Read(string url)
        {
            RssFeed rssFeed = null;
 
            WebClient myWebClient = new WebClient();
            byte[] rssBuffer = myWebClient.DownloadData(url);
            string rssText = Encoding.ASCII.GetString(rssBuffer);
 
            using (TextReader s = new StringReader(rssText))
            {
                var deserializer = new XmlSerializer(typeof(RssFeed));
                rssFeed = deserializer.Deserialize(s) as RssFeed;
            }
            return rssFeed;
        }
    }
}

Here is the RssFeed:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Serialization;
 
namespace Kendo.Mvc.ForumBrowser.Models
{
    [XmlRoot(ElementName = "rss")]
    public class RssFeed
    {
        [XmlElement(ElementName = "channel")]
        public RssChannel Channel { get; set; }
    }
 
    public class RssChannel
    {
        [XmlElement(ElementName = "title")]
        public string Title { get; set; }
 
        [XmlElement(ElementName = "link")]
        public string Link { get; set; }
 
        [XmlElement(ElementName = "lastBuildDate")]
        public string PublicationDate { get; set; }
 
        [XmlElement(ElementName = "item")]
        public List<RssItem> Items { get; set; }
    }
 
    public class RssItem
    {
        [XmlElement(ElementName = "guid")]
        public string Guid { get; set; }
 
        [XmlElement(ElementName = "title")]
        public string Title { get; set; }
 
        [XmlElement(ElementName = "creator", Namespace = "http://purl.org/dc/elements/1.1/")]
        public string Author { get; set; }
 
        [XmlElement(ElementName = "link")]
        public string Link { get; set; }
 
        [XmlElement(ElementName = "pubDate")]
        public string PublicationDate { get; set; }
 
        [XmlElement(ElementName = "description")]
        public string Description { get; set; }
    }
}

Conclusion

The Kendo MVC Extensions made it easy to create an ASP.NET MVC application using Kendo UI without having to remember all the syntax for Kendo UI and providing an API that is similar to the Telerik Extensions for ASP.NET MVC API.

References

Comments

Add a New Comment
or Sign in as Wikidot user
(will not be published)
- +
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License