Tag Archive | MVC

Kendo UI MVC Grid Extention

Kendo UI MVC Grid Extention

Didn’t like how the MVC Destroy() method worked so I wired up my own. This is a simple function to help you get the row’s data when you fire off the custom event.

Essential “e” is the event that is fired from the custom command event in your Kendo UI grid. It takes the target and turns it into a JQuery object. It grabs the uid of the row and then matches it with the uid in the data. Its very quick and works nicely. Sure there are other ways to skin this cat but this seemed to be very smooth. Seems to me there should be a JQuery data object for the row but all I saw was the uid hence this function below. Anyway thought I’d share.

   function getRowData(e) {
        var target, grid, uid, row, data;
        target = $(e.target);
        uid = target.parent().parent().data().uid;      
        grid = $('#Grid').data('kendoGrid');        
        row = grid._data;
        for (var i = 0; i < row.length; i++) {
            if (row[i].uid == uid) {
                data = row[i];
            }
        }
        return data;
    }

What you’ll get is your data for the row as an object so you can simple do:
// note below assumes you have a custom column command that on .click() is attached to the “deleteRow” function “e” is the event that the click event will pass in.

function deleteRow(e){
	var data = getRowData(e);
	var name = data.full_name // where full_name is a column in your grid. 
	alert('Are you sure you want to delete ' + name);
	// TODO: wire up your delete event passing in an id or something
	$.post('/your/path', {}, function (result) {
		if(result.deleted){
			// load your UI or whatever
		}
	});
} 

The getRowData function you can copy/paste but the above “deleteRow” is just an example you’ll need to modify to your needs.

Jquery Live Image Crop

Live Image Cropping

I’m sure by now you’ve seen similar tools. Nonetheless I wanted to post this demo for those that haven’t. This demo leverages Jcrop. I won’t get into the details here as their documentation is self explanatory. What I will do is cover what you can do with it after you crop the image. That is where some of the real magic shows up. Cropping itself is rather simplistic. Demo Here

Crop demo screenshot

Live JCrop Demo

Init JCrop


function initJcrop()//{{{
{
// where target is the target image.
$('#target').Jcrop({
onRelease: releaseCheck, // checks for release
onChange: getCoords, // gets the coords and updates the inputs.
onSelect: getCoords // same as above just a different callback.
}, function () {

jcrop_api = this;
jcrop_api.animateTo([259, 81, 469, 304]); // create the corp box at these coords (x, y, x2, y2)

// Set the default checkboxes
$('#resizeable').attr('checked', 'checked'); // whether or not you can resize crop.
$('#aspect').attr('checked', false); // should it be forced to a specific ratio.

});
};

Force Aspect Ratio example trigger using JQuery on checkbox

This is just one example but setting up these options are all very similar


$('#aspect').change(function (e) {
jcrop_api.setOptions(this.checked ?
{ aspectRatio: 4 / 3} : { aspectRatio: 0 });
jcrop_api.focus();
});

Saving the Crop

Here were are making a simple post passing a Json representation of the coords, then await response from server.


$.post(postPath, jData, function (result) {
if (result.saved) {
// here I'm check if in the json result if "saved" is true.
// if yes DO SOMETHING...
} else {
// if not show an error of some sort.
}
});

In the demo after the crop is saved, I simply update the path to the image, the newly cropped image that is.
You’ll notice a “view crop” button that shows it. This is all just for example. Normally when the dialog opens that you’ll see
you might have an input for new filename or perhaps save location or other criteria you want saved to your database. In
fact you don’t need the dialog at all but since this is a static representation/demo I needed somewhere to display the result
and didn’t want to leave the page.

Saving to File System

Unfortunately this is beyond the scope of this article. Mainly because I have no idea what platform you’re one so its pretty tough to give an example. What I can tell you is
that its actually rather trivial for the most part. For example in C# you might do something like this:


var img = Image.FromFile(imgPath, true); // get the image you were working on from file, you could also do this from stream.
Image newImg = new Bitmap(img); // now we have a new image to work with.

var netImage = NetImage.Crop(newImg, w, h, x, y); // here we are taking the coords from JCrop passed in our Json and cropping the image.
// note the above is a custom class that crops images in various ways.
// above result from our custom class is a System.Drawing.Image. we can now save it or do what we wish.

If you’re using say NodeJs(which is amazing stuff btw) or Rails you can investigate Node-Canvas or perhaps ImageMagick.

Conclusion

It really is rather simplistic to crop images. It really is up to you and your design how you want to go about it. What options or restrictions you want. Perhaps you’re wanting to keep each image cropped to a specific ratio or size. Live for profile photos that should only be say 100 x 100. All very easily obtainable.

This is not meant to be a complete start to finish tut. It is meant to point you in the right direction. You can also expand the source on the demo. As always you can send comments and questions and I’ll help if able. You can also hire us for your next project!

MVC Paging and Sorting

If you’re new to MVC this is undoubtably something you’ll face at some point. You’ve already been searching Google and found a few examples, heard about MVCContribWebGrid and Telerik. All of which are great solutions. However you want the manual control, don’t want to use tables or you just like to stay up late and bang keys on the keyboard. If that is the case this should at the very least get you on the right track. As with anything I post feel free to critique. It can only help. There are always alternate ways to skin a cat. This is no exception. Be advised as often with code I post I’ve significantly scaled this down to make it easy to follow striping out the application specific stuff.

So let’s get to it…

The first thing you’ll need to do is create a class that will handle your paging and sorting. This was expanded on from Scott Gu’s PaginatedList.cs class which didn’t support sorting but we’ll get to that.

PaginatedList.cs  (abbreviated)

Declare a few properties:

public int PageIndex { get; private set; } // Index of your current page.
public string SortExpression { get; private set; } // The expression to sort on ex: "FullName DESC".
public string SortColumn { get; private set; } // The column you are sorting on ex: "FullName".
public string SortDirection { get; private set; } // The direction to sort ex: "DESC" or "ASC".
public int PageDisplayIndex { get; private set; } // This is the page number to display. Show 1-5 instead of 0-4.
public int PageSize { get; private set; } // The amount of records per page.
public int TotalCount { get; private set; } // The total count of records.
public int TotalPages { get; private set; } // The total number of pages. TotalCount / PageSize
public int PagerSize { get; private set; } // The size of the pager. How many page numbers to show.
public int PagerStart { get; private set; } // The starting page in the pager numbers.
public int PagerEnd { get; private set; } // The ending page of the pager numbers.
public List<int> Pages { get; private set; } // The list of pager page numbers to display.

Let’s create the paginated list by passing a few variables from our controller (we’ll get to that shortly) .

public PaginatedList(IQueryable<T> source, string sortExpression, int pageIndex, int pageSize, int pagerSize)
{
    //Populate variables.
    PageIndex = pageIndex;
    SortExpression = sortExpression;
    PageDisplayIndex = pageIndex;
    PageSize = pageSize;
    PagerSize = pagerSize; 

    //Store the current Sort Column and Direction.
    //There should always be a value but just in case. This is for providing the current sort expression in our Model
    if (sortExpression != null && !string.IsNullOrEmpty(sortExpression))
    {
        string[] curSort = sortExpression.Split(' '); // Split so we can get the col and direction sep.
        SortColumn = curSort[0];

        //This could be null so let's check first.
        if (curSort.Length > 1)
        {
            SortDirection = curSort[1];
        }

    }       

    //Get the record count and the total pages.
    TotalCount = source.Count();
    TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);

    //Build out the list of pages available.
    BuildPagerPages(); // See below for this method. Creates the page numbers for the pager.

    //Finally Sort the data and select the page size.
    this.AddRange(source.OrderBy(sortExpression)
        .Skip((PageIndex - 1) * PageSize)
        .Take(PageSize));
}

Now we need a simple method to build out a list of the page numbers to display. Down load source, there are a couple other properties like “HasNextPage” or “HasPreviousPage” that you’ll need to check in your view or Html Helper to verify the link should be displayed.

/// <summary>
/// Creates list of page numbers for pager.
/// </summary>
private void BuildPagerPages()
{
    PagerStart = Math.Max(1, (PageIndex) - PagerSize / 2); // Start page of pager.
    PagerEnd = Math.Min(TotalPages, PageIndex + (PagerSize / 2)); // End page of pager.

    //Iterate through the total pages to make page selectors.
    Pages = new List<int>();
    for (var i = PagerStart; i <= PagerEnd; i++)
    {
        Pages.Add(i); // Add page number to list.
    }
}

DynamicOrderBy.cs (abbreviated)

Grabbed this in part from StackOverflow and Adam Anderson’s blog. It’s a great piece of code. I won’t bore you It is included in the source below but checkout the links for further details. Essentially though this class allows us to do our Linq OrderBy with ease.

Let’s Wire Up Our Controller

Probably the easiest part of the whole process. All we are doing here is grabbing some values from our route grabbing our data from our repository or where ever and then create our new instance of our PaginatedList class.

public ActionResult Index(string sort, int? page )
{
    // Define the page and pager defaults.
    const int pageSize = 10;
    const int pagerSize = 10;

    // You could also set this as an optional value
    // and set the default that way too.
    if (sort == null || string.IsNullOrEmpty(sort))
    {
        sort = "FullName ASC";
    }

    var accounts = repository.GetList(); // This is simply getting your data. That’s up to you, in this case a repository.
    // Note the page number default there. It is 1 instead of 0. This gets changed in our PaginatedList
    // There are a couple ways you could do this but the point is to have page numbers 1-5 instead of 0-5.
    var paginatedAccounts = new PaginatedList<Account>(accounts, sort, page ?? 1, pageSize, pagerSize);          

    return View("Index", paginatedAccounts);
}

Setup Routing

The simplest way to do this is of course in your Global.asax. Or you may have some other custom mechanisim to handle your routes as I do. But for this will show you the Global.asax method for brevity. This is very straight forward. Simply create a new route like below. Essentially you’d have something like “Clients/{sort}/{page}”.

routes.MapRoute(
    "ClientList", // Route name
    "Clients/{sort}/{page}", // URL with parameters
    new { controller = "Account", action = "Index" } // Parameter defaults
);

Last Piece of Puzzle the View and Helper

Updating the view is very easy. The below is abbreviated showing only the Previous button but you could easily add Next, First and Last if you wanted. So there are a couple ways you could do this. You could use JQuery and build out the url or you can use a Helper. There may be reasons why you might use one over the other but I’m going to show the Helper method. Remember in our PaginatedList class we had some properties called SortColumn, SortExpression and SortDirection right? Well now we need them. You could just pass the SortExpression only if you wanted and then split it up in your class however I chose to expose the expression and also the col and exp separately also. So in our link we simply need to pass in the current or last sort details plus the newly selected details. This is simply for one reason and that is if the column name passed is the same as the previous we need simply to sort the opposite way. If the column is a different one or no column exists we just sort ascending. Simple right. May see complicated but isn’t just read the comments it all makes sense.

public static MvcHtmlString SortActionLink(this HtmlHelper helper, string text, string actionName,
    string controller, string curColumn, string curDirection, string tag, object routeValues, object htmlAttributes)
{
    string sortResult, sortCol, sortExpression, sortDir, tagType;
    tagType = "a";
    sortExpression = "{0} {1}"; //Just because I don't want to write + ' ' +...ohh wait I just did. LOL.
    RouteValueDictionary routeDict = new RouteValueDictionary(routeValues);

    //Check for null. If this is the first time the page is loaded
    // if so we don't need to update the sort values.
    if (routeDict["sort"] != null)
    {
        sortCol = routeDict["sort"].ToString();
        if (string.Equals(curColumn, sortCol, StringComparison.CurrentCultureIgnoreCase))
        {
            //We're sorting the same column
            //Check the direction and then supply the opposite.
            switch (curDirection)
            {
                case "ASC":
                    sortDir = "DESC";
                    break;
                case "DESC":
                    sortDir = "ASC";
                    break;
                default:
                    sortDir = "ASC";
                    break;
            }
            sortResult = string.Format(sortExpression, sortCol, sortDir);
        }
        else
        {
            //This is a new column pass the default.
            //No need to append Sort Direction.
            //If no sort direction is supplied ASC is assumed.
            sortResult = sortCol;
        }

        //Update the Route data.
        routeDict["sort"] = sortResult;
        //helper.ViewContext.RequestContext.RouteData.Values["sort"] = sortResult;
    }

    var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
    var url = urlHelper.Action(actionName, controller, routeDict);

    if (tag != null)
        tagType = tag;

    TagBuilder build = new TagBuilder(tagType);
    build.MergeAttribute("href", url);
    build.InnerHtml = text;
    build.MergeAttributes(new RouteValueDictionary(htmlAttributes));
    return MvcHtmlString.Create(build.ToString(TagRenderMode.Normal));
}

…and the razor markup to make it all work. You are passing in the name for the link, then the action, then the controller. Then we pass in our current values of sort column and direction, then our Route Values which we modified based on the current sort values and finally any html attributes if needed.

@Html.SortActionLink("Full Name", "Index", "Client", Model.SortColumn, Model.SortDirection, new { sort = "FullName", page = Model.PageDisplayIndex }, null)

Wrap Up

To wrap up in your source you will notice that the First, Next etc links can be shown or hidden by a simple if statement that checks the HasPreviousPage bool value that we exposed in our PaginatedList class. Again see source for how to do this, if you don’t already.

Hope this helps you out,

C

Download Source

mvc_paging_sorting.rar