lbrt.Net

Pinoy Software Engineer

Tag Archives: ASP.NET MVC

ASP.NET MVC: Creating Feed Wall

Here is a simple direct approach in creating a feed wall which is similar to the wall in facebook except for the omission of the posting of statuses. i’ve created this because i wanted to present my data from a feed in a wall like approach.

First is we need to create a control that will be the parent for our wall (e.g. EventFeedsControl).

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript" src="<%= Url.Content("../../Scripts/Views/Shared/pk.eventfeeds.js") %>"></script>
<div class="div-event-feeds">
    <a href="#" id="btnEventFeed" class="dark_link">
        <span class="ui-icon ui-icon-signal-diag float-left button_link"></span>
        <label>Event Feed</label>
    </a>
    <ul id="update" class="ui-unordered-list-bullet-less"></ul>
    <a href="#" class="button_link ui-state-default" id="loadOlder">
        <label>Load Older</label>
    </a>
</div>

Next is to create the javascripts used by the control, note that make sure to point the above sample code’s js source to the correct path you would be using.


//Bind events
$(document).ready(function () {
    $("#btnEventFeed").click(function () {
        loadNotes();
    });

    $("#loadOlder").click(function () {
        loadMore();
    });

    $("#loadOlder").hover(
	function(){ 
		$(this).addClass("ui-state-hover"); 
	},
	function(){ 
		$(this).removeClass("ui-state-hover"); 
	}
)
});

//load all notes or latest
function loadNotes() {
    $.ajax({
        type: "POST",
        url: 'UploadPhotos/GetNotes',
        data: {},
        cache: false,
        dataType: "json",
        success: function (data) {
            $("ul#update").prepend(data.Html);
            $("ul#update li").slideDown("slow");
        },
        error: function (XMLHttpRequest) {
            var errorMsg = XMLHttpRequest.statusText;
        },
        complete: function (jsonData) {
            //TODO:
        }
    });
};
//end loadNotes

//load all notes or latest
function loadMore() {
    //find oldest id displayed
    var Id = $("ul#update li:last");
    if (Id[0]) {
        Id = Id.attr("id");
    } else {
        Id = 0;
    }
    //ajax call
    $.ajax({
        type: "POST",
        url: 'UploadPhotos/LoadMore',
        data: { Id : Id},
        cache: false,
        dataType: "json",
        success: function (data) {
            $("ul#update").append(data.Html);
            $("ul#update li").slideDown("slow");
        },
        error: function (XMLHttpRequest) {
            var errorMsg = XMLHttpRequest.statusText;
        },
        complete: function (jsonData) {
            //TODO:
        }
    });
};
//end LoadMore

Next is to create the control which will house the feeds (e.g. EventFeedControl). In this sample i’ve simply used a string.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Web.ViewModel.UploadPhotosViewModel>>" %>
<% foreach(var item in Model){ %>   
<li id="<%= item.Test %>">
    <div>
        <%= item.Test %>     
    </div>
</li>
<% } %>

Next is the controller, helpers and viewmodel codes

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Web.Helper;
using Web.ViewModel;

namespace Web.Controllers
{
    public class UploadPhotosController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public JsonResult Upload(HttpPostedFileBase file)
        {
            return Json(new { });
        }

        public JsonResult GetNotes()
        {
            var events = new List<UploadPhotosViewModel>();
            events.Add( new UploadPhotosViewModel() { Test = Guid.NewGuid().ToString() });

            return Json(new
            {
                Html = this.RenderPartialView("EventFeedControl", events)
            }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult LoadMore(string Id)
        {
            var events = new List<UploadPhotosViewModel>();
            events.Add(new UploadPhotosViewModel() { Test = Guid.NewGuid().ToString() });

            return Json(new
            {
                Html = this.RenderPartialView("EventFeedControl", events)
            }, JsonRequestBehavior.AllowGet);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.ViewModel
{
    public class UploadPhotosViewModel
    {
        public string Test { get; set; }
    }
}

Helper class to render partial view in controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;

namespace Web.Helper
{
    public static class ControllerHelper
    {
        public static string RenderPartialView(this Controller controller, string viewName, object model)
        {
            if (string.IsNullOrEmpty(viewName))
                viewName = controller.ControllerContext.RouteData.GetRequiredString("action");

            controller.ViewData.Model = model;
            using (var sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
                var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }
    }
}

Now simply add this to your page and your done.

<% Html.RenderPartial("EventFeedsControl"); %>

Lastly, Note that this runs in a jqueryUI ready asp.net mvc project. Hope this helps! Thanks.

ASP.NET MVC : Multiple file uploads using Uploadify and JQueryUI ProgressBar

I was on a curiosity adventure when I did this one, I wondered whether I could imitate facebook.com multiple photo uploading using ASP.NET MVC. The results where good enough for me to post this on my blog :]. This article would discuss how to create a multiple photo uploader using Uploadify and QueryUI ProgressBar.

First is create an ASP.NET MVC project. Then attach the JQueryUI and Uploadify objects as required. You could download them here.(JQueryUI and Uploadify).

Next is attached the objects in the Site.Master for re usability as shown below:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
    <%--Uploadify and JQUeryUI--%>
    <link href="<%= Url.Content("~/Scripts/Uploadify/uploadify.css") %>" rel="stylesheet" type="text/css" />
    <link href="<%= Url.Content("~/Scripts/JQueryUI/css/ui-lightness/jquery-ui-1.8.16.custom.css") %>" rel="stylesheet" type="text/css" />

    <script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery-1.6.2.min.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/JQueryUI/js/jquery-ui-1.8.16.custom.min.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/Uploadify/swfobject.js") %>"></script>
    <script type="text/javascript" src="<%= Url.Content("~/Scripts/Uploadify/jquery.uploadify.v2.1.4.min.js") %>"></script>
     <%--End Uploadify and JQUeryUI--%>
    <asp:ContentPlaceHolder ID="JavaScriptContent" runat="server" />
</head>

Next is to create a js script for the home view, I’ve seperated the js from the view to allow separation of js code from the view.

$(document).ready(function () {
    $("#multipleFiles").uploadify({
        'uploader': '/Scripts/Uploadify/uploadify.swf',
        'script': '/Home/Upload',
        'fileDataName': 'file',
        'fileDesc': 'Web Image Files (.JPG, .GIF, .PNG)',
        'fileExt': '*.jpg;*.gif;*.png',
        'buttonText': 'Upload Photos',
        'multi': true,
        'sizeLimit': 1048576,
        'simUploadLimit': 1,
        'cancelImg': '/Scripts/Uploadify/cancel.png',
        'auto': true,
        'height': 30,
        'queueID': 'fileQueue',
        'onError': function (a, b, c, d) {
            if (d.status == 404)
               alert("Could not find upload script. Use a path relative to: " + "<?= getcwd() ?>");
            else if (d.type === "HTTP")
                alert("error " + d.type + ": " + d.status);
            //else if (d.type === "File Size")
            //   alert(c.name + " " + d.type + " Limit: " + Math.round(d.info / (1024 * 1024)) + "MB");
            //else
            //    alert("error " + d.type + ": " + d.text);
        },
        'onComplete': function (event, queueId, fileObj, response, data) {
            //Do Nothing Yet
            var result = $.parseJSON(response);
            $("#progressbar").progressbar("value", result.Percentage);
        },
        'onSelectOnce': function (event, data) {
            $.ajax({
                type: 'POST',
                url: '/Home/SetUploadCount',
                data: { TotalCount: data.filesSelected },
                dataType: 'json',
                success: function (data) {
                },
                error: function (XMLHttpRequest) {
                    var errorMsg = XMLHttpRequest.statusText;
                },
                complete: function (jsonData) {
                }
            });
        }
    });

    $("#progressbar").progressbar();
});  

Then we would re-factor the Home view as shown below, Notice that I’ve created the JavaScriptContent placeholder to hold js reference for page specific js scripts. Also note that you can adjust the visibility of the Uploadify queue by adding the display none style. Don’t forget to add the js you created above on the view.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="JavaScriptContent" runat="server">
    <script type="text/javascript" src="<%= Url.Content("../../Scripts/home.js") %>"></script>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div style="width: 50%;">
         <input type="file" id="multipleFiles" style="width: 50%;"/><div style="width: 77%;float:right" id="progressbar"></div>
    </div>
    <div id="fileQueue" ></div> <%--style="display:none"--%>
</asp:Content>

Now will add the controller actions used by the Uploadify, note that the method VariousQuality is commented out. I’ve just tested an Image quality compressing approach written here which I’ve used to analyze the effects of compression to an image. Also note that the “/Uploads/” path is missing in the sample solution.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing;

namespace UploadifyTest.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        private static float _uploadCount { get; set; }
        private static float _totalCount { get; set; }

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

        public JsonResult Upload(HttpPostedFileBase file)
        {
            var uploadFile = file;
            //do business logic here
            //Also try considering compressing the file -> VariousQuality(Image.FromStream(file.InputStream, true, true));

            var percentage = default(float);

            if (_totalCount > 0) 
            {
                _uploadCount += 1;
                percentage = (_uploadCount / _totalCount) * 100;
            }

            return Json(new
            {
                Percentage = percentage
            });
        }

        public JsonResult SetUploadCount(int TotalCount)
        {
            _totalCount = TotalCount;
            _uploadCount = 0;

            return Json(new
            {
                Count = TotalCount
            });
        }

        private void VariousQuality(Image original)
        {
            ImageCodecInfo jpgEncoder = null;
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == ImageFormat.Jpeg.Guid)
                {
                    jpgEncoder = codec;
                    break;
                }
            }
            
            if (jpgEncoder != null)
            {
                Encoder encoder = Encoder.Quality;
                EncoderParameters encoderParameters = new EncoderParameters(1);

                for (long quality = 10; quality <= 100; quality += 10)
                {
                    EncoderParameter encoderParameter = new EncoderParameter(encoder, quality);
                    encoderParameters.Param[0] = encoderParameter;

                    string fileOut = Path.Combine("/Uploads/", "quality_" + quality + ".jpeg");
                    FileStream ms = new FileStream(fileOut, FileMode.Create, FileAccess.Write);
                    original.Save(ms, jpgEncoder, encoderParameters);
                    ms.Flush();
                    ms.Close();
                }
            }
        }
    }
}

This should be the output:
Before Loading
While Loading
Loading Complete

If you want to download a sample project, below is a link. It is connected to skydrive so you may want to have a hotmail account. Project is created using VS2010.
https://skydrive.live.com/embedicon.aspx/Shared/UploadifyTest.zip?cid=34106e8cbacbd842&sc=documents

ASP.NET MVC, C#: Custom jqGrid object based column definitions

There are not much reference that I’ve seen with regards to customizing a jqGrid based on an object. This sample will allow users to create a customize jqGrid where the grid and column definitions are defined within the c# codebehind. The sample would require download of jqGrid version 4.1.2 and jquery-ui which is required for jqGrid 3.5 and above.

First step is to create a ASP.NET MVC Project – Let’s name it as StepByStepJQGridGuid

Then add the jqGrid and jquery-ui objects to your solution as shown below. jquery-ui objects are marked as blue.

Now we shall add references to this scripts in our page. As a standard, we would be adding the scripts references within the Site.Master, hence allowing the jqGrid scripts to be used by any page inheriting from the Site.Master. add the references within the header tag of the Site.Master page as shown below

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <%-- Add Code --%>
    <link href="../../Scripts/css/ui.jqgrid.css" rel="stylesheet" type="text/css" />
    <link href="../../Scripts/css/ui.multiselect.css" rel="stylesheet" type="text/css" />
    <link href="../../Scripts/css/jquery-ui-1.8.7.css" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.5.2.min.js" type="text/javascript"></script>
    <script src="../../Scripts/js/i18n/grid.locale-en.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>
     <%-- End Add Code --%>
</head>

Next is we would be injecting a jqGrid in the page. For this sample we would be modifying the index.aspx in the Home View as pre-created in a ASP.NET MVC application.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<StepByStepJQGridGuid.ViewModel.HomeViewModel>" %>
<%@ Import Namespace="StepByStepJQGridGuid.Helper" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%: Html.CreateJQGrid<StepByStepJQGridGuid.ViewModel.ProductViewModel>(Model.GridData)%>
</asp:Content>

As shown above we need a Html.CreateJQGrid helper to create the jqgrid, we also need to import the namespace of the helper class to the page (e.g. <%@ Import Namespace=”StepByStepJQGridGuid.Helper” %>). Below you’ll see that where using a grid model (GridViewModel<T>) which wraps the object that would define the grid column definitions(e.g. column width, alignment etc), the grid model itself defines the jqGrid main definitions (e.g. Id,Caption,Url,Grid Width etc).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using StepByStepJQGridGuid.ViewModel;

namespace StepByStepJQGridGuid.Helper
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString CreateJQGrid<T>(this HtmlHelper helper, GridViewModel<T> model)
        {
            var htmlBuilder = new StringBuilder();

            htmlBuilder.AppendFormat(@"<table id=""{0}"" class=""scroll"" cellpadding=""0"" cellspacing=""0""></table>", model.Id);
            htmlBuilder.AppendFormat(@"<div id=""{0}Pager"" class=""scroll"" style=""text-align:center;""></div>", model.Id);

            htmlBuilder.AppendFormat(@"<script type=""text/javascript"">");
            htmlBuilder.AppendFormat(@"$(function()");
            htmlBuilder.AppendFormat(@"{{");
            htmlBuilder.AppendFormat(@"$('#{0}').jqGrid({{", model.Id);
            htmlBuilder.AppendFormat(@"url: '{0}',", model.Url);
            htmlBuilder.AppendFormat(@"datatype: 'json',");
            htmlBuilder.AppendFormat(@"mtype: 'POST',");

            //Create Columns
            htmlBuilder.AppendFormat(@"colModel: [");
            foreach (var column in model.Columns)
            {
                htmlBuilder.AppendFormat(@"{{ name: '{0}', 
                                            index: '{0}', 
                                            width: {1}, 
                                            align: '{2}', 
                                            sortable: {3},
                                            hidden: {4}
                }},", column.Name
                    , column.Width > 0 ? column.Width : 80
                    , column.Align == null ? "left" : column.Align.ToLower()
                    , column.IsSortable.ToString().ToLower()
                    , column.IsHidden.ToString().ToLower()
                    );
            }
            htmlBuilder.AppendFormat(@"],");

            htmlBuilder.AppendFormat(@"pager: jQuery('#{0}Pager'),", model.Id);
            htmlBuilder.AppendFormat(@"rowNum: 5,");
            htmlBuilder.AppendFormat(@"rowList: [5, 10, 20, 50],");
            htmlBuilder.AppendFormat(@"viewrecords: true,");
            htmlBuilder.AppendFormat(@"imgpath: '/scripts/themes/coffee/images',");
            htmlBuilder.AppendFormat(@"caption: '{0}',", model.Caption);
            htmlBuilder.AppendFormat(@"width: {0}", model.Width);
            htmlBuilder.AppendFormat(@"}});");
            htmlBuilder.AppendFormat(@"}});");
            htmlBuilder.AppendFormat(@"</script>");


            return MvcHtmlString.Create(htmlBuilder.ToString());
        }
    }
}

Below shows the object represented within the jqGrid, it is designed to contain attributes (e.g. JQGridColumn) which would be used as the column definitions.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using StepByStepJQGridGuid.Utilities;

namespace StepByStepJQGridGuid.ViewModel
{
    public class ProductViewModel
    {
        [JQGridColumn(IsHidden = true)]
        public int ProductID { get; set; }
        [JQGridColumn(Width = 200, Align = "Center")]
        public string Name { get; set; }
        [JQGridColumn(Width = 50, Align = "Left")]
        public string ProductNumber { get; set; }
        [JQGridColumn(Width = 50, Align = "Right")]
        public decimal ListPrice { get; set; }
    }
}

Below is the attribute class, to learn more about attributes visit here.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace StepByStepJQGridGuid.Utilities
{
    [AttributeUsage(AttributeTargets.Property)]
    public class JQGridColumnAttribute : Attribute
    {
        public int Width { get; set; }
        public string Align { get; set; }
        public bool IsSortable { get; set; }
        public bool IsHidden { get; set; }
    }
}

Lastly, is the GridViewModel itself, the class uses reflection to retrieve the attributes from the object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using StepByStepJQGridGuid.Utilities;

namespace StepByStepJQGridGuid.ViewModel
{
    public class GridViewModel<T>
    {
        public GridViewModel()
        {
            this.Data = new List<T>();
            CreateColumns();
        }

        public string Id { get; set; }
        public string Url { get; set; }
        public string Caption { get; set; }
        public int Width { get; set; }
        public List<T> Data { get; set; }
        public IEnumerable<JQGridColumn> Columns { get; set; }

        private void CreateColumns()
        {
            var modelType = typeof(T);
            var columns = new List<JQGridColumn>();

            var properties = modelType.GetProperties();
            if (properties != null)
            {
                foreach (var property in properties)
                {
                    var column = new JQGridColumn();
                    column.Name = property.Name;

                    var attrs = Attribute.GetCustomAttributes(property);
                    foreach (var attr in attrs)
                    {
                        var a = attr as JQGridColumnAttribute;
                        if (a != null)
                        {
                            column.Width = a.Width;
                            column.IsHidden = a.IsHidden;
                            column.IsSortable = a.IsSortable;
                            column.Align = a.Align;
                        }
                    }
                    columns.Add(column);
                }
            }
            Columns = columns;
        }
    }
}

Now we will proceed with modifying the Homecontroller, as a standard, we would be making the Home View strongly typed to a ViewModel (e.g. HomeViewModel). Then we would be using the ViewModel to pass data from Controller to View.

Below is the ViewModel:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace StepByStepJQGridGuid.ViewModel
{
    public class HomeViewModel
    {
        public HomeViewModel() 
        {
            //Grid Definitions
            this.GridData = new GridViewModel<ProductViewModel>();
            this.GridData.Url = "/Home/GridData";
            this.GridData.Id = "ProductSearchGrid";
            this.GridData.Width = 800;
            this.GridData.Caption = "Products";
        }

        public GridViewModel<ProductViewModel> GridData { get; set; }
    }
}

Next we will modify the home controller as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using StepByStepJQGridGuid.ViewModel;

namespace StepByStepJQGridGuid.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public HomeController() 
        {
        }

        public ActionResult Index()
        {
            var model = new HomeViewModel();

            if(Session["cacheProducts"] == null)
                Session["cacheProducts"] = CreateDummyProducts(500).OrderBy(x => x.ProductID).ToList();
            
            return View(model);
        }

        public ActionResult GridData(string sidx, string sord, int page, int rows)
        {
            var cacheProducts = Session["cacheProducts"] as List<ProductViewModel>;
            var totalRecords = cacheProducts.Count;
            var products = cacheProducts.Skip((page - 1) * rows).Take(rows);

            var pageIndex = Convert.ToInt32(page) - 1;
            var pageSize = rows;
            var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize);
 
            var jsonData = new
            {
                total = totalPages, 
                page = page,
                records = totalRecords, 
                rows = (
                    from p in products
                    select new {
                        i = p.ProductID,
                        cell = new[] { p.ProductID.ToString(), p.Name, p.ProductNumber, p.ListPrice.ToString() }
                    }).ToArray()
            };

            return Json(jsonData);
        }

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

        private List<ProductViewModel> CreateDummyProducts(int count) 
        {
            var products = new List<ProductViewModel>();
            for (var i = 0; i < count; i++) 
            {
                var product = new ProductViewModel();
                product.ProductID = i + 1;
                product.Name = "Product" + i.ToString() + "_" + Guid.NewGuid().ToString();
                product.ProductNumber = "PN" + i.ToString();
                product.ListPrice = i * 21;
                products.Add(product);
            }
            return products;
        }
    }
}

To try it out yourself, download a copy of the test project below, Thanks!
https://skydrive.live.com/embedicon.aspx/Shared/StepByStepJQGridGuid.zip?cid=34106e8cbacbd842&sc=documents