Jeff and .NET

The .NET musings of Jeff Putz

Sponsors

News

My Sites

Archives

March 2009 - Posts

Paging links in ASP.NET MVC

Using the MVC framework on a real, if somewhat trivial, project has been a lot of fun so far. It's unlike me to wait for something to go final, but as I Google problems I encounter, it's good to see that I avoided a great many moving targets.

That said, I've always enjoyed building custom controls. Sometimes they're not even necessary, but I find it to be a good exercise because generally there isn't anything hard that I haven't encountered before. I have a templated gallery control, for example, that populates a grid of images with an ad wrapped inside. With the community and photo stuff I often do, the potential for reuse is great.

At first I worried that you lose some of this joy with MVC, but actually you just have to refactor it. One of the things I've had plenty of uses for is a paging control that builds a series of links to page data, based on the total number of pages and the index you're currently on. It's packaged up in POP Forums (see here for an example), and I've used it in a number of other places as well. Porting it to MVC was surprisingly easy by making it an HtmlHelper extension method.

Here's what I ported into my simple blogging app...

using System;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace CliqueSite.Blog.Web.Helpers
{
    public static class Html
    {
        public static string PagerLinks(this HtmlHelper htmlHelper, string controllerName, string actionName, int pageCount, int pageIndex, string cssClass, string moreTextCssClass, string currentPageCssClass)
        {
            return PagerLinks(htmlHelper, controllerName, actionName, pageCount, pageIndex, "First", "Previous", "Next", "Last", "More", cssClass, moreTextCssClass, currentPageCssClass);
        }

        public static string PagerLinks(this HtmlHelper htmlHelper, string controllerName, string actionName, int pageCount, int pageIndex, string firstPage, string previousPage, string nextPage, string lastPage, string moreText, string cssClass, string moreTextCssClass, string currentPageCssClass)
        {
            var builder = new StringBuilder();
            if (String.IsNullOrEmpty(controllerName) || String.IsNullOrEmpty(actionName))
                throw new Exception("controllerName and actionName must be specified for PageLinks.");
            if (pageCount <= 1)
                return String.Empty;


            if (String.IsNullOrEmpty(cssClass)) builder.Append("<div>");
            else builder.Append(String.Format("<div class=\"{0}\">", cssClass));
            if (String.IsNullOrEmpty(moreTextCssClass)) builder.Append(moreText);
            else builder.Append(String.Format("<span class=\"{0}\">{1}</span>", moreTextCssClass, moreText));

            if (pageIndex != 1)
            {
                // first page link
                builder.Append(htmlHelper.RouteLink("|<", new { controller = controllerName, action = actionName, id = 1 }, new {title = firstPage}));
                if (pageIndex > 2)
                {
                    // previous page link
                    int previousLink = pageIndex - 1;
                    builder.Append(htmlHelper.RouteLink("<<", new { controller = controllerName, action = actionName, id = previousLink }, new { title = previousPage }));
                }
            }
            // calc low and high limits for numeric links
            int intLow = pageIndex - 1;
            int intHigh = pageIndex + 3;
            if (intLow < 1) intLow = 1;
            if (intHigh > pageCount) intHigh = pageCount;
            if (intHigh - intLow < 5) while ((intHigh < intLow + 4) && intHigh < pageCount) intHigh++;
            if (intHigh - intLow < 5) while ((intLow > intHigh - 4) && intLow > 1) intLow--;
            for (int x = intLow; x < intHigh + 1; x++)
            {
                // numeric links
                if (x == pageIndex)
                {
                    if (String.IsNullOrEmpty(currentPageCssClass))
                        builder.Append(String.Format("{0} of {1}", x, pageCount));
                    else builder.Append(String.Format("<span class=\"{0}\">{1} of {2}</span>", currentPageCssClass, x, pageCount));
                }
                else
                {
                    builder.Append(htmlHelper.RouteLink(x.ToString(), new { controller = controllerName, action = actionName, id = x }));
                }
            }
            if (pageIndex != pageCount)
            {
                if (pageIndex < pageCount - 1)
                {
                    // next page link
                    int nextLink = pageIndex + 1;
                    builder.Append(htmlHelper.RouteLink(">>", new { controller = controllerName, action = actionName, id = nextLink }, new { title = nextPage }));
                }
                // last page link
                builder.Append(htmlHelper.RouteLink(">|", new { controller = controllerName, action = actionName, id = pageCount }, new { title = lastPage }));
            }
            builder.Append("</div>");

            return builder.ToString();
        }
    }
}

As I said, this is largely similar to the server control I built for the forum app, with two important differences. Instead of exposing properties for the control to determine the page numbers, text labels, CSS classes and such, they're set as parameters to the extension methods. The second difference is that the actual links aren't set up using the Control's ResolveUrl() method and some String.Format() methods. In fact, these are even easier, because you simply use the existing RouteLink() extension method to do all of the magic for you. They were even clever enough to give you endless flexibility to add attributes by way of anonymous types. Well done.

In the controller, I'm setting up data like this:

public ActionResult Page(int page)
{
  int total;
  ViewData["Posts"] = _postRepository.GetPagedPosts(10, page, out total);
  ViewData["TotalPages"] = total;
  ViewData["Page"] = page;
...

Then in the view itself, this is the magic that keeps it clean:

<%= Html.PagerLinks("Admin", "Page", (int)ViewData["TotalPages"], (int)ViewData["Page"],
"theCssClass", "theMoreTextCssClass", "theCurrentPageCssClass") %>

Does this violate any kind of design principles? I don't think so, but I'm rarely one to get extremely academic about this sort of thing. I'm not crazy about loading up views with a ton of display logic, and there would be a spaghetti mess to do something like this if you left it to the view all by itself.

What do you think? Can we expect to see an explosion of HtmlHelper extension libraries? 

Three years on the Mac, plus more thoughts on the new 17" MacBook Pro

A few weeks ago, I bought one of the new 17" MacBook Pros to replace my almost-three-year-old 15" model, the first of the Intel Macs. Hard to believe it has been three years! I'll get to the thoughts on the 17" momentarily.

Given my former life as a broadcast guy, I had "my" first Mac working a video gig in the mid-90's. I went Mac because I scored a Media100 system in my job, which was a little cheaper than the Avid systems at the time. Believe it or not, back in those days, people were mostly cutting video on tape. Tape! Imagine! It was pretty exciting to get a non-linear editing system. Back in those days we had OS 8, which wasn't all that different than what I had seen in college on an old LC or something. I liked it, but it wasn't compelling enough to buy my own.

Shortly after the G4 iBooks came out in 2004, my former wife got one, with OS X, and I really liked it. For non-development work, it really did everything that people regularly do with computers, and it seemed to be without hassle. It just worked. She still has that machine, and it's still the one she uses for couch surfing.

In 2006, Apple released the first Intel-based Mac, the 15" MacBook Pro. I remember it being talked about extensively on the This Week in Tech podcast as I was driving up to Michigan to visit a friend. It wasn't clear how Windows would run on it at first, but there was a lot of enthusiasm about making it work, because a lot of people like the OS, but need Windows for one thing or another. Obviously as a .NET developer, we need Visual Studio. The Boot Camp beta came out, and there was a lot of reason to believe that virtualization would be there any minute. I was sold.

So yeah, it would cost more, but I was OK paying for it because I like OS X, and the hardware itself had all of these intangibles that satisfied my gadget lust. It felt good in the hands. It woke up instantly when you opened it. The long and short of it is that in three years of having the machine, I never had to reinstall the OS or mess with drivers or any of the things that always annoyed me about Windows. And the UI always made more sense, to the extent that the OS was nearly invisible. I realize this is partly because Apple controls the hardware and software, but so what? I've never had any computer that went three years without any software problems. I've handed it off to my about-to-be wife, and she loves it.

I did encounter some hardware issues though. They recalled a battery early on, and after about 400 charge cycles, I had to replace the one I had as it slipped under two hours per charge (the new one does about three hours). There were heat issues early on too because of the massive amounts of thermal paste used on CPU.

I also bought the first Mac Pro for my desktop, and even after two years, I don't know that all of those cores have ever been fully utilized. I love the no-tools drive bays on that thing.

The urge for the 17" was motivated by two factors. The first was that the older 15" maxed out at 2 gigs of RAM. It's nice to throw lots of memory at Parallels when you have more than one instance of Visual Studio open, so I wanted more. The second was that the 1920x1200 screen at 17" is a developers dream. That, and the newer LED screens are absolutely stunning. Oh, and the battery life was a solid motivator too.

Has it lived up to my expectations? Yes, in every way. My first couple of battery cycles didn't hit the promised eight hours, but since then, it has easily hit that. With Parallels running, it's more like 6.5 to 7, but still, that's basically a day's work on a single charge. While at Mix last week, I never had to charge mid-day (granted, I actually closed it and paid attention during many of the sessions). The screen is actually too bright at full, and the glossy has not been an issue ever, I suspect because it's so bright.

The only quirk that I've found is that the new track pad requires some minor behavioral changes. Because the whole thing is a button, you have to make sure that you pick up your "click" finger while dragging around so you don't inadvertently double click. It took a week to get used to that. What makes up for it is the four-finger gestures to activate Expose.

Yeah, I know, it's not a cheap computer. But spec for spec, it's cheaper than a Dell. And it runs OS X, which is where I spend most of my time beyond Visual Studio. I'm really pleased with it so far.

Mix09: Overall thoughts

As I mentioned during the conference, it suddenly seemed very silly to me to be blogging about the conference, when there was already plenty of that going on. I know I found it annoying that my RSS feeds were filled with posts about the same things, so there was little reason for me to add to that noise. But now that it's truly in the books, I think it's a good time to give my overall impression.

This was my third time, and it definitely was the charm (I missed the '07). I got to go on my employers dime again, which is something I quasi-negotiated when I took the job. The biggest value of going to a conference is still the conversations you have at meals or in the halls, without a doubt. Yes, you can watch the sessions online, but that lacks the human component. You can't share knowledge that way.

The keynotes are known as a platform for being product announcements. While that's very exciting to see live, and sure motivates us, it doesn't add a lot of value to the conference. What's cool about it is this year was that we got really good keynotes to go along with it. Not that I don't love Scott Guthrie (his video was hilarious), but you really want to see some big thinkers too. Bill Buxton and Debrah Adler were outstanding. I really got into what they had to say, more so than Ballmer or Gates in previous years. Those were definitely highlights for me.

The regular sessions included the kinds of deep dives I would expect, and they were 85% solid. I walked out of a couple, both from third-party speakers that had almost nothing to really add. What I didn't find on the agenda were the kind of process oriented sessions I've seen in previous years (except for one of those that I walked out of). I also didn't find that I was in a room with the creative people enough, and I'm not sure how you manage to create more situations like that outside of meals.

Overall, I enjoyed myself, and had the kind of brain rot by the end that you expect to get given the density of information you encounter. The biggest suggestion that I could make is to somehow classify sessions in terms of their level of difficulty or sophistication, and sequence them in a logical manner. There was so much Silverlight and MVC that I felt as if I had seen the same thing several times.

On a less important note, I think they failed to meet the kinds of dietary needs people have. If you were a vegetarian, you were screwed, limited to rice and salad. Seriously, did red meat have to be on every plate? And couldn't a sandwich just have good old wheat or white bread?

On another side note, epic win for Apple and the battery life span of the 17" MacBook Pro. Granted, I typically had the screen dimmed almost entirely (it's too bright for those dark rooms), but I actually got a real eight hours of use (plus sleeping) one day doing regular e-mail and Web stuff. Naturally, video, Flash and Parallels are things that greatly reduce the life span.

Posted: Mar 22 2009, 04:19 PM by Jeff | with no comments
Filed under: ,
Mix09, not blogging

I've decided that there's little reason to be blogging about Mix. It would just be noise. There are so many people already doing it that I don't feel I have much to add. Of course, I'll have my wrap up thoughts eventually.

And boy do I have a rant about the usefulness of Twitter...

Posted: Mar 19 2009, 04:34 PM by Jeff | with no comments
Filed under: ,
Mix09 Twittering

I've poo-poo'd Twitter before, because I haven't found it to be anything other than another channel to keep up with, but I'll try being active about using it whilst at Mix this week. If I make meaningful connections (and find fun people to hang with at the Tao party), I'll officially change my tune and see the value.

http://twitter.com/jeffputz

Mix09, with Mac in tow

This will be my third Mix now, and what a strange road it has been for me. The first one I went to on my own dime, sort of, in that the conference was free but travel was my thing. That was a perk of just having a book published. Then the conference got super popular. I didn't go in '07, but I did go last year, on my former employer's bill. It was pretty spectacular, and I hoped that I would have the chance to go again this year.

So I made it a negotiating point when I accepted my current gig. Especially given my move away from day-to-day coding and more into architecture, this particular conference seems like a great fit. I love that it's not a hundred code demos, too. Not that those aren't fascinating, but I like to be a part of a bigger picture. The group of people that this thing brings together really helps put together that big picture.

It also makes me feel like I lost something along the way. When my book came out, I suspected I would keep writing, maybe do some speaking, maybe even work for Microsoft (first attempt: FAIL). But then I had the whole divorce thing and more lifedrama than I would've liked, and I was constantly distracted. So this show is a bit of a turning point. I'm getting married in less than three weeks, I feel as confident as ever in my abilities, and I'm thinking about writing another book. Or contribute to one (if anyone is looking for someone to do a few chapters). Sometimes life requires a little bit of a reboot.

Tomorrow night I'll roll into the Venetian, my shiny new 17" MacBook Pro in hand, and partake in the reasonably dense but endlessly interesting Mix conference. And maybe my tenth Blue Man Group show.

Message to the .NET world: Seriously, the UX does matter

The new gig I started in December as the company's technical architect ("technical" to make the distinction that it's not "information architect") has been an interesting experience for me thus far. My experience at Insurance.com was excellent in so many ways, because the processes were entirely solid and my peers were rock stars without the ego. Getting laid-off from there sucked, but it also presented an opportunity to find something where I could lead processes and have that "enterprise" experience applied to a place that needs it.

My new employer has been primarily a creative marketing endeavor for most of its history, and app development has been a smaller part of their business. Appropriately, their processes around development, QA and deployment were a lot less structured than I was used to, so my initial goals were to mitigate risk and decrease costs in every area I could while leading the overall technical direction of new projects. As you might expect, there are a lot of cultural challenges to deal with.

But I did hit most of my goals in the first three months, including the establishment of coding standards, good source control, continuous integration, solid libraries and code reviews. I'm really happy with the way the team pulled together and bought in to what we were doing.

So what does this have to do with the title of this post? Being a largely creative agency means that people spend a lot more time thinking about how to make things functional and visually attractive. This has really influenced me a great deal, and I didn't expect that. It's like the classic comparison where people say that Windows was written by engineers for engineers (see any of Vista's copy confirm dialogs, if you need an example), while OS X was written by engineers for their grandmothers. The user experience matters. Working for a company rooted in design, I've really come to appreciate that.

No where does this become more apparent than in the various communities that surround the .NET ecosystem. We're surrounded by poorly thought out, and often ugly, implementations of all kinds of stuff. For example, in the official ASP.NET forums, you can click the button to subscribe to a thread for e-mail notifications. But here's the catch... you might not actually ever hear from the site. That's because the poorly organized user control panel has a switch to turn it off. So subscribing to a thread doesn't really subscribe you to it. There's no indication or clue that what you just did didn't actually do anything.

I think we have some incredibly awesome tools to work with in the .NET world. I mean seriously, every day I thank God for Visual Studio (not to say it doesn't have its quirks), ReSharper, anonymous types, Cruise Control .NET, and now, the MVC framework. It blows my mind that we have so much power at our finger tips to deliver really great stuff. But in the case of the poor design that I just mentioned, there are actually people who spend time being apologists for this kind of thing. They'll go as far as justify the design and chalk it up to some user issue. Are you kidding?

Yes, I can be a gear head too, but come on, my .NET brothers and sisters, we have to do better than this. We need to pay better attention to how we design our interaction with humans. I see all of this stuff built on Rails or PHP that is just so entirely slick and easy to use, and it really has nothing to do with the back end, except that the developers in those spaces seem to pay more attention to the front end design. Dependency injection is a fascinating thing to talk about with very obvious benefits, but at the end of the day, if your app is hard to use or sucks in some other way, it won't mean anything that you can manage and alter the app easily because no one is using it anyway.

The user experience matters. Stop ignoring it.

Leaving for Mix09 in a week!

Wow, time flies, and it's almost here. I'm assuming that attendance is not sold out with all of the hotel and registration discounts, but I'm really looking forward to it. The quality of sessions was off the charts solid last year.

If anyone wants to mee up, drop me a line via jeff at popw dot com or http://twitter.com/jeffputz if you prefer. Of course I'll try to do my net doody and post all about it in real time, like any good geek should.

First impressions using 17" MacBook Pro
I'm really impressed with the new laptop (and for what it costs, I suppose one should). I was just shy of using my old one, the first Intel-based 15" MacBook Pro, for three straight years, but as we speak, the old one is getting a fresh install so Diana can use it. Even at three, it's still a lot easier to use than her newer Vista Dell.

The thing that impressed me immediately is how solid it is. I've picked up and twisted the 13" and 15" models in the store, but I guess I still felt that the 17" just "had" to feel less solid. But it doesn't at all, it feels exactly the same. I give Apple a lot of credit for going to this machined solid block of aluminum. It makes a huge difference.

Also impressive was the migration app that pulls all of your junk over the wire from your old comprooder. It apparently is even smart enough to set the ethernet port to cross-over. Nice. It took a little over an hour to move the 50 gigs worth of junk, and when it was done, everything (mostly) worked as it did before. My browsing history and bookmarks were all there, all of the apps I installed, etc. Even keyboard preferences made it over (important for Visual Studio users, of course). It was even smart enough not to copy over older versions of iPhoto and such. The only pain was the serial numbers, having to re-enter them for the pro apps, and having to deactivate CS3 before activating it on the new one.

No heat issues at all. Fans idle silently at 2000 rpm, and there are no hot spots. Screen is beautiful, and I'm not getting all of the criticism toward the glossy screens. Four-finger swiping to activate Expose is sweet. Parallels screams giving it 2 of the 4 gigs to work with. Keyboard is a huge improvement. The size for the 17" isn't nearly as troublesome as I worried it might be.

The battery, man, I don't even know what to make of that. I'm running in the better performance mode, using the better video processor, screen at full brightness, keyboard lights on, and it looks like it'll easily do five hours on a single charge. My guess is that you can easily get six or seven if you back off. I know Apple says eight, but honestly, I was hoping for five or more, and that seems easily achievable.

I suppose I'll post more after I use it, but at this point I'm not honestly expecting much to be different than what I've experienced for the last three years. Yeah, I know these things aren't cheap, but considering the time I spend on it, it seems to me that it's worth the expense to buy something I like better.
More Posts