Noah Blumenthal's Blog

October 28, 2008

MvcContrib Grid with custom layout

Filed under: Uncategorized — noahblu @ 5:22 pm
Tags: , ,

I’m using the Grid Html Helper from the MvcContrib project (www.mvccontrib.org).  The default grid can create automatic pagination links at the bottom of the grid, which is a pretty nice feature.  However, in order to accomodate our design, I needed some more functionality:

  1. I needed paging at the top of the grid too
  2. I needed to add some custom markup surrounding the grid to make it look right

Luckily, this wasn’t too hard.  I started by creating a class that inherits from MvcContrib.UI.Html.Grid.Grid<T>.  Something like this:

public class AdminMerchantGrid<T> : MvcContrib.UI.Html.Grid.Grid<T> where T : class

In order to render pagination at the top I had to override the RenderGridStart like so:

        protected override void RenderGridStart()
        {
            MvcContrib.Pagination.IPagination items = base.Items as MvcContrib.Pagination.IPagination;
            RenderPagination(items, true);
            base.RenderGridStart();
        }

And I overrode the RenderGridEnd too:

        protected override void RenderGridEnd(bool isEmpty)
        {
            base.RenderText("</table>");
            MvcContrib.Pagination.IPagination items = base.Items as MvcContrib.Pagination.IPagination;
            this.RenderPagination(items, false);
        }

Now these are using a RenderPagination method that takes into account whether the pagination is on the top or bottom in order to fit into our design.  Notice how I could put in the custom CSS class names too which is kind of cool.  :

      protected void RenderPagination(MvcContrib.Pagination.IPagination pagedList, bool isTop)
        {
            StringBuilder sb = new StringBuilder();
            if (!isTop)
            {
                sb.AppendLine("<div class=\"mainbox\">");
            }
            if (pagedList != null && pagedList.TotalItems > 0)
            {
                sb.AppendLine("<div class=\"paging\">");
                sb.AppendLine("<span>");
                for (int i = 1; i <= pagedList.TotalPages; i++)
                {
                    sb.Append(this.CreatePageLink(i, i.ToString()));
                    if (i < pagedList.TotalPages)
                        sb.Append(" ");
                }
                sb.AppendLine("</span>");
                sb.AppendLine("<span class=\"position\">");
                sb.Append("Page ");
                sb.Append(pagedList.PageNumber);
                sb.Append(" of ");
                sb.Append(pagedList.TotalPages);
                if (pagedList.PageNumber > 1)
                    sb.Append(this.CreatePageLink(pagedList.PageNumber - 1, "<"));
                else
                    sb.Append("<");
                sb.Append(" ");
                if (pagedList.PageNumber < pagedList.TotalPages)
                    sb.Append(this.CreatePageLink(pagedList.PageNumber + 1, ">"));
                else
                    sb.Append(">");
                sb.AppendLine("</span>");
                sb.AppendLine("</div>");
                sb.AppendLine("<div class=\"cl\">&nbsp;</div>");
            }
            if (isTop)
                sb.AppendLine("</div>");

            if (!isTop)
                sb.AppendLine("</div>");
            base.RenderText(sb.ToString());
        }

Boy do I love Reflector!  Most of the RenderPagination method came from the original MvcContrib’s method.  I just added couple things in there like if (isTop) and such.  So not only is MvcContrib’s Grid a simple, easy to use solution for building a grid, it’s pretty easily extensible too!

ASP.NET Strongly Typed Profile

Filed under: Uncategorized — noahblu @ 5:08 pm
Tags: ,

I’m not a huge fan of the out-of-the-box functionality of ASP.NET Profiles.  I really dislike the way everything is is just serialized as a blob and there’s no out-of-the-box alternative.  That aside, I’m using Profiles for an ASP.NET MVC site right now and wanted a way to strongly type the data (and remove the need for string literals).  Here’s what I ended up with:

public class StronglyTypedProfile
    {
        private ProfileBase profile;

        public int MerchantId
        {
            get
            {
                object ob = profile.GetPropertyValue("MerchantId");
                if (ob == null)
                    return -1;
                return (int)ob;
            }
            set
            {
                profile.SetPropertyValue("MerchantId", value);
            }
        }

        public string ForumNick
        {
            get { return (string)profile.GetPropertyValue("ForumNick"); }
            set
            {
                profile.SetPropertyValue("ForumNick", value);
            }
        }

        public string FirstName {
            get
            {
                return (string)profile.GetPropertyValue("FirstName");
            }
            set
            {
                profile.SetPropertyValue("FirstName", value);
            }
        }

        public string LastName
        {
            get
            {
                return (string)profile.GetPropertyValue("LastName");
            }
            set
            {
                profile.SetPropertyValue("LastName", value);
            }
        }

        public void Save()
        {
            profile.Save();
        }

        private StronglyTypedProfile() { }
        public static StronglyTypedProfile Create(string username)
        {
            StronglyTypedProfile p = new StronglyTypedProfile();
            p.profile = ProfileBase.Create(username);
            //profile = ProfileBase.Create(username);
            return p;
        }
    }

Now I can use it like so:

StronglyTypedProfile profile = StronglyTypedProfile.Create(Username);
profile.FirstName = u.FirstName;
profile.LastName = u.LastName;
profile.Save();

Very simple, and it works.

ASP.NET MVC Captcha

Filed under: ASP.NET MVC — noahblu @ 4:56 pm
Tags: , ,

I was looking for an ASP.NET MVC Captcha control and stumbled upon Nick Berardi’s (http://www.coderjournal.com/2008/06/mvc-captcha-for-preview-release-3/).

First of all I’m using IIS7 and had trouble getting the HttpHandler to register properly.  I ended up with the following steps to get it to work:

  1. Add <add verb=”GET” path=”captcha.ashx” validate=”false” type=”ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha” /> to <httpHandlers> in Web.config
  2. Add <add name=”CaptchaImageHandler” verb=”GET” path=”captcha.ashx” type=”ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha” /> to the <handlers> section within <system.webserver> in Web.config
  3. Add routes.IgnoreRoute(“{handler}.ashx”); to Global.asax

So that got it working, but there were a few pieces of functionality I felt were missing:

1) I wanted the ability to style the CaptchaTextBox

The site I’m working with is pretty design intensive and I needed a way to inject CSS information into the CaptchaTextBox Html Helper.  I reworked the CaptchaHelper and modified the CaptchaTextBox as well as added some overloads:

        public static string CaptchaTextBox(this HtmlHelper helper, string name)
        {
            return helper.CaptchaTextBox(name, null);
        }

        public static string CaptchaTextBox(this HtmlHelper helper, string name, Object htmlAttributes)
        {
            return helper.CaptchaTextBox(name, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        public static string CaptchaTextBox(this HtmlHelper helper, string name, IDictionary<String, Object> htmlAttributes)
        {
            ModelState state;

            TagBuilder builder = new TagBuilder("input");
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("type", "text");
            builder.MergeAttribute("name", name);
            builder.MergeAttribute("id", name);
            builder.MergeAttribute("value", "");
            builder.MergeAttribute("maxlength", ManagedFusion.Web.Controls.CaptchaImage.TextLength.ToString());
            builder.MergeAttribute("autocomplete", "off");

            if (helper.ViewData.ModelState.TryGetValue(name, out state) && (state.Errors.Count > 0))
            {
                builder.AddCssClass("input-validation-error");
            }

            return builder.ToString(TagRenderMode.SelfClosing);
        }

I got the TagBuilder idea from checking out the Reflector on System.Web.MVC.  Pretty cool stuff there.  So now I can use the CaptchaTextBox like so:

<%= Html.CaptchaTextBox(“captcha”, new { @class = “field” })%>

2) I wanted the CaptchaValidationAttribute to invalidate my Model if the captcha isn’t valid

(instead of inject a captchaValid with a value of false into my routedata which is what it does off the shelf)

For this I modified the CaptchaValidationAttribute class.  The first thing I did was make add an ErrorMessage string property.  Then I modified the OnActionExecutingContext method to look like this:

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // get the guid from the post back
            string guid = filterContext.HttpContext.Request.Form["captcha-guid"];

            // check for the guid because it is required from the rest of the opperation
            if (String.IsNullOrEmpty(guid))
            {
                filterContext.Controller.ViewData.ModelState.AddModelError(Field, ErrorMessage);
                return;
            }

            // get values
            CaptchaImage image = CaptchaImage.GetCachedCaptcha(guid);
            string actualValue = filterContext.HttpContext.Request.Form[Field];
            string expectedValue = image == null ? String.Empty : image.Text;

            // removes the captch from cache so it cannot be used again
            filterContext.HttpContext.Cache.Remove(guid);

            // validate the captch
            if (String.IsNullOrEmpty(actualValue) || String.IsNullOrEmpty(expectedValue) || !String.Equals(actualValue, expectedValue, StringComparison.OrdinalIgnoreCase))
            {
                filterContext.Controller.ViewData.ModelState.AddModelError(Field, ErrorMessage);
                return;
            }
        }

Now I can use the CaptchaValidationAttribute like this:

[CaptchaValidationAttribute()]
public ActionResult Register(FormCollection form)
        {
// INCREDIBLY OVER-SIMPLIFIED BUT YOU GET THE IDEA
            if (!ViewData.ModelState.IsValid)
            {
                return View();
            }
        }

Create a free website or blog at WordPress.com.