Now we really start cranking out the classes.
Create a new folder in your solution explorer called Lib and in this folder create another one called Helpers.
The Helpers folder will have all our neat formatting as well as the presentation widgets. One of the most useful of these is called the PagedList. You’ve probably seen many different variations of this class, but I’m using here the bare essentials needed to get the job done.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace System.Web.Mvc { public interface IPagedList { int TotalCount { get; set; } int PageCount { get; set; } int PageSize { get; set; } int PageIndex { get; set; } bool HasPreviousPage { get; } bool HasNextPage { get; } } public class PagedList<T> : List<T>, IPagedList, IEnumerable { public int TotalCount { get; set; } public int PageCount { get; set; } public int PageSize { get; set; } public int PageIndex { get; set; } public bool HasPreviousPage { get { return PageIndex > 1; } } public bool HasNextPage { get { return (PageIndex * PageSize) <= TotalCount - 1; } } public bool IsPreviousPage { get { return PageIndex <= 0; } } public bool IsLastPage { get { return PageIndex >= (PageCount - 1); } } public PagedList(IEnumerable<T> source, int index, int pageSize) { index = ((index - 1) < 0) ? 0 : (index - 1); this.TotalCount = source.Count(); this.PageSize = pageSize; this.PageIndex = index + 1; if (this.TotalCount > 0) { if (index == 0) this.AddRange(source.Take(pageSize).ToList()); else this.AddRange(source.Skip(index * pageSize).Take(pageSize).ToList()); this.PageCount = (int)Math.Ceiling(this.TotalCount / (double)this.PageSize); } else this.PageCount = 0; } public PagedList(List<T> source, int index, int pageSize) { index = ((index - 1) < 0) ? 0 : (index - 1); this.TotalCount = source.Count(); this.PageSize = pageSize; this.PageIndex = index + 1; if (this.TotalCount > 0) { if (index == 0) this.AddRange(source.Take(pageSize).ToList()); else this.AddRange(source.Skip(index * pageSize).Take(pageSize).ToList()); this.PageCount = (int)Math.Ceiling(this.TotalCount / (double)this.PageSize); } else this.PageCount = 0; } } public static class Pagination { public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize) { return new PagedList<T>(source, index, pageSize); } public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index) { return new PagedList<T>(source, index, 10); } } }
Over the last few months, this class in particular has saved me quite a lot of work and repetition.
We now add another helper class with little niceties that make our V in our MVC more pleasant. You may notice that I reused my friendly dates function from some time back. I just loathe the idea of using unformatted dates with plain backslashes.
using System; using System.Web; using System.Web.Mvc; using System.Text; using SimpleCMS.Helpers; namespace System.Web.Mvc.Html { public static class MvcHelpers { /// <summary> /// Displays the last edited date /// </summary> public static string EditedDate(this HtmlHelper helper, Object ddt, Object ddm) { if ((DateTime)ddt != (DateTime)ddm) return "Edited " + MvcHelpers.FriendlyDate(helper, ddm) + " | "; return ""; } /// <summary> /// Direct link to the comments section of each page /// </summary> public static MvcHtmlString CommentsLink(this HtmlHelper helper, int pageid, int? count) { if (count.HasValue && count.Value > 0) return helper.ActionLink(count.Value + " Comments", "Read", "Pages", null, null, "comments", new { id = pageid }, null); return helper.ActionLink("No Comments", "Read", "Pages", null, null, "comments", new { id = pageid }, null); } /// <summary> /// Formatted link to the author's profile page /// </summary> public static MvcHtmlString AuthorLink(this HtmlHelper helper, string a, int? id) { if (id.HasValue && id.Value > 0) return helper.ActionLink(a, "Profile", "Users", new { id = id.Value }, new { }); return helper.ActionLink(a, "Profile", "Users", new { id = "Anonymous" }, new { }); } /// <summary> /// Title descriptions for links /// </summary> public static string AltTitle(this HtmlHelper helper, string t, string d) { return (string.IsNullOrEmpty(d)) ? t : d; } /// <summary> /// Evaluates the default value and provides checked checkbox /// </summary> public static string CheckBoxChecked(this HtmlHelper helper, bool? val, bool d) { if (SimpleCMS.Helpers.Util.DefaultBool(val, d)) return "checked=\"checked\" "; return ""; } /// <summary> /// Shows the long Sunday, October 10, 2010 date format. /// </summary> public static string FormalDate(this HtmlHelper helper, Object ddt) { DateTime dt = (DateTime)ddt; return dt.ToLongDateString(); } /// <summary> /// Creates a formatted pager to navigate a PagedList /// </summary> /// <param name="helper">This required to tie into the Html Helpers</param> /// <param name="plist">A list of "stuff" to page.</param> /// <param name="url">Current url without the page. E.G. /Pages/Read/1/###</param> /// <returns>Formatted pager</returns> public static string Pager(this HtmlHelper helper, IPagedList plist, string url) { StringBuilder sb = new StringBuilder(); // This is the convention : // <h5>Page : <strong class="curr">1</strong> . <a href="">2</a></h5> // No need to show pager if we have less than one page if (plist.PageCount > plist.PageSize) { // Begin the pager sb.Append("<h5> Page : "); if (plist.HasPreviousPage) sb.Append("<a href=\"" + url.Replace("###", (plist.PageIndex - 1).ToString()) + "\"><</a> "); // Adding the rest of the links for (int i = 1; i < plist.PageCount + 1; i++) { // Current page doesn't need a link if (i == plist.PageIndex) { sb.Append("<strong class=\"curr\">"); sb.Append((i).ToString()); sb.Append("</strong> . "); } else { sb.Append("<a href=\""); sb.Append(url.Replace("###", (i).ToString())); sb.Append("\">" + (i) + "</a> . "); } } if (plist.HasNextPage) sb.Append("<a href=\"" + url.Replace("###", (plist.PageIndex + 1).ToString()) + "\">></a>"); sb.Append("</h5>"); } return sb.ToString(); } /// <summary> /// A more user friendly X minutes/hours/days(etc..) ago date format /// </summary> public static string FriendlyDate(this HtmlHelper helper, Object ddt) { // Get all the variables set DateTime dt = (DateTime)ddt; DateTime now = DateTime.Now; StringBuilder sb = new StringBuilder(); TimeSpan elapsed; int days = 0; int hours = 0; int minutes = 0; int years = 0; // If the given date is *before* now if (now > dt) elapsed = now.Subtract(dt); // If the given date is *after* now else elapsed = dt.Subtract(now); // Leap years are always fun... // I don't care too much about accuracy after the current year if (DateTime.IsLeapYear(dt.Year)) years = (int)(elapsed.Days / 366); else years = (int)(elapsed.Days / 365); days = (int)elapsed.TotalDays; hours = (int)elapsed.TotalHours; minutes = (int)elapsed.TotalMinutes; // There aren't any hours days or years yet if (years <= 0 && days <= 0 && hours <= 0) { if (minutes > 0) { sb.Append(minutes.ToString("0") + " minute"); if (minutes > 1) sb.Append("s"); } else { // We're under a minute sb.Append("A few seconds"); } } else { // If we have years, then days and hours don't really mean much if (years > 0) { if (years < 10) { sb.Append(years.ToString("0") + " year"); if (years > 1) sb.Append("s"); } // ... Hey, ASP lasted quite a while. You just never know. else if (years > 10 && years < 30) { int dec = years / 10; sb.Append(dec + " decade"); if (dec > 1) sb.Append("s"); } else { sb.Append("A very long time"); } } else { // We have days and hours if (days > 0) { sb.Append(days.ToString("0") + " day"); if (days > 1) sb.Append("s"); } if (hours > 0) { sb.Append(" " + hours.ToString("0") + " hour"); if (hours > 1) sb.Append("s"); } } } if (now > dt) // Past time sb.Append(" ago"); else // Future time sb.Append(" from now"); return sb.ToString(); } } }
Now the niceties are complete, we can move on to adding our PageView.
A lot of tutorials overlook the fact that you’re actually sending a model to the page. Hence the Model, View, Controller. Well, it doesn’t make much sense to pass a whole lot of information through the ViewState array alone.
So we’re going to make a specific PageView class that does exactly that. It’s a model of the requested view that is strongly typed and easy to access.
Back in your solution explorer, right click the Models folder and select Add > New Item. We’re going to add a new code file which will be our PageView class.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using SimpleCMS.Helpers; namespace SimpleCMS.Models { public class PageView { /// <summary> /// Current viewing page /// </summary> public ContentPage Page { get; set; } /// <summary> /// Paged index list of pages or sub pages. /// </summary> public PagedList<ContentPage> Pages { get; set; } /// <summary> /// Paged list of comments in the current page /// </summary> public PagedList<ContentComment> Comments { get; set; } /// <summary> /// This is for breadcrumb navigation use /// </summary> public Dictionary<int, string> Parents { get; set; } /// <summary> /// Constructor /// </summary> public PageView() { } /// <summary> /// Plain view of a content page /// </summary> /// <param name="_page">Current content page</param> public PageView(ContentPage _page) { Page = _page; } /// <summary> /// Plain view with comments /// </summary> /// <param name="_page">Current content page</param> /// <param name="_comments">List of commments in the current page</param> public PageView(ContentPage _page, PagedList<ContentComment> _comments) { Page = _page; Comments = _comments; } /// <summary> /// If no comments or sub pages are present, then this is an index view /// </summary> /// <param name="_pages">List of pages in current index</param> public PageView(PagedList<ContentPage> _pages) { Pages = _pages; } /// <summary> /// Full content page view with comments and sub pages /// </summary> /// <param name="_page">Current content page</param> /// <param name="_pages">Paged list of sub pages</param> /// <param name="_comments">Paged list of comments</param> public PageView(ContentPage _page, PagedList<ContentPage> _pages, PagedList<ContentComment> _comments) { Page = _page; Pages = _pages; Comments = _comments; } /// <summary> /// Checks if this view has a page to display. If not, then it's probably a main index. /// </summary> public bool HasPage { get { return (Page != null); } } } }
We’ll get to the breadcrumbs a little later. You could pretty much call this a factory pattern, but I don’t believe in absolutes so we’ll just call it a model.
Pingback: Simple CMS with Linq to SQL part II « This page intentionally left ugly
Pingback: Forum Tables « This page intentionally left ugly
possibly rudy monty environmental stamps http://www.nz-hotrod.com/vbulletin/member.php?426506-Keropar complaining interests slide inches sergio
Did Michigan ever have Wolverines http://vibrafusionlab.com/27/when-did-the-broncos-change-their-uniforms What is the acceptance rate for UT Austin
quality calendar puts http://wzjr.beiww.com/home.php?mod=space&uid=962139&do=profile&from=space caravan philippe religion ask
p skies juicy russ http://jamesschneiderrailways.com/mybb/member.php?action=profile&uid=7147 organic matt sweden kitty
richard comments seventh fans powers http://biutiu.com/bbs/home.php?mod=space&uid=149941 over attempted youre yourselves specialist
crude schmidt spice cigarettes http://test.i4.ru/index.php?action=profile;u=162 psychic certificate weekly spite