Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Views

Adding Stylesheets, Scripts In ASP.NET MVC2

with 9 comments

A while back I worked on some code that allowed you to add content to different areas of a MVC view, specifically for dealing with headers and scripts. The code worked well enough but relied on a lesser used member in the Reponse class – the Filter property.

Except, now in MVC2 you see this little message when you try to mess with the Filter…

Filtering is not allowed? What? Why not?!?

Admittedly, the original solution was rather hackish because it tried to perform all of the work in the Render phase of the page life cycle and then update the correct content areas in a custom stream that as assigned to the Filter property. You could only pass content as a string value so it mean’t a lot of ugly escaping of characters instead of simply writing normal HTML.

Lately, I’ve been working on another personal project related to templating MVC pages. I won’t give away too much for now but let’s just say that I think that Web Controls still have great potential. Sometimes a page layout takes a couple passes to ensure everything is where it needs to be which is something that the page life cycle handles for you.

Let’s look at another example that uses Web Controls to update content in different parts of the page.

[Download Source Code: ContentArea / ContentDisplayArea] – Remember, I’m posting code on github.com and specifically using gist – If you aren’t using it then you might want to check it out! I hacked out this code this evening so if you discover bugs then feel free to report them to me. (and considering the developer who wrote it, you probably will) 🙂

After you’ve added the code to your project you’re going to have to add a reference to the controls by adding an entry to your web.config. It will look something like the example below.

<?xml version="1.0"?>
<configuration>
    <!-- Snip... -->
    <system.web>
        <pages>
            <controls>
                <!-- Snip... -->
                <add tagPrefix="mvc" namespace="MvcWebControls" assembly="YourProjectName" />
            </controls>
        </pages>
    </system.web>
</configuration>

So, now you have the controls added let’s pretend we have a view that looks like the example below and we want to add a few stylesheets to it.

[View.aspx]

<% @Page ... %>
<html>
    <head>
        <title>My Mvc Application</title>

        <!-- Consumes the content of areas with a target named 'header' -->
        <mvc:ContentDisplayArea ID="header" runat="server" />

    </head>
    <body>
        <% this.Html.RenderPartial("SomeControl.ascx"); %>
    </body>
</html>

And then in our partial control we use a ContentArea to add content to the header.

[SomeControl.ascx]

<% @Control ... %>

<mvc:ContentArea runat="server" Target="header" >
    <link rel="stylesheet" src="<% =this.Url.Content("~/resources/somecontrol.css") %>" />
</mvc:ContentArea>

<div class="control-container" >
    Hello World... uh... nevermind...
</div>

An just like that our content appears in the header of our page! Excellent!

How does it work? Some sort of magic? Maybe a fancy, super technical programming trick?

… or maybe it just uses the existing ASP.net functionality to track controls, render content and then move them to the correct containers by linking into different phases of the page life cycle. Not exactly ‘magical’ and somewhat anti-climatic but definitely reminds us that while MVC is the new hawtness it still is ASP.NET.

Anyways, in the next few weeks I’ll share more about my thoughts for alternative ways to create views in MVC and my new templating project… but for now try out the code and enjoy!

Advertisements

Written by hugoware

April 6, 2010 at 10:48 pm

Build A Smarter Loop With C#

with 3 comments

One of the first things every programmer learns in how to loop through an array of information. We learn how to do for, foreach, while loops but then we never really improve on them. For the most part the standard loop is as far as a programmer goes.

It seems to me that loops should be more intelligent than that. In fact we often write code that uses the loop for additional information. It isn’t uncommon to see a loop check if it is an even or odd index or the first or last value.

Modestly Smarter Loops

I really like lambdas a lot – in fact probably, too much. Being able to pass an delegate around like an argument definitely can be abused but in some cases it can make for some really elegant code. Let’s look at some code that can improve working with arrays.

namespace LoopExtensions {
//Some comments removed for brevity...

    #region Extension Methods

    /// <summary>
    /// Handles looping through collections with additional details
    /// </summary>
    public static class LoopExtensionMethods {

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, 
            Action<ElementDetail<T>> each) {
            return LoopExtensionMethods.Each<T>(collection, 0, collection.Count(), each);            
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, 
            int start, 
            Action<ElementDetail<T>> each) {
            return LoopExtensionMethods.Each<T>(collection, start, collection.Count(), each);
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection,
            int start,
            int end,
            Action<ElementDetail<T>> each) {
            Action<ElementDetail<T>, T> handle = (detail, item) => { each(detail); };
            return LoopExtensionMethods.Each<T>(collection, start, end, handle);
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, 
            Action<ElementDetail<T>, T> each) {
            return LoopExtensionMethods.Each<T>(collection, 0, collection.Count(), each);            
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, 
            int start, 
            Action<ElementDetail<T>, T> each) {
            return LoopExtensionMethods.Each<T>(collection, start, collection.Count(), each);
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, 
            int start, 
            int end, 
            Action<ElementDetail<T>, T> each) {

            //verify the ranges
            if (start < 0 || end > collection.Count()) {
                throw new ArgumentOutOfRangeException();
            }

            //perform the work
            foreach (T value in collection) {
                each(new ElementDetail<T>(value, start++, end, collection), value);
                if (start == end) { break; }
            }
            return collection;
        }

    }

    #endregion

    #region Detail Information Class

    /// <summary>  
    /// Contains a summary of information about the item current in the loop  
    /// </summary>  
    public class ElementDetail<T> {

        #region Constructors

        internal ElementDetail(T value, int index, int total, IEnumerable<T> collection) {
            this.Value = value;
            this.Index = index;
            this.Total = total;
            this.Collection = collection;
        }

        #endregion

        #region Loop Information

        public int Index { get; private set; }

        public int Total { get; private set; }

        public T Value { get; private set; }

        public IEnumerable<T> Collection { get; private set; }

        #endregion

        #region Value Properties

        public T Previous {
            get {
                return !this.First
                    ? this.Collection.ElementAt(this.Index - 1)
                    : default(T);
            }
        }

        public T Next {
            get {
                return !this.Last
                    ? this.Collection.ElementAt(this.Index + 1)
                    : default(T);
            }
        }

        public bool Last {
            get { return this.Index == (this.Total - 1); }
        }

        public bool First {
            get { return this.Index == 0; }
        }

        public bool Outer {
            get { return this.First || this.Last; }
        }

        public bool Inner {
            get { return !this.Outer; }
        }

        public bool Even {
            get { return this.Index % 2 == 0; }
        }

        public bool Odd {
            get { return !this.Even; }
        }

        #endregion

        #region Collection Properties

        public int StepNumber {
            get { return this.Index + 1; }
        }

        public float PercentCompleted {
            get { return (((float)this.Index / (float)this.Total) * 100); }
        }

        public float PercentRemaining {
            get { return 100 - this.PercentCompleted; }
        }

        public int StepsCompleted {
            get { return this.Index; }
        }

        public int StepsRemaining {
            get { return this.Total - this.Index; }
        }

        #endregion

    } 

    #endregion

}

Now that is a lot of code but if you review over it you’ll find that it is a set of extension methods that can be used with IEnumerable. The basic idea is that we can perform our loop within a delegate that accepts additional information about the elements in our loop.

So here is a quick example…

string[] items = {
    "Apple",
    "Orange",
    "Grape",
    "Watermellon",
    "Kiwi"
};

items.Each((item) => {
    Console.Write("{0} > ", item.Inner ? "Inner" : "Outer");
    if (!item.First) { Console.Write("Previous: {0}, ", item.Previous); }
    Console.Write("Current: {0} ({1})", item.Value, item.StepNumber);
    if (!item.Last) { Console.Write(", Next: {0}", item.Next); }
    Console.WriteLine(" -- {0}% remaining", item.PercentRemaining);
});

// Output
// Outer > Current: Apple (1), Next: Orange -- 100% remaining
// Inner > Previous: Apple, Current: Orange (2), Next: Grape -- 80% remaining
// Inner > Previous: Orange, Current: Grape (3), Next: Watermellon -- 60% remaining
// Inner > Previous: Grape, Current: Watermellon (4), Next: Kiwi -- 40% remaining
// Outer > Previous: Watermellon, Current: Kiwi (5) -- 20% remaining

Normally you would simply write all the comparisons within your loop and then use them as needed. Instead, in this code, we pass in an additional parameter that contains shortcuts to many common comparisons you might find inside of loops. By doing this we improve readability and focus on what the loop is doing.

Whats Wrong With The Old Way

Nothing! Writing your comparisons inline can be advantageous since you aren’t doing any more work than you need to do. However, in some cases, the improved readability would make a big difference in the quality of the code. Consider the two snippets of MVC code and decide with one is easier to read.

<ul>
<% foreach(SiteMapNode node in breadcrumb) { %>
    <li class="item <% =(breadcrumb.IndexOf(node) % 2 == 0 ? "item-even" : "item-odd") %>" >
    <% if (breadcrumb.First().Equals(node) || breadcrumb.Last().Equals(node)) { %>
        <strong>
    <% } %>
        <a href="<% =node.Url %>" >
            (<% =(breadcrumb.IndexOf(node) + 1) %>) : <% =node.Text %>
        </a>
    <% if (breadcrumb.First().Equals(node) || breadcrumb.Last().Equals(node)) { %>
        </strong>
    <% } %>
    </li>
<% } %>
</ul>

Or the same code using a loop helper…

<ul>
<% breadcrumb.Each((node) => { %>
    <li class="item <% =(node.Even ? "item-even" : "item-odd") %>" >
    <% if (node.Outer) { %><strong><% } %>
        <a href="<% =node.Value.Url %>" >
            (<% = node.StepNumber %>) : <% =node.Value.Text %>
        </a>
    <% if (node.Outer) { %></strong><% } %>
    </li>
<% }); %>
</ul>

This example shows using only the ElementDetail class but of course you could use the other extension method that also supplies the value by itself.

Write Code For Humans Not For Computers

Everyone has heard the advice before – more or less, readability is probably the most important part of your code. The computer can read both of those examples easily whereas a human might need to think for a moment before they are positive what the code does.

In any case, this simple but effective approach can transform previously unreadable code into works of programming art… or something like that…

Written by hugoware

September 2, 2009 at 2:12 am