Responsive Design mit ASP.NET MVC

In Zeiten vermehrter Absatzzahlen für den Bereich Mobilgeräte wird es auch und gerade für Webentwickler immer wichtiger, diesen Geräten gebührend Rechnung zu tragen. codingfreaks zeigt anhand des Foundation-Projektes, wie so etwas in ASP.NET MVC relativ leicht umgesetzt werden kann.


VS-2012-Projektmappe (7zip)

Vorbemerkungen

Wie immer, will ich auch hier erstmal auf Responsive Design eingehen. Eigentlich ist auch hier alles ganz einfach. Es geht um die Frage, wie man ein User Interface so gestalten kann, dass es sich automatisch selbst an extreme Schwankungen innerhalb der verwendeten Anzeige- und Bediengeräte anpasst. Das klingt vielleicht etwas umständlich. Man kann es auch so ausdrücken, dass wir eine UI so entwickeln wollen, dass es sowohl auf kleinen Handy-Displays, 30-Zoll-Bildschirmen und im Hoch- oder Querformat gut aussieht und die Inhalte transportiert. Auch soll es egal sein, ob der User es per Maus oder Hand oder Auge oder was weiß ich steuert. Soweit sind sich meist erstmal alle einig: Ja, das wollen wir auch!

Jetzt aber gehts ans eingemachte und die Bandbreite von möglichen Umsetzungen spreizt sich auf. (Der Einfachheit halber werde ich mich ab sofort auf den Bereich HTML beziehen).

Schauen wir uns aber erstmal ein paar einfache Beispiele an, um eine Vorstellung vom Ziel zu bekommen.

Screencast

Wie immer für erstmal der Screencast zum Zurücklehnen:

Responsive

Um einfach möglichst simpel zu bleiben, sollen folgende 2 Skizzen (neudeutsch: Sketches) das Ziel verdeutlichen:

Abb. 1: Webseite im Browser

Abb. 1: Webseite im Browser

Abb. 2: Webseite im Phone

Abb. 2: Webseite im Phone

So gesehen, ist alles ziemlich einfach. Trennen wir uns mal von der Tatsache, dass Abb. 2 ein Telefon ist, bleibt festzustellen, dass Abb. 1 im Querformat auf einem relativ großen Bildschirm funktioniert. Abb. 2 hingegen soll ins Spiel kommen, wenn die Auflösung kleiner wird bzw. ein hochformatiges Display im Einsatz ist. Das soll nun möglichst reibungslos umschalten, ohne dass wir uns als Entwickler allzu viele Gedanken darüber machen müssen.

Nun geht es los und es entstehen die merkwürdigsten Ansätze. Eine Variante könnte sein, alle Styles in unterschiedlichen CSS-Dateien zu halten und per CSS media query zwischen diesen umzuschalten. HTML und CSS bieten dazu Selektoren an, die sich quasi die jeweils verwandten Bildschirme ansieht und ggf. den Style umschaltet. Hier ein kleines Beispiel:

Listing 1 geht gleich den saubersten Ansatz und schaltet auf HTML-link-Ebene zwischen 2 Dateien um, sodass ein bestimmter Screen bestimmte CSS-Styles geliefert bekommt. Wer hier mehr wissen möchte, sei z.B. auf diesen sehr ausführlichen iX-Artikel verwiesen.

Das ist schon mal recht nett, bedeutet aber einen nicht unerheblichen Aufwand in der Wartung und führt u.U. zu kruden CSS-Hacks. Media-Queries allein sind nicht etwas DAS Mittel für Responsiveness, sondern sollten als ein Baustein in diesem Feld verstanden werden.

Rasterdenken

Leider glauben zu viele HTML-Programmierer, dass sie durch die Kenntnisse in diesem Bereich auch gleichzeitig gute Webdesigner wären. Wie man an den Abbildungen 1 und 2 sehen kann, würde auch ich zu dieser Gruppe gehören, wäre mir mein Mangel an gestalterischen Kenntnissen nicht ausreichend bewusst. Ich nutze für Arbeiten aus diesem Bereich die Kenntnisse von Kollegen, die sich damit auskennen. Genau so erwarte ich ja auch, dass keiner der letzteren Gruppe anfängt “Neues Projekt” im Visual Studio aufzurufen.

Was aber hat die Zunft der Designer uns Programmiereren hier vorraus? Heruntergebrochen könnte man sagen: Sie denken oft in Rastern. Ein Designer beginnt ein UI-Design meist damit, dass er festlegt, welche Aufteilung dem Ergebnis angedeihen soll. Soll es 2-spaltig erscheinen, auf 6 Spalten basieren oder vielleicht sogar nur als einspaltiger Wurm über den Bildschirm glitschen? Diese Frage steht ganz am Anfang.

Den Prozess kann man sich z.B. online mit Gridpak zu Gemüte führen. Ich habe mir mal ein 3-spaltiges Layout erstellt und dazu ein paar Abstände definiert. Das ganze sieht dann ca. so wie in Abb. 3 aus:

Abb. 3: Gridpak-Layout mit 3 Spalten

Abb. 3: Gridpak-Layout mit 3 Spalten

Wählt man auf der Gridpak-Seite unten links “Download your Gridpak”, kann man sich das Ganze dann gleich aus CSS- und JavaScript-Sicht ansehen. Sieht man in die gridpak.css-Datei, erkennt man neben den Media-Queries vor allem eine Menge an CSS-Klassen mit dem Titel “span_x”. Diese Klassen kann man selbst verwenden, um z.B. <div>-Elemente eine am Grid angepasste Breite zu geben.

12-Spalten-Denken

Jetzt könnte man also hergehen, sich über Gridpak jedesmal ein neues Layout überlegen und dann seine Seiten designen. Das muss man aber nicht, weil es im Laufe der Zeit zu Best-Practices im Raster-Design gekommen ist. Irgendwie hat sich für den Web-Bereich dabei ein 12-Spalten-Raster als in vielen Fällen hilfreich gezeigt. Das heißt nicht, dass viele Webseiten 12-Spaltig aussehen, sondern, dass ihr Layout intern auf 12 Spalten basiert. Dazu ein abstraktes Beispiel.

Nehmen wir an, wir planen eine Webseite, die einen Header über die gesamte Breite haben soll. Darunter sollen Abwechselnd Reihen kommen, in denen mal rechts- und mal linksbündig ein Bild und daneben ein Erklärungstext erscheinen sollen. Das Raster ließe sich mit 12 Spalten wie folgt beschreiben:

Abb. 4: Design mit 12 Spalten

Abb. 4: Design mit 12 Spalten

Wichtig ist also immer nur, dass eine gedachte Reihe in dem Layout 12 Spalten berücksichtigt. Dort, wo ich vielleicht eine Lücke klaffen lassen will, belege ich z.B. eine Spalte mit keinem Inhalt und fertig.

Angewandt auf unser Beispiel aus Abb. 1 könnte ich also folgendes Raster ansetzen:

Abb. 5: Anwendung eines Rasters

Abb. 5: Anwendung eines Rasters

Zugegeben, das Beispiel ist recht einfach. Aber bereits hier zeigt sich, dass es sogar in diesem Bereich mehrere Wege zum Ziel gibt. Sehen wir uns dazu mal Abb. 6 an:

Abb. 6: Abwandlung des Rasters

Abb. 6: Abwandlung des Rasters

In Abb. 5 habe ich bereits auf der Titel-Zeile eine Spaltenaufteilung auf Grundlage der 12 Basis-Raster-Spalten vorgenommen. Ich kann das gleiche Ergebnis erzielen, indem ich wie in Abb. 6 zunächst festlege, dass der Titelbereich als logische Einheit mit voller Breite verstanden werden soll (blaues Rechteck). Diese Einheit kann ich dann wieder in 12 Spalten unterteilen und den Arbeitsschritt aus Abb. 5 anwenden (grüne Rechtecke). Es ist wichtig zu verstehen, dass jede der 12 Spalten des Basis-Rasters wieder aus 12 Spalten gebaut werden kann. Mit diesem Werkzeug bewaffnet, lässt sich nun prinzipiell jedes Weblayout rastern.

Responsive durch Raster

Um jetzt zum Responsive zu kommen, müsste man nur noch steuern, wie sich die auf Spaltenaufteilung bei unterschiedlichen Geräten verhalten soll. Um in unserem Beispiel zu bleiben, würde ich nun für den Phone-Bereich folgendes anstreben (immer noch basierend auf 12 Spalten):

Abb. 7: Mobil-Raster

Abb. 7: Mobil-Raster

Ich lege also fest, dass das Logo-Element, das in Abb. 7 noch 7 Spalten einnehmen durfte, auf einem Bilgerät nun die vollen 12 Spalten bekommt. Dadurch passt das Menu-Element, das nun  ebenfalls 12 Spalten einnehmen kann automatisch nur noch in die nächste “Zeile” und so entsteht ein responsive Layout.

Die Media-Queries sind hier also nur die technische Weiche, um letztlich zu erkennen, wann welches Element welche Position und Größe im Raster einnehmen soll. Wie aber soll man dies nun technisch möglichst einfach lösen, ohne jedes Mal alle Media-Queries neu zu schreiben usw.?

Zurb Foundation

Hier kommen Frameworks, wie die Zurb Foundation ins Spiel. Foundation basiert ebenfalls auf einem 12-Spalten-Layout und bringt bereits alles mit, was man braucht, um im berühmten Zusammenspiel HTML5, CSS und JavaScript ein responsive Design umzusetzen. Da wir hier ein .NET-Blog sind, interessiert uns natürlich vor allem die Umsetzung in ASP.NET MVC. Genau darum soll es im weiteren Verlauf dieses Artikels gehen. Ich nutze ASP.NET MVC 4 mit dem Visual Studio 2012. Die vorgestellten Schritte sollten allerdings ohne weiteres auch im VS 2010 nachzuvollziehen sein.

Nuget

Nach dem obligatorischen Anlegen eines neues Projektes sollte unser Blick in der heutigen Zeit sofort zu Nuget schweifen. Pakete werden nicht mehr händisch verdrahtet, sondern per Nuget bezogen. Nirgendwo sonst sollte einem das so sehr in Fleisch und Blut übergehen, wie im ASP-Bereich. Ich werde mich auf die Package Manager Console beschränken, weil ich hierdurch besser, als mit Screenshots dokumentieren kann und der Leser die Befehler dann gleich aus dem Blog übernehmen kann.

Zunächst suche ich mal im Nuget-Katalog nach etwas Passendem:

get-package -remote foundation

Listing 2 bringt eine ziemlich lange Liste und die Erkenntnis, dass “zurb” wohl ein besserer Suchbegriff gewesen wäre:

get-package -remote zurb

Aktuell findet meine Console 5 passende Einträge, von denen “Foundation3_MVC4″ der richtige ist. Also dann mal her damit:

install-package Foundation3_MVC4

Anpassung

Listing 4 bringt mir vor allem folgende Elemente in das Projekt:

  • Ordner “/Content/foundation”
  • Ordner “/Scripts/foundation”
  • Datei “Foundation_readme.txt” im Root-Ordner
  • Datei “Foundation_Index.cshtml” in “/Views/Home/”
  • Datei “_Foundation.cshtml” in “/Views/Shared”

Die erwähnte readme sollte als erstes genutzt werden, um sich damit vertraut zu machen, was die Macher von Foundation sich dabei gedacht haben. Offensichtlich viel Gutes! Die Installation verändert nämlich nichts an einem bereits vorhandenen MVC-Projekt, sondern lässt uns die Wahl, jetzt einzelne Views auf Foundation umzustellen. Dazu dient die “_Foundation.cshtml”, die ich als Alternative zur “_Layout.cshtml” nutzen kann, wenn ich will.

Bevor ich aber so richtig damit loslegen kann, muss ich erst die richtigen Verweise zu den notwendigen CSS- und JS-Dateien setzen. Auch das ist in der readme beschrieben und dank MVC und App_Start geht es hier sehr schnell. Innerhalb der BundleConfig.cs-Methode “RegisterBundles” füge ich einfach folgenden Code ein:

#region Foundation Bundles

bundles.Add(new StyleBundle("~/Content/foundation/css").Include(
    "~/Content/foundation/foundation.css",
    "~/Content/foundation/foundation.mvc.css",
    "~/Content/foundation/app.css"));
bundles.Add(new ScriptBundle("~/bundles/foundation").Include(
    "~/Scripts/foundation/jquery.*",
    "~/Scripts/foundation/app.js"));

#endregion

Würde Ich das Projekt nun starten, würde ich immer noch nicht viel Foundation sehen. Das liegt einfach daran, dass ich mindestens einen der Views auf Foundation umstellung muss. Auch diesen Schritt erläutert die readme und so genügt es, die Datei “/Views/Home/Index.cshtml” in irgendwas anderes zu benennen und dafür “/Views/Home/Foundation_Index.cshtml” in “Index.cshtml” umzunamen.

Das Ergebnis dieser ganzen Schritte ist dann:

Abb. 8: Foundation in MVC läuft

Abb. 8: Foundation in MVC läuft

Noch kein Grid, aber vollkommen egal: Die Integration hat geklappt und das ganze, ohne dass irgendwas am Projekt beschädigt wäre. Klickt man im MVC-Standard-Template z.B. oben im Menu auf “Contact”, kommt man zurück zur Standard-Kontaktseite, die ja immer noch die “_Layout.cshtml” als Master-Page nutzt. Alles cool, also!

Jetzt aber los!

Nun wird es aber Zeit, endlich mit dem Raster zu arbeiten. Zunächst kümmere ich mich um die “_Foundation.cshtml” und versuche, das Raster aus Abb. 5 umzusetzen:

<!DOCTYPE html>
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
<!--[if IE 7]>    <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
<!--[if IE 8]>    <html class="no-js lt-ie9" lang="en"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js" lang="en">
<!--<![endif]-->
    <head>
        <meta charset="utf-8" />        
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>@ViewBag.Title</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        @Styles.Render("~/Content/foundation/css")
        @RenderSection("head", required: false)
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header class="row">
            <div id="logo" class="seven columns"></div>
            <nav id="menu" class="four columns offset-by-one">
                <ul>
                    <li>Home&nbsp;&nbsp;|</li>
                    <li>Artikel&nbsp;&nbsp;|</li>
                    <li>Über&nbsp;&nbsp;|</li>
                    <li>Kontakt</li>
                </ul>
            </nav>
        </header>
        <section id="body">
            @RenderBody()
        </section>
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/foundation")        
        @RenderSection("scripts", required: false)
    </body>
</html>

Hier sind nun einige interessante Dinge beisammen. Alles basiert bei Foundation auf CSS-Klassen. Sinnvollerweise haben die Jungs die Klassen dort nach Zahlworten benannt, wo es im Sinne des Rasters besonders Sinn macht. Zunächst aber muss man immer ein Element einfügen, das als Klasse “row” annimmt. Eine Row ist der Container für die 12 zu vergebenden Columns.

Das Layout an sich beginnt in Zeile 19. Hier definiere ich ein HTML-header-Element und mache es außerdem zu einer Foundation-Row. Innerhalb dieser Row muss ich nun die 12 Spalten komplett vergeben. Dazu erstelle ich mir ein div mit der ID “logo” und teile über die Foundation-Klassen “seven” und “columns” mit, dass innerhalb der Row diese Element 7 Spalten Platz einnehmen soll.

Jetzt kommt ein HTML-nav-Element mit der ID “menu” dem ich Foundation-seitig 4 Spalten zuweise. Außerdem sage ich mit “offset-by-one”, dass es nicht direkt in der Spalte neben dem Vorgänger (“logo”) platziert werden soll, sonder dazwischen 1 Spalte “*by-one” Platz bleiben soll.

Sehen wir uns nun die “Index.cshtml” im “/Views/Home/”-Ordner an, deren Inhalt durch @RenderBody() ab Zeile 31 eingefügt wird:

@{
    Layout = "~/Views/Shared/_Foundation.cshtml";
}
<div class="row">
    <div class="twelve columns">
        <h2>Wilkommen</h2>                
    </div>
</div>

Auch hier nutze ich “row” und “columns”. Indem ich dem inneren div mit “twelve” alle 12 verfügbaren Spalten gebe, sage ich letztlich nur, dass ich will, dass dieses div die volle Breite einnehmen soll. Super einfach und super flexibel.

CSS

Bevor es ans Testen gehen kann, muss ich noch einiges an eigenem CSS definieren. Dazu stellt Foundation im Ordner “/Content/foundation” die Datei “app.css” bereit. Sie hat erstmal keine Inhalte und erlaubt es mir, einfach eigene Styles zu definieren. Das mache ich auch:

.row 
{
    width: 1000px  
}

section#body 
{
    margin-top: 40px;
}

#logo 
{
    background: url('/Images/logo.png') no-repeat;
    width: 350px;
    height: 138px;
}

nav 
{
    display: block;
}

    nav ul 
    {
        list-style: none;
    }

        nav ul li 
        {
            float: left;
            display: inline;
            padding: 4px;
        }

So, das wars eigentlich. Das Logo habe ich natürlich auch noch in den “Images”-Ordner kopiert und dann kommt schon “F5″. Das Ergebnis kann sich sehen lassen:

Abb. 9: Die Seite im Debugger

Abb. 9: Die Seite im Debugger

Wenn man nun aber das Browser-Fenster verkleinert, ist da nichts responsive. Abb. 6 schreibt ja vor, dass die Elemente im Telefon untereinander stehen sollen. Was nun?

Styles für Mobile

Auch darauf hat Foundation eine Antwort: Klassen mit dem Präfix “mobile”. Ändere ich die Zeilen 20 und 21 aus Listing 6 wie folgt:

<div id="logo" class="seven columns mobile-twelve-up"></div>
<nav id="menu" class="four columns offset-by-one mobile-twelve-up">

reagiert das Layout sofort auf Größenänderungen. Schiebe ich die Seite zu weit zusammen, wird mein Menu korrekt unter mein Logo gebrochen. Da mein Body-Teil bereits 12 Spalten hatte, brauche ich hier nichts zu ändern. D.h., dass ich z.B. neben der Angabe “seven columns” ein “mobile-twelve-up” setzen kann, um Foundation mitzuteilen: “Wenn Du glaubst, es sei ein Mobil-Display im Spiel, dann nutze bitte die mobile-Klassen.
So ganz perfekt habe ich das hier natürlich noch nicht gelöst, aber die Grundidee sollte klar geworden sein. Unter Foundation-Grid kann man sich viele weiter Optionen und Tipps ansehen.

Foundation ist mehr als Grid

Foundation selbst bietet viele weitere nützliche Erweiterungen, die man sich auf der Seite hervorragend präsentiert anschauen kann. Darunter fallen Dinge, wie Tabs, Breadcrumbs, Navigations-Menus, Tooltips oder auch Formulare. Foundation setzt sich dabei als Wrapper um jQueryUI und erlaubt es, z.B. ein Tab-Element über die Angabe einer CSS-Klasse zu definieren.

Fazit

Foundation ist schon der Hammer, bedenkt man den sauberen, klaren Aufbau und vor allem die nahtlose Integration in MVC! Da können sich einige Frameworks mehrere Scheiben abschneiden. Aber! Foundation ist auch nur ein technisches Hilfsmittel, um einen Prozess zu unterstützen, den viel zu viele einfach nicht durchführen! Es bleibt bei der alten Weisheit, dass das Design mehr ist, als Farben und Schriften! Entweder, man beschäftigt sich als Programmierer mit diesen Themen oder hat das Glück, entsprechende Menschen in seinem Team zu haben. Ist letzteres der Fall, muss man sie nur noch konsequent einbinden! Foundation hilft erst danach, wenn es darum geht, das Design in die Tat umzusetzen.

Blogverzeichnis - Blog Verzeichnis bloggerei.de

One Response to “Responsive Design mit ASP.NET MVC”

  1. » Blog Archive » .NET@mobile – #1: Einführung in App-Entwicklung und Xamarin Says:

    […] soll. Das einfachste Beispiel ist da noch das responsive Design von Webseiten (siehe Artikel “Responsive Design mit ASP.NET MVC“). Darum geht es hier jedoch nicht. Vielmehr soll hier das sog. native Entwickeln von […]

Leave a Reply